From 227110c4c3c34fb1a0b1c11667d58e056a509815 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Wed, 28 Mar 2012 11:07:42 +0000 Subject: [PATCH 01/46] libwebp interface changes for lossless encoding. Change-Id: I703a1a18347acf78378cb23fddc6e5ca6dc6a0bb --- src/enc/config.c | 3 + src/enc/picture.c | 321 +++++++++++++++++++++++------------------ src/enc/vp8l.c | 230 +++++++++++++++++++++++++++++ src/enc/vp8li.h | 69 +++++++++ src/enc/webpenc.c | 47 +++--- src/utils/bit_writer.c | 79 ++++++++++ src/utils/bit_writer.h | 50 +++++++ src/webp/encode.h | 7 +- 8 files changed, 648 insertions(+), 158 deletions(-) create mode 100644 src/enc/vp8l.c create mode 100644 src/enc/vp8li.h diff --git a/src/enc/config.c b/src/enc/config.c index d51d9995..5e1b2540 100644 --- a/src/enc/config.c +++ b/src/enc/config.c @@ -44,6 +44,7 @@ int WebPConfigInitInternal(WebPConfig* const config, config->alpha_compression = 1; config->alpha_filtering = 1; config->alpha_quality = 100; + config->lossless = 0; // TODO(skal): tune. switch (preset) { @@ -116,6 +117,8 @@ int WebPValidateConfig(const WebPConfig* const config) { return 0; if (config->alpha_quality < 0 || config->alpha_quality > 100) return 0; + if (config->lossless < 0 || config->lossless > 1) + return 0; return 1; } diff --git a/src/enc/picture.c b/src/enc/picture.c index ef20608d..9885d2be 100644 --- a/src/enc/picture.c +++ b/src/enc/picture.c @@ -32,75 +32,88 @@ int WebPPictureAlloc(WebPPicture* const picture) { const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT; const int width = picture->width; const int height = picture->height; - const int y_stride = width; - const int uv_width = HALVE(width); - const int uv_height = HALVE(height); - const int uv_stride = uv_width; - int uv0_stride = 0; - int a_width, a_stride; - uint64_t y_size, uv_size, uv0_size, a_size, total_size; - uint8_t* mem; - // U/V - switch (uv_csp) { - case WEBP_YUV420: - break; + if (!picture->use_argb_input) { + const int y_stride = width; + const int uv_width = HALVE(width); + const int uv_height = HALVE(height); + const int uv_stride = uv_width; + int uv0_stride = 0; + int a_width, a_stride; + uint64_t y_size, uv_size, uv0_size, a_size, total_size; + uint8_t* mem; + + // U/V + switch (uv_csp) { + case WEBP_YUV420: + break; #ifdef WEBP_EXPERIMENTAL_FEATURES - case WEBP_YUV400: // for now, we'll just reset the U/V samples - break; - case WEBP_YUV422: - uv0_stride = uv_width; - break; - case WEBP_YUV444: - uv0_stride = width; - break; + case WEBP_YUV400: // for now, we'll just reset the U/V samples + break; + case WEBP_YUV422: + uv0_stride = uv_width; + break; + case WEBP_YUV444: + uv0_stride = width; + break; #endif - default: + default: + return 0; + } + uv0_size = height * uv0_stride; + + // alpha + a_width = has_alpha ? width : 0; + a_stride = a_width; + y_size = (uint64_t)y_stride * height; + uv_size = (uint64_t)uv_stride * uv_height; + a_size = (uint64_t)a_stride * height; + + total_size = y_size + a_size + 2 * uv_size + 2 * uv0_size; + + // Security and validation checks + if (width <= 0 || height <= 0 || // check for luma/alpha param error + uv_width < 0 || uv_height < 0 || // check for u/v param error + y_size >= (1ULL << 40) || // check for reasonable global size + (size_t)total_size != total_size) { // check for overflow on 32bit return 0; - } - uv0_size = height * uv0_stride; + } + picture->y_stride = y_stride; + picture->uv_stride = uv_stride; + picture->a_stride = a_stride; + picture->uv0_stride = uv0_stride; + WebPPictureFree(picture); // erase previous buffer + mem = (uint8_t*)malloc((size_t)total_size); + if (mem == NULL) return 0; - // alpha - a_width = has_alpha ? width : 0; - a_stride = a_width; - y_size = (uint64_t)y_stride * height; - uv_size = (uint64_t)uv_stride * uv_height; - a_size = (uint64_t)a_stride * height; + picture->y = mem; + mem += y_size; - total_size = y_size + a_size + 2 * uv_size + 2 * uv0_size; + picture->u = mem; + mem += uv_size; + picture->v = mem; + mem += uv_size; - // Security and validation checks - if (width <= 0 || height <= 0 || // check for luma/alpha param error - uv_width < 0 || uv_height < 0 || // check for u/v param error - y_size >= (1ULL << 40) || // check for reasonable global size - (size_t)total_size != total_size) { // check for overflow on 32bit - return 0; - } - picture->y_stride = y_stride; - picture->uv_stride = uv_stride; - picture->a_stride = a_stride; - picture->uv0_stride = uv0_stride; - WebPPictureFree(picture); // erase previous buffer - mem = (uint8_t*)malloc((size_t)total_size); - if (mem == NULL) return 0; - - picture->y = mem; - mem += y_size; - - picture->u = mem; - mem += uv_size; - picture->v = mem; - mem += uv_size; - - if (a_size) { - picture->a = mem; - mem += a_size; - } - if (uv0_size) { - picture->u0 = mem; - mem += uv0_size; - picture->v0 = mem; - mem += uv0_size; + if (a_size) { + picture->a = mem; + mem += a_size; + } + if (uv0_size) { + picture->u0 = mem; + mem += uv0_size; + picture->v0 = mem; + mem += uv0_size; + } + } else { + if (width <= 0 || height <= 0 || + width >= 0x4000 || height >= 0x4000) { + return 0; + } + WebPPictureFree(picture); // erase previous buffer + picture->argb = (uint32_t*)malloc(width * height * + sizeof(*picture->argb)); + if (picture->argb == NULL) return 0; + picture->argb_stride = width; } } return 1; @@ -114,12 +127,14 @@ static void WebPPictureGrabSpecs(const WebPPicture* const src, dst->y = dst->u = dst->v = NULL; dst->u0 = dst->v0 = NULL; dst->a = NULL; + dst->argb = NULL; } // Release memory owned by 'picture'. void WebPPictureFree(WebPPicture* const picture) { if (picture != NULL) { free(picture->y); + free(picture->argb); WebPPictureGrabSpecs(NULL, picture); } } @@ -144,28 +159,34 @@ int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst) { WebPPictureGrabSpecs(src, dst); if (!WebPPictureAlloc(dst)) return 0; - CopyPlane(src->y, src->y_stride, - dst->y, dst->y_stride, dst->width, dst->height); - CopyPlane(src->u, src->uv_stride, - dst->u, dst->uv_stride, HALVE(dst->width), HALVE(dst->height)); - CopyPlane(src->v, src->uv_stride, - dst->v, dst->uv_stride, HALVE(dst->width), HALVE(dst->height)); - if (dst->a != NULL) { - CopyPlane(src->a, src->a_stride, - dst->a, dst->a_stride, dst->width, dst->height); - } -#ifdef WEBP_EXPERIMENTAL_FEATURES - if (dst->u0 != NULL) { - int uv0_width = src->width; - if ((dst->colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) { - uv0_width = HALVE(uv0_width); + if (!src->use_argb_input) { + CopyPlane(src->y, src->y_stride, + dst->y, dst->y_stride, dst->width, dst->height); + CopyPlane(src->u, src->uv_stride, + dst->u, dst->uv_stride, HALVE(dst->width), HALVE(dst->height)); + CopyPlane(src->v, src->uv_stride, + dst->v, dst->uv_stride, HALVE(dst->width), HALVE(dst->height)); + if (dst->a != NULL) { + CopyPlane(src->a, src->a_stride, + dst->a, dst->a_stride, dst->width, dst->height); + } +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (dst->u0 != NULL) { + int uv0_width = src->width; + if ((dst->colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) { + uv0_width = HALVE(uv0_width); + } + CopyPlane(src->u0, src->uv0_stride, + dst->u0, dst->uv0_stride, uv0_width, dst->height); + CopyPlane(src->v0, src->uv0_stride, + dst->v0, dst->uv0_stride, uv0_width, dst->height); } - CopyPlane(src->u0, src->uv0_stride, - dst->u0, dst->uv0_stride, uv0_width, dst->height); - CopyPlane(src->v0, src->uv0_stride, - dst->v0, dst->uv0_stride, uv0_width, dst->height); - } #endif + } else { + CopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, + (uint8_t*)dst->argb, 4 * dst->argb_stride, + 4 * dst->width, dst->height); + } return 1; } @@ -438,64 +459,88 @@ static int Import(WebPPicture* const picture, const int width = picture->width; const int height = picture->height; - // Import luma plane - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - const int offset = step * x + y * rgb_stride; - picture->y[x + y * picture->y_stride] = - rgb_to_y(r_ptr[offset], g_ptr[offset], b_ptr[offset]); - } - } - - // Downsample U/V plane - if (uv_csp != WEBP_YUV400) { - for (y = 0; y < (height >> 1); ++y) { - for (x = 0; x < (width >> 1); ++x) { - RGB_TO_UV(x, y, SUM4); - } - if (picture->width & 1) { - RGB_TO_UV(x, y, SUM2V); - } - } - if (height & 1) { - for (x = 0; x < (width >> 1); ++x) { - RGB_TO_UV(x, y, SUM2H); - } - if (width & 1) { - RGB_TO_UV(x, y, SUM1); - } - } - -#ifdef WEBP_EXPERIMENTAL_FEATURES - // Store original U/V samples too - if (uv_csp == WEBP_YUV422) { - for (y = 0; y < height; ++y) { - for (x = 0; x < (width >> 1); ++x) { - RGB_TO_UV0(2 * x, x, y, SUM2H); - } - if (width & 1) { - RGB_TO_UV0(2 * x, x, y, SUM1); - } - } - } else if (uv_csp == WEBP_YUV444) { - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - RGB_TO_UV0(x, x, y, SUM1); - } - } - } -#endif - } else { - MakeGray(picture); - } - - if (import_alpha) { - const uint8_t* const a_ptr = rgb + 3; - assert(step >= 4); + if (!picture->use_argb_input) { + // Import luma plane for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { - picture->a[x + y * picture->a_stride] = - a_ptr[step * x + y * rgb_stride]; + const int offset = step * x + y * rgb_stride; + picture->y[x + y * picture->y_stride] = + rgb_to_y(r_ptr[offset], g_ptr[offset], b_ptr[offset]); + } + } + + // Downsample U/V plane + if (uv_csp != WEBP_YUV400) { + for (y = 0; y < (height >> 1); ++y) { + for (x = 0; x < (width >> 1); ++x) { + RGB_TO_UV(x, y, SUM4); + } + if (picture->width & 1) { + RGB_TO_UV(x, y, SUM2V); + } + } + if (height & 1) { + for (x = 0; x < (width >> 1); ++x) { + RGB_TO_UV(x, y, SUM2H); + } + if (width & 1) { + RGB_TO_UV(x, y, SUM1); + } + } + +#ifdef WEBP_EXPERIMENTAL_FEATURES + // Store original U/V samples too + if (uv_csp == WEBP_YUV422) { + for (y = 0; y < height; ++y) { + for (x = 0; x < (width >> 1); ++x) { + RGB_TO_UV0(2 * x, x, y, SUM2H); + } + if (width & 1) { + RGB_TO_UV0(2 * x, x, y, SUM1); + } + } + } else if (uv_csp == WEBP_YUV444) { + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + RGB_TO_UV0(x, x, y, SUM1); + } + } + } +#endif + } else { + MakeGray(picture); + } + + if (import_alpha) { + const uint8_t* const a_ptr = rgb + 3; + assert(step >= 4); + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + picture->a[x + y * picture->a_stride] = + a_ptr[step * x + y * rgb_stride]; + } + } + } + } else { + if (!import_alpha) { + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + const int offset = step * x + y * rgb_stride; + const uint32_t argb = 0xff000000 | (r_ptr[offset] << 16) | + (g_ptr[offset] << 8) | (b_ptr[offset]); + picture->argb[x + y * picture->argb_stride] = argb; + } + } + } else { + const uint8_t* const a_ptr = rgb + 3; + assert(step >= 4); + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + const int offset = step * x + y * rgb_stride; + const uint32_t argb = (a_ptr[offset] << 24) | (r_ptr[offset] << 16) | + (g_ptr[offset] << 8) | (b_ptr[offset]); + picture->argb[x + y * picture->argb_stride] = argb; + } } } } diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c new file mode 100644 index 00000000..228ee446 --- /dev/null +++ b/src/enc/vp8l.c @@ -0,0 +1,230 @@ +// 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/ +// ----------------------------------------------------------------------------- +// +// main entry for the decoder +// +// Author: Vikas Arora (vikaas.arora@gmail.com) +// + +#include +#include +#include +#include "./vp8enci.h" +#include "./vp8li.h" +#include "../utils/bit_writer.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +static const uint32_t kImageSizeBits = 14; + +static int VP8LEncAnalyze(VP8LEncoder* const enc) { + (void)enc; + return 1; +} + +static int EncodeImageInternal(VP8LEncoder* const enc) { + (void)enc; + return 1; +} + +static int CreatePalette(VP8LEncoder* const enc) { + (void)enc; + return 1; +} + +static void EvalSubtractGreen(VP8LEncoder* const enc) { + (void)enc; +} + +static int ApplyPredictFilter(VP8LEncoder* const enc) { + (void)enc; + return 1; +} + +static int ApplyCrossColorFilter(VP8LEncoder* const enc) { + (void)enc; + return 1; +} + +static void EvalEmergingPalette(VP8LEncoder* const enc) { + (void)enc; +} + +static void PutLE32(uint8_t* const data, uint32_t val) { + data[0] = (val >> 0) & 0xff; + data[1] = (val >> 8) & 0xff; + data[2] = (val >> 16) & 0xff; + data[3] = (val >> 24) & 0xff; +} + +static WebPEncodingError WriteRiffHeader(VP8LEncoder* const enc, + size_t riff_size, size_t vp8l_size) { + const WebPPicture* const pic = enc->pic_; + uint8_t riff[HEADER_SIZE + SIGNATURE_SIZE] = { + 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P', + 'V', 'P', '8', 'L', 0, 0, 0, 0, LOSSLESS_MAGIC_BYTE, + }; + if (riff_size < (vp8l_size + TAG_SIZE + CHUNK_HEADER_SIZE)) { + return VP8_ENC_ERROR_INVALID_CONFIGURATION; + } + PutLE32(riff + TAG_SIZE, (uint32_t)riff_size); + PutLE32(riff + RIFF_HEADER_SIZE + TAG_SIZE, (uint32_t)vp8l_size); + if (!pic->writer(riff, sizeof(riff), pic)) { + return VP8_ENC_ERROR_BAD_WRITE; + } + return VP8_ENC_OK; +} + +static WebPEncodingError WriteImage(VP8LEncoder* const enc) { + size_t riff_size, vp8l_size, webpll_size, pad; + WebPPicture* const pic = enc->pic_; + WebPEncodingError err = VP8_ENC_OK; + const uint8_t* const webpll_data = VP8LBitWriterFinish(&enc->bw_); + + webpll_size = VP8LBitWriterNumBytes(&enc->bw_); + vp8l_size = SIGNATURE_SIZE + webpll_size; + pad = vp8l_size & 1; + vp8l_size += pad; + + riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size; + err = WriteRiffHeader(enc, riff_size, vp8l_size); + if (err != VP8_ENC_OK) goto Error; + + if (!pic->writer(webpll_data, webpll_size, pic)) { + err = VP8_ENC_ERROR_BAD_WRITE; + goto Error; + } + + if (pad) { + const uint8_t pad_byte[1] = { 0 }; + if (!pic->writer(pad_byte, 1, pic)) { + err = VP8_ENC_ERROR_BAD_WRITE; + goto Error; + } + } + return VP8_ENC_OK; + + Error: + WebPEncodingSetError(pic, err); + return err; +} + +static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, + WebPPicture* const picture) { + VP8LEncoder* enc; + const int kEstmatedEncodeSize = (picture->width * picture->height) >> 1; + (void)config; + + enc = (VP8LEncoder*)malloc(sizeof(*enc)); + if (enc == NULL) { + WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); + return NULL; + } + enc->pic_ = picture; + enc->use_lz77_ = 1; + enc->palette_bits_ = 7; + + // TODO: Use config.quality to initialize histo_bits_ and transform_bits_. + enc->histo_bits_ = 4; + enc->transform_bits_ = 4; + VP8LBitWriterInit(&enc->bw_, kEstmatedEncodeSize); + + return enc; +} + +static WebPEncodingError WriteImageSize(VP8LEncoder* const enc) { + WebPEncodingError status = VP8_ENC_OK; + WebPPicture* const pic = enc->pic_; + const int width = pic->width - 1; + const int height = pic->height - 1; + if (width < 0x4000 && height < 0x4000) { + VP8LWriteBits(&enc->bw_, kImageSizeBits, width); + VP8LWriteBits(&enc->bw_, kImageSizeBits, height); + } else { + status = VP8_ENC_ERROR_BAD_DIMENSION; + } + return status; +} + +static void DeleteVP8LEncoder(VP8LEncoder* enc) { + free(enc); +} + +int VP8LEncodeImage(const WebPConfig* const config, + WebPPicture* const picture) { + int ok = 0; + VP8LEncoder* enc = NULL; + WebPEncodingError err = VP8_ENC_OK; + + if (config == NULL || picture == NULL) return 0; + if (picture->argb == NULL) return 0; + enc = InitVP8LEncoder(config, picture); + if (enc == NULL) goto Error; + + // --------------------------------------------------------------------------- + // Analyze image (entropy, num_palettes etc) + + if (!VP8LEncAnalyze(enc)) goto Error; + + if (enc->use_palette_) { + CreatePalette(enc); + } + + // Write image size. + err = WriteImageSize(enc); + if (err != VP8_ENC_OK) { + ok = 0; + goto Error; + } + + // --------------------------------------------------------------------------- + // Apply transforms and write transform data. + + EvalSubtractGreen(enc); + + if (enc->use_predict_) { + if (!ApplyPredictFilter(enc)) goto Error; + } + + if (enc->use_cross_color_) { + if (!ApplyCrossColorFilter(enc)) goto Error; + } + + if (enc->use_emerging_palette_) { + EvalEmergingPalette(enc); + } + + // --------------------------------------------------------------------------- + // Encode and write the transformed image. + + ok = EncodeImageInternal(enc); + if (!ok) goto Error; + + err = WriteImage(enc); + if (err != VP8_ENC_OK) { + ok = 0; + goto Error; + } + + Error: + VP8LBitWriterDestroy(&enc->bw_); + DeleteVP8LEncoder(enc); + if (!ok) { + // TODO(vikasa): err is not set for all error paths. Set default err. + if (err == VP8_ENC_OK) err = VP8_ENC_ERROR_BAD_WRITE; + WebPEncodingSetError(picture, err); + } + return ok; +} + +//------------------------------------------------------------------------------ + +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/src/enc/vp8li.h b/src/enc/vp8li.h new file mode 100644 index 00000000..e46e2d2f --- /dev/null +++ b/src/enc/vp8li.h @@ -0,0 +1,69 @@ +// 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/ +// ----------------------------------------------------------------------------- +// +// Lossless encoder: internal header. +// +// Author: Vikas Arora(vikaas.arora@gmail.com) + +#ifndef WEBP_ENC_VP8LI_H_ +#define WEBP_ENC_VP8LI_H_ + +#include "../webp/encode.h" +#include "../utils/bit_writer.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +// TODO(vikasa): factorize these with ones used in lossless decoder. +#define TAG_SIZE 4 +#define CHUNK_HEADER_SIZE 8 +#define RIFF_HEADER_SIZE 12 +#define HEADER_SIZE (RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE) +#define SIGNATURE_SIZE 1 +#define LOSSLESS_MAGIC_BYTE 0x64 + +typedef struct { + const WebPConfig* config_; // user configuration and parameters + WebPPicture* pic_; // input / output picture + + // Encoding parameters derived from quality parameter. + int use_lz77_; + int palette_bits_; + int histo_bits_; + int transform_bits_; + + // Encoding parameters derived from image characteristics. + int predicted_bits_; + int non_predicted_bits_; + int use_palette_; + int num_palette_colors; + int use_predict_; + int use_cross_color_; + int use_emerging_palette_; + + VP8LBitWriter bw_; +} VP8LEncoder; + +//------------------------------------------------------------------------------ +// internal functions. Not public. + +// in vp8l.c + +// Encodes the picture. +// Returns 0 if config or picture is NULL or picture doesn't have valid argb +// input. +int VP8LEncodeImage(const WebPConfig* const config, + WebPPicture* const picture); + +//------------------------------------------------------------------------------ + +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif + +#endif /* WEBP_ENC_VP8LI_H_ */ diff --git a/src/enc/webpenc.c b/src/enc/webpenc.c index 714d9811..00a3d5cc 100644 --- a/src/enc/webpenc.c +++ b/src/enc/webpenc.c @@ -15,6 +15,7 @@ #include #include "./vp8enci.h" +#include "./vp8li.h" // #define PRINT_MEMORY_INFO @@ -142,8 +143,8 @@ static void MapConfigToTools(VP8Encoder* const enc) { // LFStats: 2048 // Picture size (yuv): 589824 -static VP8Encoder* InitEncoder(const WebPConfig* const config, - WebPPicture* const picture) { +static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, + WebPPicture* const picture) { const int use_filter = (config->filter_strength > 0) || (config->autofilter > 0); const int mb_w = (picture->width + 15) >> 4; @@ -259,7 +260,7 @@ static VP8Encoder* InitEncoder(const WebPConfig* const config, return enc; } -static void DeleteEncoder(VP8Encoder* enc) { +static void DeleteVP8Encoder(VP8Encoder* enc) { if (enc) { VP8EncDeleteAlpha(enc); #ifdef WEBP_EXPERIMENTAL_FEATURES @@ -327,7 +328,6 @@ int WebPReportProgress(VP8Encoder* const enc, int percent) { //------------------------------------------------------------------------------ int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) { - VP8Encoder* enc; int ok; if (pic == NULL) @@ -339,27 +339,36 @@ int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) { return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION); if (pic->width <= 0 || pic->height <= 0) return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); - if (pic->y == NULL || pic->u == NULL || pic->v == NULL) - return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION) return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); - enc = InitEncoder(config, pic); - if (enc == NULL) return 0; // pic->error is already set. - // Note: each of the tasks below account for 20% in the progress report. - ok = VP8EncAnalyze(enc) - && VP8StatLoop(enc) - && VP8EncLoop(enc) - && VP8EncFinishAlpha(enc) + if (!config->lossless) { + VP8Encoder* enc = NULL; + if (pic->y == NULL || pic->u == NULL || pic->v == NULL) + return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); + + enc = InitVP8Encoder(config, pic); + if (enc == NULL) return 0; // pic->error is already set. + // Note: each of the tasks below account for 20% in the progress report. + ok = VP8EncAnalyze(enc) + && VP8StatLoop(enc) + && VP8EncLoop(enc) + && VP8EncFinishAlpha(enc) #ifdef WEBP_EXPERIMENTAL_FEATURES - && VP8EncFinishLayer(enc) + && VP8EncFinishLayer(enc) #endif - && VP8EncWrite(enc); - StoreStats(enc); - if (!ok) { - VP8EncFreeBitWriters(enc); + && VP8EncWrite(enc); + StoreStats(enc); + if (!ok) { + VP8EncFreeBitWriters(enc); + } + DeleteVP8Encoder(enc); + } else { + if (pic->argb == NULL) + return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); + + ok = VP8LEncodeImage(config, pic); } - DeleteEncoder(enc); return ok; } diff --git a/src/utils/bit_writer.c b/src/utils/bit_writer.c index 6ab44833..04c0db63 100644 --- a/src/utils/bit_writer.c +++ b/src/utils/bit_writer.c @@ -8,6 +8,7 @@ // Bit writing and boolean coder // // Author: Skal (pascal.massimino@gmail.com) +// Vikas Arora (vikaas.arora@gmail.com) #include #include // for memcpy() @@ -186,6 +187,84 @@ void VP8BitWriterWipeOut(VP8BitWriter* const bw) { } } +//------------------------------------------------------------------------------ +// VP8LBitWriter + +// Returns 1 on success. +static int VP8LBitWriterResize(VP8LBitWriter* const bw, size_t extra_size) { + uint8_t* allocated_buf; + size_t allocated_size; + const size_t size_required = VP8LBitWriterNumBytes(bw) + extra_size; + if ((bw->max_bytes_ > 0) && (size_required <= bw->max_bytes_)) return 1; + allocated_size = (3 * bw->max_bytes_) >> 1; + if (allocated_size < size_required) { + allocated_size = size_required; + } + // Make Allocated size multiple of KBs + allocated_size = (((allocated_size >> 10) + 1) << 10); + allocated_buf = (uint8_t*)malloc(allocated_size); + if (allocated_buf == NULL) return 0; + memset(allocated_buf, 0, allocated_size); + if (bw->bit_pos_ > 0) { + memcpy(allocated_buf, bw->buf_, VP8LBitWriterNumBytes(bw)); + } + free(bw->buf_); + bw->buf_ = allocated_buf; + bw->max_bytes_ = allocated_size; + return 1; +} + +int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size) { + memset(bw, 0, sizeof(*bw)); + return VP8LBitWriterResize(bw, expected_size); +} + +void VP8LBitWriterDestroy(VP8LBitWriter* const bw) { + if (bw != NULL) { + free(bw->buf_); + memset(bw, 0, sizeof(*bw)); + } +} + +void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits) { + if (n_bits < 1) return; +#if !defined(__BIG_ENDIAN__) + // Technically, this branch of the code can write up to 25 bits at a time, + // but in deflate, the maximum number of bits written is 16 at a time. + { + uint8_t* p = &bw->buf_[bw->bit_pos_ >> 3]; + uint32_t v = *(const uint32_t*)(p); + v |= bits << (bw->bit_pos_ & 7); + *(uint32_t*)(p) = v; + bw->bit_pos_ += n_bits; + } +#else // LITTLE_ENDIAN + // implicit & 0xff is assumed for uint8_t arithmetics + { + uint8_t* p = &bw->buf_[bw->bit_pos_ >> 3]; + const int bits_reserved_in_first_byte = (bw->bit_pos_ & 7); + *p++ |= (bits << bits_reserved_in_first_byte); + const int bits_left_to_write = n_bits - 8 + bits_reserved_in_first_byte; + if (bits_left_to_write >= 1) { + *p++ = bits >> (8 - bits_reserved_in_first_byte); + if (bits_left_to_write >= 9) { + *p++ = bits >> (16 - bits_reserved_in_first_byte); + } + } + *p = 0; + bw->bit_pos_ += n_bits; + } +#endif // BIG_ENDIAN + if ((bw->bit_pos_ >> 3) > (bw->max_bytes_ - 8)) { + const size_t kAdditionalBuffer = 32768 + bw->max_bytes_; + if (!VP8LBitWriterResize(bw, kAdditionalBuffer)) { + bw->bit_pos_ = 0; + bw->error_ = 1; + } + } +} + + //------------------------------------------------------------------------------ #if defined(__cplusplus) || defined(c_plusplus) diff --git a/src/utils/bit_writer.h b/src/utils/bit_writer.h index c85356fe..40ea86fb 100644 --- a/src/utils/bit_writer.h +++ b/src/utils/bit_writer.h @@ -64,6 +64,56 @@ static WEBP_INLINE size_t VP8BitWriterSize(const VP8BitWriter* const bw) { return bw->pos_; } +//------------------------------------------------------------------------------ +// VP8LBitWriter +// TODO(vikasa): VP8LBitWriter is copied as-is from lossless code. There's scope +// of re-using VP8BitWriter. Will evaluate once basic lossless encoder is +// implemented. + +typedef struct { + uint8_t* buf_; + size_t bit_pos_; + size_t max_bytes_; + + // After all bits are written, the caller must observe the state of + // error_. A value of 1 indicates that a memory allocation failure + // has happened during bit writing. A value of 0 indicates successful + // writing of bits. + int error_; +} VP8LBitWriter; + +static WEBP_INLINE size_t VP8LBitWriterNumBytes(VP8LBitWriter* const bw) { + return (bw->bit_pos_ + 7) >> 3; +} + +static WEBP_INLINE uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw) { + return bw->buf_; +} + +// Returns 1 on success. +int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size); + +void VP8LBitWriterDestroy(VP8LBitWriter* const bw); + +// This function writes bits into bytes in increasing addresses, and within +// a byte least-significant-bit first. +// +// The function can write up to 16 bits in one go with WriteBits +// Example: let's assume that 3 bits (Rs below) have been written already: +// +// BYTE-0 BYTE+1 BYTE+2 +// +// 0000 0RRR 0000 0000 0000 0000 +// +// Now, we could write 5 or less bits in MSB by just sifting by 3 +// and OR'ing to BYTE-0. +// +// For n bits, we take the last 5 bytes, OR that with high bits in BYTE-0, +// and locate the rest in BYTE+1 and BYTE+2. +// +// returns 1 on success. +void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits); + //------------------------------------------------------------------------------ #if defined(__cplusplus) || defined(c_plusplus) diff --git a/src/webp/encode.h b/src/webp/encode.h index cabc9823..8704810f 100644 --- a/src/webp/encode.h +++ b/src/webp/encode.h @@ -76,6 +76,7 @@ typedef struct { // 0: none, 1: fast, 2: best. Default if 1. int alpha_quality; // Between 0 (smallest size) and 100 (lossless). // Default is 100. + int lossless; // Lossless encoding (0=lossy(default), 1=lossless). } WebPConfig; // Enumerate some predefined settings for WebPConfig, depending on the type @@ -189,7 +190,7 @@ struct WebPPicture { int width, height; // dimensions (less or equal to WEBP_MAX_DIMENSION) uint8_t *y, *u, *v; // pointers to luma/chroma planes. int y_stride, uv_stride; // luma/chroma strides. - uint8_t *a; // pointer to the alpha plane + uint8_t* a; // pointer to the alpha plane int a_stride; // stride of the alpha plane // output @@ -216,6 +217,10 @@ struct WebPPicture { WebPEncodingError error_code; // error code in case of problem. WebPProgressHook progress_hook; // if not NULL, called while encoding. + + int use_argb_input; // Flag for encoder to read argb pixels as input. + uint32_t* argb; // Pointer to argb (32 bit) plane. + int argb_stride; // This is stride in pixels units, not bytes. }; // Internal, version-checked, entry point From fdccaaddcfe1c55d48ef98a5b531387147c6f9a3 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Mon, 2 Apr 2012 10:58:36 +0000 Subject: [PATCH 02/46] Fixing nits - Const Handling of picture object, removed bitwriter from encoder. Change-Id: Id943854de09324de81cca615ada960390c4b8152 --- src/enc/picture.c | 22 ++++++++++----- src/enc/vp8l.c | 61 +++++++++++++++++++++--------------------- src/enc/vp8li.h | 4 +-- src/enc/webpenc.c | 2 +- src/utils/bit_writer.h | 4 +-- src/webp/encode.h | 2 +- 6 files changed, 51 insertions(+), 44 deletions(-) diff --git a/src/enc/picture.c b/src/enc/picture.c index 9885d2be..75fc9038 100644 --- a/src/enc/picture.c +++ b/src/enc/picture.c @@ -105,13 +105,15 @@ int WebPPictureAlloc(WebPPicture* const picture) { mem += uv0_size; } } else { + const uint64_t argb_size = (uint64_t)width * height; + const uint64_t total_size = argb_size * sizeof(*picture->argb); if (width <= 0 || height <= 0 || - width >= 0x4000 || height >= 0x4000) { + argb_size >= (1ULL << 40) || + (size_t)total_size != total_size) { return 0; } WebPPictureFree(picture); // erase previous buffer - picture->argb = (uint32_t*)malloc(width * height * - sizeof(*picture->argb)); + picture->argb = (uint32_t*)malloc(total_size); if (picture->argb == NULL) return 0; picture->argb_stride = width; } @@ -526,8 +528,11 @@ static int Import(WebPPicture* const picture, for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { const int offset = step * x + y * rgb_stride; - const uint32_t argb = 0xff000000 | (r_ptr[offset] << 16) | - (g_ptr[offset] << 8) | (b_ptr[offset]); + const uint32_t argb = + 0xff000000 | + (r_ptr[offset] << 16) | + (g_ptr[offset] << 8) | + (b_ptr[offset]); picture->argb[x + y * picture->argb_stride] = argb; } } @@ -537,8 +542,11 @@ static int Import(WebPPicture* const picture, for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { const int offset = step * x + y * rgb_stride; - const uint32_t argb = (a_ptr[offset] << 24) | (r_ptr[offset] << 16) | - (g_ptr[offset] << 8) | (b_ptr[offset]); + const uint32_t argb = + (a_ptr[offset] << 24) | + (r_ptr[offset] << 16) | + (g_ptr[offset] << 8) | + (b_ptr[offset]); picture->argb[x + y * picture->argb_stride] = argb; } } diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 228ee446..40157bc3 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -5,7 +5,7 @@ // Additional IP Rights Grant: http://www.webmproject.org/license/additional/ // ----------------------------------------------------------------------------- // -// main entry for the decoder +// main entry for the lossless encoder. // // Author: Vikas Arora (vikaas.arora@gmail.com) // @@ -52,7 +52,7 @@ static int ApplyCrossColorFilter(VP8LEncoder* const enc) { return 1; } -static void EvalEmergingPalette(VP8LEncoder* const enc) { +static void EvalColorCache(VP8LEncoder* const enc) { (void)enc; } @@ -81,13 +81,14 @@ static WebPEncodingError WriteRiffHeader(VP8LEncoder* const enc, return VP8_ENC_OK; } -static WebPEncodingError WriteImage(VP8LEncoder* const enc) { +static WebPEncodingError WriteImage(VP8LEncoder* const enc, + VP8LBitWriter* const bw) { size_t riff_size, vp8l_size, webpll_size, pad; - WebPPicture* const pic = enc->pic_; + const WebPPicture* const pic = enc->pic_; WebPEncodingError err = VP8_ENC_OK; - const uint8_t* const webpll_data = VP8LBitWriterFinish(&enc->bw_); + const uint8_t* const webpll_data = VP8LBitWriterFinish(bw); - webpll_size = VP8LBitWriterNumBytes(&enc->bw_); + webpll_size = VP8LBitWriterNumBytes(bw); vp8l_size = SIGNATURE_SIZE + webpll_size; pad = vp8l_size & 1; vp8l_size += pad; @@ -111,14 +112,12 @@ static WebPEncodingError WriteImage(VP8LEncoder* const enc) { return VP8_ENC_OK; Error: - WebPEncodingSetError(pic, err); return err; } static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, WebPPicture* const picture) { VP8LEncoder* enc; - const int kEstmatedEncodeSize = (picture->width * picture->height) >> 1; (void)config; enc = (VP8LEncoder*)malloc(sizeof(*enc)); @@ -133,23 +132,18 @@ static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, // TODO: Use config.quality to initialize histo_bits_ and transform_bits_. enc->histo_bits_ = 4; enc->transform_bits_ = 4; - VP8LBitWriterInit(&enc->bw_, kEstmatedEncodeSize); return enc; } -static WebPEncodingError WriteImageSize(VP8LEncoder* const enc) { - WebPEncodingError status = VP8_ENC_OK; +static void WriteImageSize(VP8LEncoder* const enc, VP8LBitWriter* const bw) { WebPPicture* const pic = enc->pic_; const int width = pic->width - 1; - const int height = pic->height - 1; - if (width < 0x4000 && height < 0x4000) { - VP8LWriteBits(&enc->bw_, kImageSizeBits, width); - VP8LWriteBits(&enc->bw_, kImageSizeBits, height); - } else { - status = VP8_ENC_ERROR_BAD_DIMENSION; - } - return status; + const int height = pic->height -1; + assert(width < WEBP_MAX_DIMENSION && height < WEBP_MAX_DIMENSION); + + VP8LWriteBits(bw, kImageSizeBits, width); + VP8LWriteBits(bw, kImageSizeBits, height); } static void DeleteVP8LEncoder(VP8LEncoder* enc) { @@ -161,11 +155,22 @@ int VP8LEncodeImage(const WebPConfig* const config, int ok = 0; VP8LEncoder* enc = NULL; WebPEncodingError err = VP8_ENC_OK; + VP8LBitWriter bw; if (config == NULL || picture == NULL) return 0; - if (picture->argb == NULL) return 0; + + if (picture->argb == NULL) { + err = VP8_ENC_ERROR_NULL_PARAMETER; + goto Error; + } + enc = InitVP8LEncoder(config, picture); - if (enc == NULL) goto Error; + if (enc == NULL) { + err = VP8_ENC_ERROR_NULL_PARAMETER; + goto Error; + } + + VP8LBitWriterInit(&bw, (picture->width * picture->height) >> 1); // --------------------------------------------------------------------------- // Analyze image (entropy, num_palettes etc) @@ -177,11 +182,7 @@ int VP8LEncodeImage(const WebPConfig* const config, } // Write image size. - err = WriteImageSize(enc); - if (err != VP8_ENC_OK) { - ok = 0; - goto Error; - } + WriteImageSize(enc, &bw); // --------------------------------------------------------------------------- // Apply transforms and write transform data. @@ -196,8 +197,8 @@ int VP8LEncodeImage(const WebPConfig* const config, if (!ApplyCrossColorFilter(enc)) goto Error; } - if (enc->use_emerging_palette_) { - EvalEmergingPalette(enc); + if (enc->use_color_cache) { + EvalColorCache(enc); } // --------------------------------------------------------------------------- @@ -206,14 +207,14 @@ int VP8LEncodeImage(const WebPConfig* const config, ok = EncodeImageInternal(enc); if (!ok) goto Error; - err = WriteImage(enc); + err = WriteImage(enc, &bw); if (err != VP8_ENC_OK) { ok = 0; goto Error; } Error: - VP8LBitWriterDestroy(&enc->bw_); + VP8LBitWriterDestroy(&bw); DeleteVP8LEncoder(enc); if (!ok) { // TODO(vikasa): err is not set for all error paths. Set default err. diff --git a/src/enc/vp8li.h b/src/enc/vp8li.h index e46e2d2f..fd2a1bf6 100644 --- a/src/enc/vp8li.h +++ b/src/enc/vp8li.h @@ -44,9 +44,7 @@ typedef struct { int num_palette_colors; int use_predict_; int use_cross_color_; - int use_emerging_palette_; - - VP8LBitWriter bw_; + int use_color_cache; } VP8LEncoder; //------------------------------------------------------------------------------ diff --git a/src/enc/webpenc.c b/src/enc/webpenc.c index 00a3d5cc..dc183333 100644 --- a/src/enc/webpenc.c +++ b/src/enc/webpenc.c @@ -367,7 +367,7 @@ int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) { if (pic->argb == NULL) return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); - ok = VP8LEncodeImage(config, pic); + ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem. } return ok; diff --git a/src/utils/bit_writer.h b/src/utils/bit_writer.h index 40ea86fb..f7ca0849 100644 --- a/src/utils/bit_writer.h +++ b/src/utils/bit_writer.h @@ -90,7 +90,7 @@ static WEBP_INLINE uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw) { return bw->buf_; } -// Returns 1 on success. +// Returns 0 in case of memory allocation error. int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size); void VP8LBitWriterDestroy(VP8LBitWriter* const bw); @@ -111,7 +111,7 @@ void VP8LBitWriterDestroy(VP8LBitWriter* const bw); // For n bits, we take the last 5 bytes, OR that with high bits in BYTE-0, // and locate the rest in BYTE+1 and BYTE+2. // -// returns 1 on success. +// VP8LBitWriter's error_ flag is set in case of memory allocation error. void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits); //------------------------------------------------------------------------------ diff --git a/src/webp/encode.h b/src/webp/encode.h index 8704810f..28a1d6bf 100644 --- a/src/webp/encode.h +++ b/src/webp/encode.h @@ -218,7 +218,7 @@ struct WebPPicture { WebPProgressHook progress_hook; // if not NULL, called while encoding. - int use_argb_input; // Flag for encoder to read argb pixels as input. + int use_argb_input; // Flag for encoder to use argb pixels as input. uint32_t* argb; // Pointer to argb (32 bit) plane. int argb_stride; // This is stride in pixels units, not bytes. }; From bc7037465d7e1512fc64c962119deadeb9fc52f2 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Tue, 3 Apr 2012 14:24:25 +0000 Subject: [PATCH 03/46] Add backward_ref, histogram & huffman encode modules from lossless. Change-Id: Iac056d27972956782defa182caa3ea400cdb77f8 --- src/enc/backward_references.c | 787 ++++++++++++++++++++++++++++++++++ src/enc/backward_references.h | 234 ++++++++++ src/enc/histogram.c | 515 ++++++++++++++++++++++ src/enc/histogram.h | 152 +++++++ src/utils/huffman_encode.c | 313 ++++++++++++++ src/utils/huffman_encode.h | 52 +++ 6 files changed, 2053 insertions(+) create mode 100644 src/enc/backward_references.c create mode 100644 src/enc/backward_references.h create mode 100644 src/enc/histogram.c create mode 100644 src/enc/histogram.h create mode 100644 src/utils/huffman_encode.c create mode 100644 src/utils/huffman_encode.h diff --git a/src/enc/backward_references.c b/src/enc/backward_references.c new file mode 100644 index 00000000..2561472c --- /dev/null +++ b/src/enc/backward_references.c @@ -0,0 +1,787 @@ +// 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/ +// ----------------------------------------------------------------------------- +// +// Author: Jyrki Alakuijala (jyrki@google.com) +// + +#include +#include +#include +#include + +#include "./backward_references.h" +#include "./histogram.h" +#include "../utils/color_cache.h" + +#define VALUES_IN_BYTE 256 + +static const uint8_t plane_to_code_lut[128] = { + 96, 73, 55, 39, 23, 13, 5, 1, 255, 255, 255, 255, 255, 255, 255, 255, + 101, 78, 58, 42, 26, 16, 8, 2, 0, 3, 9, 17, 27, 43, 59, 79, + 102, 86, 62, 46, 32, 20, 10, 6, 4, 7, 11, 21, 33, 47, 63, 87, + 105, 90, 70, 52, 37, 28, 18, 14, 12, 15, 19, 29, 38, 53, 71, 91, + 110, 99, 82, 66, 48, 35, 30, 24, 22, 25, 31, 36, 49, 67, 83, 100, + 115, 108, 94, 76, 64, 50, 44, 40, 34, 41, 45, 51, 65, 77, 95, 109, + 118, 113, 103, 92, 80, 68, 60, 56, 54, 57, 61, 69, 81, 93, 104, 114, + 119, 116, 111, 106, 97, 88, 84, 74, 72, 75, 85, 89, 98, 107, 112, 117, +}; + +static const int kMinLength = 2; + +int DistanceToPlaneCode(int xsize, int dist) { + int yoffset = dist / xsize; + int xoffset = dist - yoffset * xsize; + if (xoffset <= 8 && yoffset < 8) { + return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1; + } else if (xoffset > xsize - 8 && yoffset < 7) { + return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1; + } + return dist + 120; +} + +static WEBP_INLINE int FindMatchLength(const uint32_t* array1, + const uint32_t* array2, + const int max_limit) { + int matched = 0; + while (matched < max_limit && array1[matched] == array2[matched]) { + ++matched; + } + return matched; +} + +#define HASH_BITS 18 +#define HASH_SIZE (1 << HASH_BITS) +static const uint64_t kHashMultiplier = 0xc6a4a7935bd1e995ULL; +static const int kWindowSize = (1 << 20) - 120; // A window with 1M pixels + // (4 megabytes) - 120 + // special codes for short + // distances. + +static WEBP_INLINE uint64_t GetHash64(uint64_t num) { + num *= kHashMultiplier; + num >>= 64 - HASH_BITS; + return num; +} + +static WEBP_INLINE uint64_t GetPixPair(const uint32_t* argb) { + return ((uint64_t)(argb[1]) << 32) | argb[0]; +} + +typedef struct { + // Stores the most recently added position with the given hash value. + int32_t hash_to_first_index_[HASH_SIZE]; + // chain_[pos] stores the previous position with the same hash value + // for every pixel in the image. + int32_t* chain_; +} VP8LHashChain; + +static int VP8LHashChain_Init(VP8LHashChain* p, int size) { + int i; + p->chain_ = (int*)malloc(size * sizeof(*p->chain_)); + if (!p->chain_) { + return 0; + } + for (i = 0; i < size; ++i) { + p->chain_[i] = -1; + } + for (i = 0; i < HASH_SIZE; ++i) { + p->hash_to_first_index_[i] = -1; + } + return 1; +} + +static void VP8LHashChain_Delete(VP8LHashChain* p) { + if (p != NULL) { + free(p->chain_); + } +} + +static void VP8LHashChain_Insert(VP8LHashChain* p, + const uint32_t* argb, int32_t ix) { + // Insertion of two pixels at a time. + const uint64_t key = GetPixPair(argb); + const uint64_t hash_code = GetHash64(key); + p->chain_[ix] = p->hash_to_first_index_[hash_code]; + p->hash_to_first_index_[hash_code] = ix; +} + +static int VP8LHashChain_FindCopy(VP8LHashChain* p, + int quality, + int index, int xsize, + const uint32_t* argb, + int maxlen, int* offset_out, + int* len_out) { + const uint64_t next_two_pixels = GetPixPair(&argb[index]); + const uint64_t hash_code = GetHash64(next_two_pixels); + int prev_length = 0; + int64_t best_val = 0; + int give_up = quality * 3 / 4 + 25; + const int min_pos = (index > kWindowSize) ? index - kWindowSize : 0; + int32_t pos; + int64_t length; + int64_t val; + int x; + int y; + int len = 0; + int offset = 0; + for (pos = p->hash_to_first_index_[hash_code]; + pos >= min_pos; + pos = p->chain_[pos]) { + if (give_up < 0) { + if (give_up < -quality * 8 || + best_val >= 0xff0000) { + break; + } + } + --give_up; + if (len != 0 && argb[pos + len - 1] != argb[index + len - 1]) { + continue; + } + length = FindMatchLength(argb + pos, argb + index, maxlen); + if (length < prev_length) { + continue; + } + val = 65536 * length; + // Favoring 2d locality here gives savings for certain images. + if (index - pos < 9 * xsize) { + y = (index - pos) / xsize; + x = (index - pos) % xsize; + if (x > xsize / 2) { + x = xsize - x; + } + if (x <= 7 && x >= -8) { + val -= y * y + x * x; + } else { + val -= 9 * 9 + 9 * 9; + } + } else { + val -= 9 * 9 + 9 * 9; + } + if (best_val < val) { + prev_length = length; + best_val = val; + len = length; + offset = index - pos; + if (length >= kMaxLength) { + break; + } + if ((offset == 1 || offset == xsize) && len >= 128) { + break; + } + } + } + *offset_out = offset; + *len_out = len; + return len >= kMinLength; +} + +static WEBP_INLINE void PushBackCopy(int length, + PixOrCopy* stream, + int* stream_size) { + while (length >= kMaxLength) { + stream[*stream_size] = PixOrCopyCreateCopy(1, kMaxLength); + ++(*stream_size); + length -= kMaxLength; + } + if (length > 0) { + stream[*stream_size] = PixOrCopyCreateCopy(1, length); + ++(*stream_size); + } +} + +void BackwardReferencesRle(int xsize, int ysize, const uint32_t* argb, + PixOrCopy* stream, int* stream_size) { + const int pix_count = xsize * ysize; + int streak = 0; + int i; + *stream_size = 0; + for (i = 0; i < pix_count; ++i) { + if (i >= 1 && argb[i] == argb[i - 1]) { + ++streak; + } else { + PushBackCopy(streak, stream, stream_size); + streak = 0; + stream[*stream_size] = PixOrCopyCreateLiteral(argb[i]); + ++(*stream_size); + } + } + PushBackCopy(streak, stream, stream_size); +} + +// Returns 1 when successful. +int BackwardReferencesHashChain(int xsize, int ysize, int use_palette, + const uint32_t* argb, int palette_bits, + int quality, + PixOrCopy* stream, int* stream_size) { + const int pix_count = xsize * ysize; + int i; + int ok = 0; + VP8LHashChain* hash_chain = (VP8LHashChain*)malloc(sizeof(*hash_chain)); + VP8LColorCache hashers; + if (!hash_chain || + !VP8LColorCacheInit(&hashers, palette_bits) || + !VP8LHashChain_Init(hash_chain, pix_count)) { + goto Error; + } + *stream_size = 0; + for (i = 0; i < pix_count; ) { + // Alternative#1: Code the pixels starting at 'i' using backward reference. + int offset = 0; + int len = 0; + if (i < pix_count - 1) { // FindCopy(i,..) reads pixels at [i] and [i + 1]. + int maxlen = pix_count - i; + if (maxlen > kMaxLength) { + maxlen = kMaxLength; + } + VP8LHashChain_FindCopy(hash_chain, quality, + i, xsize, argb, maxlen, &offset, &len); + } + if (len >= kMinLength) { + // Alternative#2: Insert the pixel at 'i' as literal, and code the + // pixels starting at 'i + 1' using backward reference. + int offset2 = 0; + int len2 = 0; + int k; + VP8LHashChain_Insert(hash_chain, &argb[i], i); + if (i < pix_count - 2) { // FindCopy(i+1,..) reads [i + 1] and [i + 2]. + int maxlen = pix_count - (i + 1); + if (maxlen > kMaxLength) { + maxlen = kMaxLength; + } + VP8LHashChain_FindCopy(hash_chain, quality, + i + 1, xsize, argb, maxlen, &offset2, &len2); + if (len2 > len + 1) { + // Alternative#2 is a better match. So push pixel at 'i' as literal. + if (use_palette && VP8LColorCacheContains(&hashers, argb[i])) { + const int ix = VP8LColorCacheGetIndex(&hashers, argb[i]); + stream[*stream_size] = PixOrCopyCreatePaletteIx(ix); + } else { + stream[*stream_size] = PixOrCopyCreateLiteral(argb[i]); + } + ++(*stream_size); + VP8LColorCacheInsert(&hashers, argb[i]); + i++; // Backward reference to be done for next pixel. + len = len2; + offset = offset2; + } + } + if (len >= kMaxLength) { + len = kMaxLength - 1; + } + stream[*stream_size] = PixOrCopyCreateCopy(offset, len); + ++(*stream_size); + for (k = 0; k < len; ++k) { + VP8LColorCacheInsert(&hashers, argb[i + k]); + if (k != 0 && i + k + 1 < pix_count) { + // Add to the hash_chain (but cannot add the last pixel). + VP8LHashChain_Insert(hash_chain, &argb[i + k], i + k); + } + } + i += len; + } else { + if (use_palette && VP8LColorCacheContains(&hashers, argb[i])) { + // push pixel as a palette pixel + int ix = VP8LColorCacheGetIndex(&hashers, argb[i]); + stream[*stream_size] = PixOrCopyCreatePaletteIx(ix); + } else { + stream[*stream_size] = PixOrCopyCreateLiteral(argb[i]); + } + ++(*stream_size); + VP8LColorCacheInsert(&hashers, argb[i]); + if (i + 1 < pix_count) { + VP8LHashChain_Insert(hash_chain, &argb[i], i); + } + ++i; + } + } + ok = 1; +Error: + VP8LHashChain_Delete(hash_chain); + free(hash_chain); + VP8LColorCacheDelete(&hashers); + return ok; +} + +typedef struct { + double alpha_[VALUES_IN_BYTE]; + double red_[VALUES_IN_BYTE]; + double literal_[PIX_OR_COPY_CODES_MAX]; + double blue_[VALUES_IN_BYTE]; + double distance_[DISTANCE_CODES_MAX]; + int palette_bits_; +} CostModel; + +static int CostModel_Build(CostModel* p, int xsize, int ysize, + int recursion_level, int use_palette, + const uint32_t* argb, int palette_bits) { + int ok = 0; + int stream_size; + Histogram histo; + int i; + PixOrCopy* stream = (PixOrCopy*)malloc(xsize * ysize * sizeof(*stream)); + if (stream == NULL) { + goto Error; + } + p->palette_bits_ = palette_bits; + if (recursion_level > 0) { + if (!BackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1, + use_palette, argb, + palette_bits, + &stream[0], &stream_size)) { + goto Error; + } + } else { + const int quality = 100; + if (!BackwardReferencesHashChain(xsize, ysize, use_palette, argb, + palette_bits, quality, + &stream[0], &stream_size)) { + goto Error; + } + } + HistogramInit(&histo, palette_bits); + for (i = 0; i < stream_size; ++i) { + HistogramAddSinglePixOrCopy(&histo, stream[i]); + } + ConvertPopulationCountTableToBitEstimates( + HistogramNumPixOrCopyCodes(&histo), + &histo.literal_[0], &p->literal_[0]); + ConvertPopulationCountTableToBitEstimates( + VALUES_IN_BYTE, &histo.red_[0], &p->red_[0]); + ConvertPopulationCountTableToBitEstimates( + VALUES_IN_BYTE, &histo.blue_[0], &p->blue_[0]); + ConvertPopulationCountTableToBitEstimates( + VALUES_IN_BYTE, &histo.alpha_[0], &p->alpha_[0]); + ConvertPopulationCountTableToBitEstimates( + DISTANCE_CODES_MAX, &histo.distance_[0], &p->distance_[0]); + ok = 1; +Error: + free(stream); + return ok; +} + +static WEBP_INLINE double CostModel_LiteralCost(const CostModel* p, + uint32_t v) { + return p->alpha_[v >> 24] + + p->red_[(v >> 16) & 0xff] + + p->literal_[(v >> 8) & 0xff] + + p->blue_[v & 0xff]; +} + +static WEBP_INLINE double CostModel_PaletteCost(const CostModel* p, + uint32_t ix) { + int literal_ix = VALUES_IN_BYTE + kLengthCodes + ix; + return p->literal_[literal_ix]; +} + +static WEBP_INLINE double CostModel_LengthCost(const CostModel* p, + uint32_t len) { + int code, extra_bits_count, extra_bits_value; + PrefixEncode(len, &code, &extra_bits_count, &extra_bits_value); + return p->literal_[VALUES_IN_BYTE + code] + extra_bits_count; +} + +static WEBP_INLINE double CostModel_DistanceCost(const CostModel* p, + uint32_t distance) { + int code, extra_bits_count, extra_bits_value; + PrefixEncode(distance, &code, &extra_bits_count, &extra_bits_value); + return p->distance_[code] + extra_bits_count; +} + +static int BackwardReferencesHashChainDistanceOnly( + int xsize, int ysize, + int recursive_cost_model, + int use_palette, + const uint32_t* argb, + int palette_bits, + uint32_t* dist_array) { + const int quality = 100; + const int pix_count = xsize * ysize; + double* cost = (double*)malloc(pix_count * sizeof(*cost)); + int i; + CostModel* cost_model = (CostModel*)malloc(sizeof(*cost_model)); + + VP8LColorCache hashers; + VP8LHashChain* hash_chain = (VP8LHashChain*)malloc(sizeof(*hash_chain)); + int ok = 0; + if (cost == NULL || + cost_model == NULL || + hash_chain == NULL || + !VP8LColorCacheInit(&hashers, palette_bits)) { + goto Error; + } + VP8LHashChain_Init(hash_chain, pix_count); + CostModel_Build(cost_model, xsize, ysize, recursive_cost_model, + use_palette, argb, palette_bits); + for (i = 0; i < pix_count; ++i) { + cost[i] = 1e100; + } + // We loop one pixel at a time, but store all currently best points to + // non-processed locations from this point. + dist_array[0] = 0; + for (i = 0; i < pix_count; ++i) { + double prev_cost = 0.0; + int shortmax; + if (i > 0) { + prev_cost = cost[i - 1]; + } + for (shortmax = 0; shortmax < 2; ++shortmax) { + int offset = 0; + int len = 0; + if (i < pix_count - 1) { // FindCopy reads pixels at [i] and [i + 1]. + int maxlen = shortmax ? 2 : kMaxLength; + if (maxlen > pix_count - i) { + maxlen = pix_count - i; + } + VP8LHashChain_FindCopy(hash_chain, quality, i, xsize, argb, maxlen, + &offset, &len); + } + if (len >= kMinLength) { + const int code = DistanceToPlaneCode(xsize, offset); + const double distance_cost = + prev_cost + CostModel_DistanceCost(cost_model, code); + int k; + for (k = 1; k < len; ++k) { + const double cost_val = + distance_cost + CostModel_LengthCost(cost_model, k); + if (cost[i + k] > cost_val) { + cost[i + k] = cost_val; + dist_array[i + k] = k + 1; + } + } + // This if is for speedup only. It roughly doubles the speed, and + // makes compression worse by .1 %. + if (len >= 128 && code < 2) { + // Long copy for short distances, let's skip the middle + // lookups for better copies. + // 1) insert the hashes. + for (k = 0; k < len; ++k) { + VP8LColorCacheInsert(&hashers, argb[i + k]); + if (i + k + 1 < pix_count) { + // Add to the hash_chain (but cannot add the last pixel). + VP8LHashChain_Insert(hash_chain, &argb[i + k], i + k); + } + } + // 2) jump. + i += len - 1; // for loop does ++i, thus -1 here. + goto next_symbol; + } + } + } + if (i < pix_count - 1) { + VP8LHashChain_Insert(hash_chain, &argb[i], i); + } + { + // inserting a literal pixel + double cost_val = prev_cost; + double mul0 = 1.0; + double mul1 = 1.0; + if (recursive_cost_model == 0) { + mul0 = 0.68; + mul1 = 0.82; + } + if (use_palette && VP8LColorCacheContains(&hashers, argb[i])) { + int ix = VP8LColorCacheGetIndex(&hashers, argb[i]); + cost_val += CostModel_PaletteCost(cost_model, ix) * mul0; + } else { + cost_val += CostModel_LiteralCost(cost_model, argb[i]) * mul1; + } + if (cost[i] > cost_val) { + cost[i] = cost_val; + dist_array[i] = 1; // only one is inserted. + } + VP8LColorCacheInsert(&hashers, argb[i]); + } + next_symbol: ; + } + // Last pixel still to do, it can only be a single step if not reached + // through cheaper means already. + ok = 1; +Error: + if (hash_chain) VP8LHashChain_Delete(hash_chain); + free(hash_chain); + free(cost_model); + free(cost); + VP8LColorCacheDelete(&hashers); + return ok; +} + +static void TraceBackwards(const uint32_t* dist_array, int dist_array_size, + uint32_t** chosen_path, int* chosen_path_size) { + int i; + // Count how many. + int count = 0; + for (i = dist_array_size - 1; i >= 0; ) { + int k = dist_array[i]; + assert(k >= 1); + ++count; + i -= k; + } + // Allocate. + *chosen_path_size = count; + *chosen_path = (uint32_t*)malloc(count * sizeof(*chosen_path)); + // Write in reverse order. + for (i = dist_array_size - 1; i >= 0; ) { + int k = dist_array[i]; + assert(k >= 1); + (*chosen_path)[--count] = k; + i -= k; + } +} + +static int BackwardReferencesHashChainFollowChosenPath( + int xsize, + int ysize, + int use_palette, + const uint32_t* argb, + int palette_bits, + uint32_t* chosen_path, + int chosen_path_size, + PixOrCopy* stream, + int* stream_size) { + const int quality = 100; + const int pix_count = xsize * ysize; + int i = 0; + int k; + int ix; + int ok = 0; + VP8LColorCache hashers; + VP8LHashChain* hash_chain = (VP8LHashChain*)malloc(sizeof(*hash_chain)); + VP8LHashChain_Init(hash_chain, pix_count); + if (hash_chain == NULL || + !VP8LColorCacheInit(&hashers, palette_bits)) { + goto Error; + } + *stream_size = 0; + for (ix = 0; ix < chosen_path_size; ++ix) { + int offset = 0; + int len = 0; + int maxlen = chosen_path[ix]; + if (maxlen != 1) { + VP8LHashChain_FindCopy(hash_chain, quality, + i, xsize, argb, maxlen, &offset, &len); + assert(len == maxlen); + stream[*stream_size] = PixOrCopyCreateCopy(offset, len); + ++(*stream_size); + for (k = 0; k < len; ++k) { + VP8LColorCacheInsert(&hashers, argb[i + k]); + if (i + k + 1 < pix_count) { + // Add to the hash_chain (but cannot add the last pixel). + VP8LHashChain_Insert(hash_chain, &argb[i + k], i + k); + } + } + i += len; + } else { + if (use_palette && VP8LColorCacheContains(&hashers, argb[i])) { + // push pixel as a palette pixel + int ix = VP8LColorCacheGetIndex(&hashers, argb[i]); + stream[*stream_size] = PixOrCopyCreatePaletteIx(ix); + } else { + stream[*stream_size] = PixOrCopyCreateLiteral(argb[i]); + } + ++(*stream_size); + VP8LColorCacheInsert(&hashers, argb[i]); + if (i + 1 < pix_count) { + VP8LHashChain_Insert(hash_chain, &argb[i], i); + } + ++i; + } + } + ok = 1; +Error: + VP8LHashChain_Delete(hash_chain); + if (hash_chain) { + free(hash_chain); + } + VP8LColorCacheDelete(&hashers); + return ok; +} + +// Returns 1 on success. +int BackwardReferencesTraceBackwards(int xsize, int ysize, + int recursive_cost_model, + int use_palette, + const uint32_t* argb, + int palette_bits, + PixOrCopy* stream, + int* stream_size) { + int ok = 0; + const int dist_array_size = xsize * ysize; + uint32_t* chosen_path = NULL; + int chosen_path_size = 0; + uint32_t* const dist_array = (uint32_t*) + malloc(dist_array_size * sizeof(*dist_array)); + if (dist_array == NULL) { + goto Error; + } + *stream_size = 0; + if (!BackwardReferencesHashChainDistanceOnly( + xsize, ysize, recursive_cost_model, use_palette, argb, palette_bits, + dist_array)) { + free(dist_array); + goto Error; + } + TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size); + free(dist_array); + if (!BackwardReferencesHashChainFollowChosenPath( + xsize, ysize, use_palette, argb, palette_bits, + chosen_path, chosen_path_size, + stream, stream_size)) { + goto Error; + } + ok = 1; +Error: + free(chosen_path); + return ok; +} + +void BackwardReferences2DLocality(int xsize, int data_size, PixOrCopy* data) { + int i; + for (i = 0; i < data_size; ++i) { + if (PixOrCopyIsCopy(&data[i])) { + int dist = data[i].argb_or_offset; + int transformed_dist = DistanceToPlaneCode(xsize, dist); + data[i].argb_or_offset = transformed_dist; + } + } +} + +int VerifyBackwardReferences(const uint32_t* argb, int xsize, int ysize, + int palette_bits, + const PixOrCopy* lit, + int lit_size) { + int num_pixels = 0; + int i; + VP8LColorCache hashers; + VP8LColorCacheInit(&hashers, palette_bits); + for (i = 0; i < lit_size; ++i) { + if (PixOrCopyIsLiteral(&lit[i])) { + if (argb[num_pixels] != PixOrCopyArgb(&lit[i])) { + printf("i %d, pixel %d, original: 0x%08x, literal: 0x%08x\n", + i, num_pixels, argb[num_pixels], PixOrCopyArgb(&lit[i])); + VP8LColorCacheDelete(&hashers); + return 0; + } + VP8LColorCacheInsert(&hashers, argb[num_pixels]); + ++num_pixels; + } else if (PixOrCopyIsPaletteIx(&lit[i])) { + uint32_t palette_entry = + VP8LColorCacheLookup(&hashers, PixOrCopyPaletteIx(&lit[i])); + if (argb[num_pixels] != palette_entry) { + printf("i %d, pixel %d, original: 0x%08x, palette_ix: %d, " + "palette_entry: 0x%08x\n", + i, num_pixels, argb[num_pixels], PixOrCopyPaletteIx(&lit[i]), + palette_entry); + VP8LColorCacheDelete(&hashers); + return 0; + } + VP8LColorCacheInsert(&hashers, argb[num_pixels]); + ++num_pixels; + } else if (PixOrCopyIsCopy(&lit[i])) { + int k; + if (PixOrCopyDistance(&lit[i]) == 0) { + printf("Bw reference with zero distance.\n"); + VP8LColorCacheDelete(&hashers); + return 0; + } + for (k = 0; k < lit[i].len; ++k) { + if (argb[num_pixels] != + argb[num_pixels - PixOrCopyDistance(&lit[i])]) { + printf("i %d, pixel %d, original: 0x%08x, copied: 0x%08x, dist: %d\n", + i, num_pixels, argb[num_pixels], + argb[num_pixels - PixOrCopyDistance(&lit[i])], + PixOrCopyDistance(&lit[i])); + VP8LColorCacheDelete(&hashers); + return 0; + } + VP8LColorCacheInsert(&hashers, argb[num_pixels]); + ++num_pixels; + } + } + } + { + const int pix_count = xsize * ysize; + if (num_pixels != pix_count) { + printf("verify failure: %d != %d\n", num_pixels, pix_count); + VP8LColorCacheDelete(&hashers); + return 0; + } + } + VP8LColorCacheDelete(&hashers); + return 1; +} + +// Returns 1 on success. +static int ComputePaletteHistogram(const uint32_t* argb, int xsize, int ysize, + PixOrCopy* stream, int stream_size, + int palette_bits, Histogram* histo) { + int pixel_index = 0; + int i; + uint32_t k; + VP8LColorCache hashers; + if (!VP8LColorCacheInit(&hashers, palette_bits)) { + return 0; + } + for (i = 0; i < stream_size; ++i) { + const PixOrCopy v = stream[i]; + if (PixOrCopyIsLiteral(&v)) { + if (palette_bits != 0 && + VP8LColorCacheContains(&hashers, argb[pixel_index])) { + // push pixel as a palette pixel + const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]); + HistogramAddSinglePixOrCopy(histo, PixOrCopyCreatePaletteIx(ix)); + } else { + HistogramAddSinglePixOrCopy(histo, v); + } + } else { + HistogramAddSinglePixOrCopy(histo, v); + } + for (k = 0; k < PixOrCopyLength(&v); ++k) { + VP8LColorCacheInsert(&hashers, argb[pixel_index]); + ++pixel_index; + } + } + assert(pixel_index == xsize * ysize); + (void)xsize; // xsize is not used in non-debug compilations otherwise. + (void)ysize; // ysize is not used in non-debug compilations otherwise. + VP8LColorCacheDelete(&hashers); + return 1; +} + +// Returns how many bits are to be used for a palette. +int CalculateEstimateForPaletteSize(const uint32_t* argb, + int xsize, int ysize, + int* best_palette_bits) { + int ok = 0; + int palette_bits; + double lowest_entropy = 1e99; + PixOrCopy* stream = (PixOrCopy*)malloc(xsize * ysize * sizeof(*stream)); + int stream_size; + static const double kSmallPenaltyForLargePalette = 4.0; + static const int quality = 30; + if (stream == NULL || + !BackwardReferencesHashChain(xsize, ysize, + 0, argb, 0, quality, stream, &stream_size)) { + goto Error; + } + for (palette_bits = 0; palette_bits < 12; ++palette_bits) { + double cur_entropy; + Histogram histo; + HistogramInit(&histo, palette_bits); + ComputePaletteHistogram(argb, xsize, ysize, &stream[0], stream_size, + palette_bits, &histo); + cur_entropy = HistogramEstimateBits(&histo) + + kSmallPenaltyForLargePalette * palette_bits; + if (palette_bits == 0 || cur_entropy < lowest_entropy) { + *best_palette_bits = palette_bits; + lowest_entropy = cur_entropy; + } + } + ok = 1; +Error: + free(stream); + return ok; +} diff --git a/src/enc/backward_references.h b/src/enc/backward_references.h new file mode 100644 index 00000000..b9a91894 --- /dev/null +++ b/src/enc/backward_references.h @@ -0,0 +1,234 @@ +// 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/ +// ----------------------------------------------------------------------------- +// +// Author: Jyrki Alakuijala (jyrki@google.com) +// + +#ifndef WEBP_ENC_BACKWARD_REFERENCES_H_ +#define WEBP_ENC_BACKWARD_REFERENCES_H_ + +#include +#include + +#include "../webp/types.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +// Backward reference distance prefix codes +#define DISTANCE_CODES_MAX 40 + +// Compression constants +#define CODE_LENGTH_CODES 19 +static const int kLengthCodes = 24; +static const int kPaletteCodeBitsMax = 11; +#define PIX_OR_COPY_CODES_MAX (256 + 24 + (1 << 11)) +static const int kMaxLength = 4096; + +// use GNU builtins where available. +#if defined(__GNUC__) && \ + ((__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || __GNUC__ >= 4) +static WEBP_INLINE int BitsLog2Floor(uint32_t n) { + return n == 0 ? -1 : 31 ^ __builtin_clz(n); +} +#else +static WEBP_INLINE int BitsLog2Floor(uint32_t n) { + int log; + uint32_t value; + int i; + if (n == 0) + return -1; + log = 0; + value = n; + for (i = 4; i >= 0; --i) { + int shift = (1 << i); + uint32_t x = value >> shift; + if (x != 0) { + value = x; + log += shift; + } + } + return log; +} +#endif + +static WEBP_INLINE int BitsLog2Ceiling(uint32_t n) { + int floor = BitsLog2Floor(n); + if (n == (n & ~(n - 1))) // zero or a power of two. + return floor; + else + return floor + 1; +} + +// Splitting of distance and length codes into prefixes and +// extra bits. The prefixes are encoded with an entropy code +// while the extra bits are stored just as normal bits. +static WEBP_INLINE void PrefixEncode( + int distance, + int *code, + int *extra_bits_count, + int *extra_bits_value) { + // Collect the two most significant bits where the highest bit is 1. + const int highest_bit = BitsLog2Floor(--distance); + // & 0x3f is to make behavior well defined when highest_bit + // does not exist or is the least significant bit. + const int second_highest_bit = + (distance >> ((highest_bit - 1) & 0x3f)) & 1; + *extra_bits_count = (highest_bit > 0) ? highest_bit - 1 : 0; + *extra_bits_value = distance & ((1 << *extra_bits_count) - 1); + *code = (highest_bit > 0) ? 2 * highest_bit + second_highest_bit : + (highest_bit == 0) ? 1 : 0; +} + +enum Mode { + kLiteral, + kPaletteIx, + kCopy, + kNone, +}; + +typedef struct { + // mode as uint8_t to make the memory layout to be exactly 8 bytes. + uint8_t mode; + uint16_t len; + uint32_t argb_or_offset; +} PixOrCopy; + +static WEBP_INLINE PixOrCopy PixOrCopyCreateCopy(uint32_t offset, + uint16_t len) { + PixOrCopy retval; + retval.mode = kCopy; + retval.argb_or_offset = offset; + retval.len = len; + return retval; +} + +static WEBP_INLINE PixOrCopy PixOrCopyCreatePaletteIx(int ix) { + PixOrCopy retval; + assert(ix >= 0); + assert(ix < (1 << kPaletteCodeBitsMax)); + retval.mode = kPaletteIx; + retval.argb_or_offset = ix; + retval.len = 1; + return retval; +} + +static WEBP_INLINE PixOrCopy PixOrCopyCreateLiteral(uint32_t argb) { + PixOrCopy retval; + retval.mode = kLiteral; + retval.argb_or_offset = argb; + retval.len = 1; + return retval; +} + +static WEBP_INLINE int PixOrCopyIsLiteral(const PixOrCopy *p) { + return p->mode == kLiteral; +} + +static WEBP_INLINE int PixOrCopyIsPaletteIx(const PixOrCopy *p) { + return p->mode == kPaletteIx; +} + +static WEBP_INLINE int PixOrCopyIsCopy(const PixOrCopy *p) { + return p->mode == kCopy; +} + +static WEBP_INLINE uint32_t PixOrCopyLiteral(const PixOrCopy *p, + int component) { + assert(p->mode == kLiteral); + return (p->argb_or_offset >> (component * 8)) & 0xff; +} + +static WEBP_INLINE uint32_t PixOrCopyLength(const PixOrCopy *p) { + return p->len; +} + +static WEBP_INLINE uint32_t PixOrCopyArgb(const PixOrCopy *p) { + assert(p->mode == kLiteral); + return p->argb_or_offset; +} + +static WEBP_INLINE uint32_t PixOrCopyPaletteIx(const PixOrCopy *p) { + assert(p->mode == kPaletteIx); + assert(p->argb_or_offset < (1 << kPaletteCodeBitsMax)); + return p->argb_or_offset; +} + +static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy *p) { + assert(p->mode == kCopy); + return p->argb_or_offset; +} + +static WEBP_INLINE void PixOrCopyLengthCodeAndBits( + const PixOrCopy *p, int *code, int *n_bits, int *bits) { + assert(p->len >= 1 && p->len <= kMaxLength); + PrefixEncode(p->len, code, n_bits, bits); +} + + +// Ridiculously simple backward references for images where it is unlikely +// that there are large backward references (photos). +void BackwardReferencesRle( + int xsize, + int ysize, + const uint32_t *argb, + PixOrCopy *stream, + int *stream_size); + +// This is a simple fast function for obtaining backward references +// based on simple heuristics. Returns 1 on success. +int BackwardReferencesHashChain( + int xsize, + int ysize, + int use_palette, + const uint32_t *argb, + int palette_bits, + int quality, + PixOrCopy *stream, + int *stream_size); + +// This method looks for a shortest path through the backward reference +// network based on a cost model generated by a first round of compression. +// Returns 1 on success. +int BackwardReferencesTraceBackwards( + int xsize, + int ysize, + int recursive_cost_model, + int use_palette, + const uint32_t *argb, + int palette_bits, + PixOrCopy *stream, + int *stream_size); + +// Convert backward references that are of linear distance along +// the image scan lines to have a 2d locality indexing where +// smaller values are used for backward references that are close by. +void BackwardReferences2DLocality(int xsize, int data_size, + PixOrCopy *data); + +// Internals of locality transform exposed for testing use. +int DistanceToPlaneCode(int xsize, int distance); + +// Returns true if the given backward references actually produce +// the image given in tuple (argb, xsize, ysize). +int VerifyBackwardReferences(const uint32_t* argb, + int xsize, int ysize, + int palette_bits, + const PixOrCopy *lit, + int lit_size); + +// Produce an estimate for a good emerging palette size for the image. +int CalculateEstimateForPaletteSize(const uint32_t *argb, + int xsize, int ysize, + int *best_palette_bits); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif // WEBP_ENC_BACKWARD_REFERENCES_H_ diff --git a/src/enc/histogram.c b/src/enc/histogram.c new file mode 100644 index 00000000..528a6452 --- /dev/null +++ b/src/enc/histogram.c @@ -0,0 +1,515 @@ +// 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/ +// ----------------------------------------------------------------------------- +// +// Author: Jyrki Alakuijala (jyrki@google.com) +// + + +#include +#include + +#include "./backward_references.h" +#include "./histogram.h" + +// A lookup table for small values of log(int) to be used in entropy +// computation. +// +// ", ".join(["%.16ff" % x for x in [0.0]+[log(x) for x in range(1, 256)]]) +static const float kLogTable[] = { + 0.0000000000000000f, 0.0000000000000000f, 0.6931471805599453f, + 1.0986122886681098f, 1.3862943611198906f, 1.6094379124341003f, + 1.7917594692280550f, 1.9459101490553132f, 2.0794415416798357f, + 2.1972245773362196f, 2.3025850929940459f, 2.3978952727983707f, + 2.4849066497880004f, 2.5649493574615367f, 2.6390573296152584f, + 2.7080502011022101f, 2.7725887222397811f, 2.8332133440562162f, + 2.8903717578961645f, 2.9444389791664403f, 2.9957322735539909f, + 3.0445224377234230f, 3.0910424533583161f, 3.1354942159291497f, + 3.1780538303479458f, 3.2188758248682006f, 3.2580965380214821f, + 3.2958368660043291f, 3.3322045101752038f, 3.3672958299864741f, + 3.4011973816621555f, 3.4339872044851463f, 3.4657359027997265f, + 3.4965075614664802f, 3.5263605246161616f, 3.5553480614894135f, + 3.5835189384561099f, 3.6109179126442243f, 3.6375861597263857f, + 3.6635616461296463f, 3.6888794541139363f, 3.7135720667043080f, + 3.7376696182833684f, 3.7612001156935624f, 3.7841896339182610f, + 3.8066624897703196f, 3.8286413964890951f, 3.8501476017100584f, + 3.8712010109078911f, 3.8918202981106265f, 3.9120230054281460f, + 3.9318256327243257f, 3.9512437185814275f, 3.9702919135521220f, + 3.9889840465642745f, 4.0073331852324712f, 4.0253516907351496f, + 4.0430512678345503f, 4.0604430105464191f, 4.0775374439057197f, + 4.0943445622221004f, 4.1108738641733114f, 4.1271343850450917f, + 4.1431347263915326f, 4.1588830833596715f, 4.1743872698956368f, + 4.1896547420264252f, 4.2046926193909657f, 4.2195077051761070f, + 4.2341065045972597f, 4.2484952420493594f, 4.2626798770413155f, + 4.2766661190160553f, 4.2904594411483910f, 4.3040650932041702f, + 4.3174881135363101f, 4.3307333402863311f, 4.3438054218536841f, + 4.3567088266895917f, 4.3694478524670215f, 4.3820266346738812f, + 4.3944491546724391f, 4.4067192472642533f, 4.4188406077965983f, + 4.4308167988433134f, 4.4426512564903167f, 4.4543472962535073f, + 4.4659081186545837f, 4.4773368144782069f, 4.4886363697321396f, + 4.4998096703302650f, 4.5108595065168497f, 4.5217885770490405f, + 4.5325994931532563f, 4.5432947822700038f, 4.5538768916005408f, + 4.5643481914678361f, 4.5747109785033828f, 4.5849674786705723f, + 4.5951198501345898f, 4.6051701859880918f, 4.6151205168412597f, + 4.6249728132842707f, 4.6347289882296359f, 4.6443908991413725f, + 4.6539603501575231f, 4.6634390941120669f, 4.6728288344619058f, + 4.6821312271242199f, 4.6913478822291435f, 4.7004803657924166f, + 4.7095302013123339f, 4.7184988712950942f, 4.7273878187123408f, + 4.7361984483944957f, 4.7449321283632502f, 4.7535901911063645f, + 4.7621739347977563f, 4.7706846244656651f, 4.7791234931115296f, + 4.7874917427820458f, 4.7957905455967413f, 4.8040210447332568f, + 4.8121843553724171f, 4.8202815656050371f, 4.8283137373023015f, + 4.8362819069514780f, 4.8441870864585912f, 4.8520302639196169f, + 4.8598124043616719f, 4.8675344504555822f, 4.8751973232011512f, + 4.8828019225863706f, 4.8903491282217537f, 4.8978397999509111f, + 4.9052747784384296f, 4.9126548857360524f, 4.9199809258281251f, + 4.9272536851572051f, 4.9344739331306915f, 4.9416424226093039f, + 4.9487598903781684f, 4.9558270576012609f, 4.9628446302599070f, + 4.9698132995760007f, 4.9767337424205742f, 4.9836066217083363f, + 4.9904325867787360f, 4.9972122737641147f, 5.0039463059454592f, + 5.0106352940962555f, 5.0172798368149243f, 5.0238805208462765f, + 5.0304379213924353f, 5.0369526024136295f, 5.0434251169192468f, + 5.0498560072495371f, 5.0562458053483077f, 5.0625950330269669f, + 5.0689042022202315f, 5.0751738152338266f, 5.0814043649844631f, + 5.0875963352323836f, 5.0937502008067623f, 5.0998664278241987f, + 5.1059454739005803f, 5.1119877883565437f, 5.1179938124167554f, + 5.1239639794032588f, 5.1298987149230735f, 5.1357984370502621f, + 5.1416635565026603f, 5.1474944768134527f, 5.1532915944977793f, + 5.1590552992145291f, 5.1647859739235145f, 5.1704839950381514f, + 5.1761497325738288f, 5.1817835502920850f, 5.1873858058407549f, + 5.1929568508902104f, 5.1984970312658261f, 5.2040066870767951f, + 5.2094861528414214f, 5.2149357576089859f, 5.2203558250783244f, + 5.2257466737132017f, 5.2311086168545868f, 5.2364419628299492f, + 5.2417470150596426f, 5.2470240721604862f, 5.2522734280466299f, + 5.2574953720277815f, 5.2626901889048856f, 5.2678581590633282f, + 5.2729995585637468f, 5.2781146592305168f, 5.2832037287379885f, + 5.2882670306945352f, 5.2933048247244923f, 5.2983173665480363f, + 5.3033049080590757f, 5.3082676974012051f, 5.3132059790417872f, + 5.3181199938442161f, 5.3230099791384085f, 5.3278761687895813f, + 5.3327187932653688f, 5.3375380797013179f, 5.3423342519648109f, + 5.3471075307174685f, 5.3518581334760666f, 5.3565862746720123f, + 5.3612921657094255f, 5.3659760150218512f, 5.3706380281276624f, + 5.3752784076841653f, 5.3798973535404597f, 5.3844950627890888f, + 5.3890717298165010f, 5.3936275463523620f, 5.3981627015177525f, + 5.4026773818722793f, 5.4071717714601188f, 5.4116460518550396f, + 5.4161004022044201f, 5.4205349992722862f, 5.4249500174814029f, + 5.4293456289544411f, 5.4337220035542400f, 5.4380793089231956f, + 5.4424177105217932f, 5.4467373716663099f, 5.4510384535657002f, + 5.4553211153577017f, 5.4595855141441589f, 5.4638318050256105f, + 5.4680601411351315f, 5.4722706736714750f, 5.4764635519315110f, + 5.4806389233419912f, 5.4847969334906548f, 5.4889377261566867f, + 5.4930614433405482f, 5.4971682252932021f, 5.5012582105447274f, + 5.5053315359323625f, 5.5093883366279774f, 5.5134287461649825f, + 5.5174528964647074f, 5.5214609178622460f, 5.5254529391317835f, + 5.5294290875114234f, 5.5333894887275203f, 5.5373342670185366f, + 5.5412635451584258f, +}; + +// Faster logarithm for small integers, with the property of log(0) == 0. +static WEBP_INLINE double FastLog(int v) { + if (v < (int)(sizeof(kLogTable) / sizeof(kLogTable[0]))) { + return kLogTable[v]; + } + return log(v); +} + +void ConvertPopulationCountTableToBitEstimates( + int num_symbols, + const int* const population_counts, + double* const output) { + int sum = 0; + int nonzeros = 0; + int i; + for (i = 0; i < num_symbols; ++i) { + sum += population_counts[i]; + if (population_counts[i] > 0) { + ++nonzeros; + } + } + if (nonzeros <= 1) { + memset(output, 0, num_symbols * sizeof(*output)); + return; + } + { + const double log2sum = log2(sum); + for (i = 0; i < num_symbols; ++i) { + if (population_counts[i] == 0) { + output[i] = log2sum; + } else { + output[i] = log2sum - log2(population_counts[i]); + } + } + } +} + +void HistogramAddSinglePixOrCopy(Histogram* const p, const PixOrCopy v) { + if (PixOrCopyIsLiteral(&v)) { + ++p->alpha_[PixOrCopyLiteral(&v, 3)]; + ++p->red_[PixOrCopyLiteral(&v, 2)]; + ++p->literal_[PixOrCopyLiteral(&v, 1)]; + ++p->blue_[PixOrCopyLiteral(&v, 0)]; + } else if (PixOrCopyIsPaletteIx(&v)) { + int literal_ix = 256 + kLengthCodes + PixOrCopyPaletteIx(&v); + ++p->literal_[literal_ix]; + } else { + int code, extra_bits_count, extra_bits_value; + PrefixEncode(PixOrCopyLength(&v), + &code, &extra_bits_count, &extra_bits_value); + ++p->literal_[256 + code]; + PrefixEncode(PixOrCopyDistance(&v), + &code, &extra_bits_count, &extra_bits_value); + ++p->distance_[code]; + } +} + +void HistogramBuild(Histogram* const p, + const PixOrCopy* const literal_and_length, + int n_literal_and_length) { + int i; + HistogramClear(p); + for (i = 0; i < n_literal_and_length; ++i) { + HistogramAddSinglePixOrCopy(p, literal_and_length[i]); + } +} + +double ShannonEntropy(const int* const array, int n) { + int i; + double retval = 0; + int sum = 0; + for (i = 0; i < n; ++i) { + if (array[i] != 0) { + sum += array[i]; + retval += array[i] * FastLog(array[i]); + } + } + retval -= sum * FastLog(sum); + retval *= -1.4426950408889634; // 1.0 / -FastLog(2); + return retval; +} + +static double BitsEntropy(const int* const array, int n) { + double retval = 0; + int sum = 0; + int nonzeros = 0; + int max_val = 0; + int i; + double mix; + for (i = 0; i < n; ++i) { + if (array[i] != 0) { + sum += array[i]; + ++nonzeros; + retval += array[i] * FastLog(array[i]); + if (max_val < array[i]) { + max_val = array[i]; + } + } + } + retval -= sum * FastLog(sum); + retval *= -1.4426950408889634; // 1.0 / -FastLog(2); + mix = 0.627; + if (nonzeros < 5) { + if (nonzeros <= 1) { + return 0; + } + // Two symbols, they will be 0 and 1 in a Huffman code. + // Let's mix in a bit of entropy to favor good clustering when + // distributions of these are combined. + if (nonzeros == 2) { + return 0.99 * sum + 0.01 * retval; + } + // No matter what the entropy says, we cannot be better than min_limit + // with Huffman coding. I am mixing a bit of entropy into the + // min_limit since it produces much better (~0.5 %) compression results + // perhaps because of better entropy clustering. + if (nonzeros == 3) { + mix = 0.95; + } else { + mix = 0.7; // nonzeros == 4. + } + } + { + double min_limit = 2 * sum - max_val; + min_limit = mix * min_limit + (1.0 - mix) * retval; + if (retval < min_limit) { + return min_limit; + } + } + return retval; +} + +double HistogramEstimateBitsBulk(const Histogram* const p) { + double retval = BitsEntropy(&p->literal_[0], HistogramNumPixOrCopyCodes(p)) + + BitsEntropy(&p->red_[0], 256) + + BitsEntropy(&p->blue_[0], 256) + + BitsEntropy(&p->alpha_[0], 256) + + BitsEntropy(&p->distance_[0], DISTANCE_CODES_MAX); + // Compute the extra bits cost. + size_t i; + for (i = 2; i < kLengthCodes - 2; ++i) { + retval += + (i >> 1) * p->literal_[256 + i + 2]; + } + for (i = 2; i < DISTANCE_CODES_MAX - 2; ++i) { + retval += (i >> 1) * p->distance_[i + 2]; + } + return retval; +} + +double HistogramEstimateBits(const Histogram* const p) { + return HistogramEstimateBitsHeader(p) + HistogramEstimateBitsBulk(p); +} + +// Returns the cost encode the rle-encoded entropy code. +// The constants in this function are experimental. +static double HuffmanCost(const int* const population, int length) { + // Small bias because Huffman code length is typically not stored in + // full length. + static const int kHuffmanCodeOfHuffmanCodeSize = CODE_LENGTH_CODES * 3; + static const double kSmallBias = 9.1; + double retval = kHuffmanCodeOfHuffmanCodeSize - kSmallBias; + int streak = 0; + int i = 0; + for (; i < length - 1; ++i) { + ++streak; + if (population[i] == population[i + 1]) { + continue; + } + last_streak_hack: + // population[i] points now to the symbol in the streak of same values. + if (streak > 3) { + if (population[i] == 0) { + retval += 1.5625 + 0.234375 * streak; + } else { + retval += 2.578125 + 0.703125 * streak; + } + } else { + if (population[i] == 0) { + retval += 1.796875 * streak; + } else { + retval += 3.28125 * streak; + } + } + streak = 0; + } + if (i == length - 1) { + ++streak; + goto last_streak_hack; + } + return retval; +} + +double HistogramEstimateBitsHeader(const Histogram* const p) { + return HuffmanCost(&p->alpha_[0], 256) + + HuffmanCost(&p->red_[0], 256) + + HuffmanCost(&p->literal_[0], HistogramNumPixOrCopyCodes(p)) + + HuffmanCost(&p->blue_[0], 256) + + HuffmanCost(&p->distance_[0], DISTANCE_CODES_MAX); +} + +int BuildHistogramImage(int xsize, int ysize, + int histobits, + int palettebits, + const PixOrCopy* backward_refs, + int backward_refs_size, + Histogram*** image_arg, + int* image_size) { + int histo_xsize = histobits ? (xsize + (1 << histobits) - 1) >> histobits : 1; + int histo_ysize = histobits ? (ysize + (1 << histobits) - 1) >> histobits : 1; + int i; + int x = 0; + int y = 0; + Histogram** image; + *image_arg = NULL; + *image_size = histo_xsize * histo_ysize; + image = (Histogram**)calloc(*image_size, sizeof(*image)); + if (image == NULL) { + return 0; + } + for (i = 0; i < *image_size; ++i) { + image[i] = (Histogram*)malloc(sizeof(*image[i])); + if (!image[i]) { + int k; + for (k = 0; k < *image_size; ++k) { + free(image[k]); + } + free(image); + return 0; + } + HistogramInit(image[i], palettebits); + } + // x and y trace the position in the image. + for (i = 0; i < backward_refs_size; ++i) { + const PixOrCopy v = backward_refs[i]; + const int ix = + histobits ? (y >> histobits) * histo_xsize + (x >> histobits) : 0; + HistogramAddSinglePixOrCopy(image[ix], v); + x += PixOrCopyLength(&v); + while (x >= xsize) { + x -= xsize; + ++y; + } + } + *image_arg = image; + return 1; +} + +int CombineHistogramImage(Histogram** in, + int in_size, + int quality, + Histogram*** out_arg, + int* out_size) { + int ok = 0; + int i; + unsigned int seed = 0; + int tries_with_no_success = 0; + int inner_iters = 10 + quality / 2; + int iter; + double* bit_costs = (double*)malloc(in_size * sizeof(*bit_costs)); + Histogram** out = (Histogram**)calloc(in_size, sizeof(*out)); + *out_arg = out; + *out_size = in_size; + if (bit_costs == NULL || out == NULL) { + goto Error; + } + // Copy + for (i = 0; i < in_size; ++i) { + Histogram* new_histo = (Histogram*)malloc(sizeof(*new_histo)); + if (new_histo == NULL) { + goto Error; + } + *new_histo = *(in[i]); + out[i] = new_histo; + bit_costs[i] = HistogramEstimateBits(out[i]); + } + // Collapse similar histograms. + for (iter = 0; iter < in_size * 3 && *out_size >= 2; ++iter) { + double best_val = 0; + int best_ix0 = 0; + int best_ix1 = 0; + // Try a few times. + int k; + for (k = 0; k < inner_iters; ++k) { + // Choose two, build a combo out of them. + double cost_val; + Histogram* combo; + int ix0 = rand_r(&seed) % *out_size; + int ix1; + int diff = ((k & 7) + 1) % (*out_size - 1); + if (diff >= 3) { + diff = rand_r(&seed) % (*out_size - 1); + } + ix1 = (ix0 + diff + 1) % *out_size; + if (ix0 == ix1) { + continue; + } + combo = (Histogram*)malloc(sizeof(*combo)); + if (combo == NULL) { + goto Error; + } + *combo = *out[ix0]; + HistogramAdd(combo, out[ix1]); + cost_val = HistogramEstimateBits(combo) - bit_costs[ix0] - bit_costs[ix1]; + if (best_val > cost_val) { + best_val = cost_val; + best_ix0 = ix0; + best_ix1 = ix1; + } + free(combo); + } + if (best_val < 0.0) { + HistogramAdd(out[best_ix0], out[best_ix1]); + bit_costs[best_ix0] = + best_val + bit_costs[best_ix0] + bit_costs[best_ix1]; + // Erase (*out)[best_ix1] + free(out[best_ix1]); + memmove(&out[best_ix1], &out[best_ix1 + 1], + (*out_size - best_ix1 - 1) * sizeof(out[0])); + memmove(&bit_costs[best_ix1], &bit_costs[best_ix1 + 1], + (*out_size - best_ix1 - 1) * sizeof(bit_costs[0])); + --(*out_size); + tries_with_no_success = 0; + } + if (++tries_with_no_success >= 50) { + break; + } + } + ok = 1; +Error: + free(bit_costs); + if (!ok) { + if (out) { + int i; + for (i = 0; i < *out_size; ++i) { + free(out[i]); + } + free(out); + } + } + return ok; +} + +// What is the bit cost of moving square_histogram from +// cur_symbol to candidate_symbol. +static double HistogramDistance(const Histogram* const square_histogram, + int cur_symbol, + int candidate_symbol, + Histogram** candidate_histograms) { + double new_bit_cost; + double previous_bit_cost; + Histogram modified; + if (cur_symbol == candidate_symbol) { + return 0; // Going nowhere. No savings. + } + previous_bit_cost = + HistogramEstimateBits(candidate_histograms[candidate_symbol]); + if (cur_symbol != -1) { + previous_bit_cost += + HistogramEstimateBits(candidate_histograms[cur_symbol]); + } + + // Compute the bit cost of the histogram where the data moves to. + modified = *candidate_histograms[candidate_symbol]; + HistogramAdd(&modified, square_histogram); + new_bit_cost = HistogramEstimateBits(&modified); + + // Compute the bit cost of the histogram where the data moves away. + if (cur_symbol != -1) { + modified = *candidate_histograms[cur_symbol]; + HistogramRemove(&modified, square_histogram); + new_bit_cost += HistogramEstimateBits(&modified); + } + return new_bit_cost - previous_bit_cost; +} + +void RefineHistogramImage(Histogram** raw, + int raw_size, + uint32_t* symbols, + int out_size, + Histogram** out) { + int i; + // Find the best 'out' histogram for each of the raw histograms + for (i = 0; i < raw_size; ++i) { + int best_out = 0; + double best_bits = HistogramDistance(raw[i], symbols[i], 0, out); + int k; + for (k = 1; k < out_size; ++k) { + double cur_bits = HistogramDistance(raw[i], symbols[i], k, out); + if (cur_bits < best_bits) { + best_bits = cur_bits; + best_out = k; + } + } + symbols[i] = best_out; + } + + // Recompute each out based on raw and symbols. + for (i = 0; i < out_size; ++i) { + HistogramClear(out[i]); + } + for (i = 0; i < raw_size; ++i) { + HistogramAdd(out[symbols[i]], raw[i]); + } +} diff --git a/src/enc/histogram.h b/src/enc/histogram.h new file mode 100644 index 00000000..38af5c36 --- /dev/null +++ b/src/enc/histogram.h @@ -0,0 +1,152 @@ +// 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/ +// ----------------------------------------------------------------------------- +// +// Author: Jyrki Alakuijala (jyrki@google.com) +// +// Models the histograms of literal and distance codes. + +#ifndef WEBP_ENC_HISTOGRAM_H_ +#define WEBP_ENC_HISTOGRAM_H_ + +#include +#include +#include +#include +#include + +#include "./backward_references.h" +#include "../webp/types.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +// A simple container for histograms of data. +typedef struct { + // literal_ contains green literal, palette-code and + // copy-length-prefix histogram + int literal_[PIX_OR_COPY_CODES_MAX]; + int red_[256]; + int blue_[256]; + int alpha_[256]; + // Backward reference prefix-code histogram. + int distance_[DISTANCE_CODES_MAX]; + int palette_code_bits_; +} Histogram; + +static WEBP_INLINE void HistogramClear(Histogram* const p) { + memset(&p->literal_[0], 0, sizeof(p->literal_)); + memset(&p->red_[0], 0, sizeof(p->red_)); + memset(&p->blue_[0], 0, sizeof(p->blue_)); + memset(&p->alpha_[0], 0, sizeof(p->alpha_)); + memset(&p->distance_[0], 0, sizeof(p->distance_)); +} + +static WEBP_INLINE void HistogramInit(Histogram* const p, + int palette_code_bits) { + p->palette_code_bits_ = palette_code_bits; + HistogramClear(p); +} + +// Create the histogram. +// +// The input data is the PixOrCopy data, which models the +// literals, stop codes and backward references (both distances and lengths) +void HistogramBuild(Histogram* const p, + const PixOrCopy* const literal_and_length, + int n_literal_and_length); + +void HistogramAddSinglePixOrCopy(Histogram* const p, const PixOrCopy v); + +// Estimate how many bits the combined entropy of literals and distance +// approximately maps to. +double HistogramEstimateBits(const Histogram* const p); + +// This function estimates the Huffman dictionary + other block overhead +// size for creating a new deflate block. +double HistogramEstimateBitsHeader(const Histogram* const p); + +// This function estimates the cost in bits excluding the bits needed to +// represent the entropy code itself. +double HistogramEstimateBitsBulk(const Histogram* const p); + +static WEBP_INLINE void HistogramAdd(Histogram* const p, + const Histogram* const a) { + int i; + for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) { + p->literal_[i] += a->literal_[i]; + } + for (i = 0; i < DISTANCE_CODES_MAX; ++i) { + p->distance_[i] += a->distance_[i]; + } + for (i = 0; i < 256; ++i) { + p->red_[i] += a->red_[i]; + p->blue_[i] += a->blue_[i]; + p->alpha_[i] += a->alpha_[i]; + } +} + +static WEBP_INLINE void HistogramRemove(Histogram* const p, + const Histogram* const a) { + int i; + for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) { + p->literal_[i] -= a->literal_[i]; + assert(p->literal_[i] >= 0); + } + for (i = 0; i < DISTANCE_CODES_MAX; ++i) { + p->distance_[i] -= a->distance_[i]; + assert(p->distance_[i] >= 0); + } + for (i = 0; i < 256; ++i) { + p->red_[i] -= a->red_[i]; + p->blue_[i] -= a->blue_[i]; + p->alpha_[i] -= a->alpha_[i]; + assert(p->red_[i] >= 0); + assert(p->blue_[i] >= 0); + assert(p->alpha_[i] >= 0); + } +} + +static WEBP_INLINE int HistogramNumPixOrCopyCodes(const Histogram* const p) { + return 256 + kLengthCodes + (1 << p->palette_code_bits_); +} + +void ConvertPopulationCountTableToBitEstimates( + int n, const int* const population_counts, double* const output); + +double ShannonEntropy(const int* const array, int n); + +// Build a 2d image of histograms, subresolutioned by (1 << histobits) to +// the original image. +int BuildHistogramImage(int xsize, int ysize, + int histobits, + int palette_bits, + const PixOrCopy* backward_refs, + int backward_refs_size, + Histogram*** image, + int* histogram_size); + +// Combines several histograms into fewer histograms. +int CombineHistogramImage(Histogram** in, + int in_size, + int quality, + Histogram*** out, + int* out_size); + +// Moves histograms from one cluster to another if smaller entropy can +// be achieved by doing that. +void RefineHistogramImage(Histogram** raw, + int raw_size, + uint32_t* symbols, + int out_size, + Histogram** out); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif // WEBP_ENC_HISTOGRAM_H_ diff --git a/src/utils/huffman_encode.c b/src/utils/huffman_encode.c new file mode 100644 index 00000000..405ead37 --- /dev/null +++ b/src/utils/huffman_encode.c @@ -0,0 +1,313 @@ +// Copyright 2011 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/ +// ----------------------------------------------------------------------------- +// +// Author: jyrki@google.com (Jyrki Alakuijala) +// +// Flate like entropy encoding (Huffman) for webp lossless. + +#include "./huffman_encode.h" + +#include +#include +#include + +typedef struct { + int total_count_; + int value_; + int pool_index_left_; + int pool_index_right_; +} HuffmanTree; + +// Sort the root nodes, most popular first. +static int CompHuffmanTree(const void* vp0, const void* vp1) { + const HuffmanTree* v0 = (const HuffmanTree*)vp0; + const HuffmanTree* v1 = (const HuffmanTree*)vp1; + if (v0->total_count_ > v1->total_count_) { + return -1; + } else if (v0->total_count_ < v1->total_count_) { + return 1; + } else { + if (v0->value_ < v1->value_) { + return -1; + } + if (v0->value_ > v1->value_) { + return 1; + } + return 0; + } +} + +static void SetDepth(const HuffmanTree* p, + HuffmanTree* pool, + uint8_t* depth, + const int level) { + if (p->pool_index_left_ >= 0) { + SetDepth(&pool[p->pool_index_left_], pool, depth, level + 1); + SetDepth(&pool[p->pool_index_right_], pool, depth, level + 1); + } else { + depth[p->value_] = level; + } +} + +// This function will create a Huffman tree. +// +// The catch here is that the tree cannot be arbitrarily deep. +// Deflate specifies a maximum depth of 15 bits for "code trees" +// and 7 bits for "code length code trees." +// +// count_limit is the value that is to be faked as the minimum value +// and this minimum value is raised until the tree matches the +// maximum length requirement. +// +// This algorithm is not of excellent performance for very long data blocks, +// especially when population counts are longer than 2**tree_limit, but +// we are not planning to use this with extremely long blocks. +// +// See http://en.wikipedia.org/wiki/Huffman_coding +int CreateHuffmanTree(const int* const histogram, int histogram_size, + int tree_depth_limit, + uint8_t* const bit_depths) { + HuffmanTree* tree; + HuffmanTree* tree_pool; + int tree_pool_size; + // For block sizes with less than 64k symbols we never need to do a + // second iteration of this loop. + // If we actually start running inside this loop a lot, we would perhaps + // be better off with the Katajainen algorithm. + int count_limit; + for (count_limit = 1; ; count_limit *= 2) { + int tree_size = 0; + int i; + for (i = 0; i < histogram_size; ++i) { + if (histogram[i]) { + ++tree_size; + } + } + // 3 * tree_size is enough to cover all the nodes representing a + // population and all the inserted nodes combining two existing nodes. + // The tree pool needs 2 * (tree_size - 1) entities, and the + // tree needs exactly tree_size entities. + tree = (HuffmanTree*)malloc(3 * tree_size * sizeof(*tree)); + if (tree == NULL) { + return 0; + } + { + int j = 0; + int i; + for (i = 0; i < histogram_size; ++i) { + if (histogram[i]) { + const int count = + (histogram[i] < count_limit) ? count_limit : histogram[i]; + tree[j].total_count_ = count; + tree[j].value_ = i; + tree[j].pool_index_left_ = -1; + tree[j].pool_index_right_ = -1; + ++j; + } + } + } + qsort((void*)tree, tree_size, sizeof(*tree), CompHuffmanTree); + tree_pool = tree + tree_size; + tree_pool_size = 0; + if (tree_size >= 2) { + while (tree_size >= 2) { // Finish when we have only one root. + int count; + tree_pool[tree_pool_size] = tree[tree_size - 1]; + ++tree_pool_size; + tree_pool[tree_pool_size] = tree[tree_size - 2]; + ++tree_pool_size; + count = + tree_pool[tree_pool_size - 1].total_count_ + + tree_pool[tree_pool_size - 2].total_count_; + tree_size -= 2; + { + int k = 0; + // Search for the insertion point. + for (k = 0; k < tree_size; ++k) { + if (tree[k].total_count_ <= count) { + break; + } + } + memmove(tree + (k + 1), tree + k, (tree_size - k) * sizeof(*tree)); + tree[k].total_count_ = count; + tree[k].value_ = -1; + + tree[k].pool_index_left_ = tree_pool_size - 1; + tree[k].pool_index_right_ = tree_pool_size - 2; + tree_size = tree_size + 1; + } + } + SetDepth(&tree[0], tree_pool, bit_depths, 0); + } else { + if (tree_size == 1) { + // Only one element. + bit_depths[tree[0].value_] = 1; + } + } + free(tree); + // We need to pack the Huffman tree in tree_depth_limit bits. + // If this was not successful, add fake entities to the lowest values + // and retry. + { + int max_depth = bit_depths[0]; + int j; + for (j = 1; j < histogram_size; ++j) { + if (max_depth < bit_depths[j]) { + max_depth = bit_depths[j]; + } + } + if (max_depth <= tree_depth_limit) { + break; + } + } + } + return 1; +} + +static void WriteHuffmanTreeRepetitions( + const int value, + const int prev_value, + int repetitions, + int* num_symbols, + uint8_t* tree, + uint8_t* extra_bits_data) { + if (value != prev_value) { + tree[*num_symbols] = value; + extra_bits_data[*num_symbols] = 0; + ++(*num_symbols); + --repetitions; + } + while (repetitions >= 1) { + if (repetitions < 3) { + int i; + for (i = 0; i < repetitions; ++i) { + tree[*num_symbols] = value; + extra_bits_data[*num_symbols] = 0; + ++(*num_symbols); + } + return; + } else if (repetitions < 7) { + // 3 to 6 left + tree[*num_symbols] = 16; + extra_bits_data[*num_symbols] = repetitions - 3; + ++(*num_symbols); + return; + } else { + tree[*num_symbols] = 16; + extra_bits_data[*num_symbols] = 3; + ++(*num_symbols); + repetitions -= 6; + } + } +} + +static void WriteHuffmanTreeRepetitionsZeros( + const int value, + int repetitions, + int* num_symbols, + uint8_t* tree, + uint8_t* extra_bits_data) { + while (repetitions >= 1) { + if (repetitions < 3) { + int i; + for (i = 0; i < repetitions; ++i) { + tree[*num_symbols] = value; + extra_bits_data[*num_symbols] = 0; + ++(*num_symbols); + } + return; + } else if (repetitions < 11) { + tree[*num_symbols] = 17; + extra_bits_data[*num_symbols] = repetitions - 3; + ++(*num_symbols); + return; + } else if (repetitions < 139) { + tree[*num_symbols] = 18; + extra_bits_data[*num_symbols] = repetitions - 11; + ++(*num_symbols); + return; + } else { + tree[*num_symbols] = 18; + extra_bits_data[*num_symbols] = 0x7f; // 138 repeated 0s + ++(*num_symbols); + repetitions -= 138; + } + } +} + +void CreateCompressedHuffmanTree(const uint8_t* depth, + int depth_size, + int* num_symbols, + uint8_t* tree, + uint8_t* extra_bits_data) { + int prev_value = 8; // 8 is the initial value for rle. + int i; + for (i = 0; i < depth_size;) { + const int value = depth[i]; + int reps = 1; + int k; + for (k = i + 1; k < depth_size && depth[k] == value; ++k) { + ++reps; + } + if (value == 0) { + WriteHuffmanTreeRepetitionsZeros(value, reps, + num_symbols, + tree, extra_bits_data); + } else { + WriteHuffmanTreeRepetitions(value, prev_value, reps, + num_symbols, + tree, extra_bits_data); + prev_value = value; + } + i += reps; + } +} + +static uint32_t ReverseBits(int num_bits, uint32_t bits) { + uint32_t retval = 0; + int i; + for (i = 0; i < num_bits; ++i) { + retval <<= 1; + retval |= bits & 1; + bits >>= 1; + } + return retval; +} + +void ConvertBitDepthsToSymbols(const uint8_t* depth, int len, + uint16_t* bits) { + // This function is based on RFC 1951. + // + // In deflate, all bit depths are [1..15] + // 0 bit depth means that the symbol does not exist. + + // 0..15 are values for bits +#define MAX_BITS 16 + uint32_t next_code[MAX_BITS]; + uint32_t bl_count[MAX_BITS] = { 0 }; + int i; + { + for (i = 0; i < len; ++i) { + ++bl_count[depth[i]]; + } + bl_count[0] = 0; + } + next_code[0] = 0; + { + int code = 0; + int bits; + for (bits = 1; bits < MAX_BITS; ++bits) { + code = (code + bl_count[bits - 1]) << 1; + next_code[bits] = code; + } + } + for (i = 0; i < len; ++i) { + if (depth[i]) { + bits[i] = ReverseBits(depth[i], next_code[depth[i]]++); + } + } +} diff --git a/src/utils/huffman_encode.h b/src/utils/huffman_encode.h new file mode 100644 index 00000000..7999a61f --- /dev/null +++ b/src/utils/huffman_encode.h @@ -0,0 +1,52 @@ +// Copyright 2011 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/ +// ----------------------------------------------------------------------------- +// +// Author: jyrki@google.com (Jyrki Alakuijala) +// +// Flate like entropy encoding (Huffman) for webp lossless + +#ifndef WEBP_UTILS_ENTROPY_ENCODE_H_ +#define WEBP_UTILS_ENTROPY_ENCODE_H_ + +#include + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +// This function will create a Huffman tree. +// +// The (data,length) contains the population counts. +// The tree_limit is the maximum bit depth of the Huffman codes. +// +// The depth contains the tree, i.e., how many bits are used for +// the symbol. +// +// See http://en.wikipedia.org/wiki/Huffman_coding +// +// Returns 0 when an error has occured. +int CreateHuffmanTree(const int* data, + const int length, + const int tree_limit, + uint8_t* depth); + +// Write a huffman tree from bit depths into the deflate representation +// of a Huffman tree. In deflate, the generated Huffman tree is to be +// compressed once more using a Huffman tree. +void CreateCompressedHuffmanTree(const uint8_t* depth, int len, + int* num_symbols, + uint8_t* tree, + uint8_t* extra_bits_data); + +// Get the actual bit values for a tree of bit depths. +void ConvertBitDepthsToSymbols(const uint8_t* depth, int len, uint16_t* bits); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif // WEBP_UTILS_ENTROPY_ENCODE_H_ From fcba7be2d3e312d616bc96292b84d6ac47d7bd7a Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Wed, 4 Apr 2012 08:24:29 +0000 Subject: [PATCH 04/46] Fixed header file tag (WEBP_UTILS_HUFFMAN_ENCODE_H_) Change-Id: I7ccd00361b1b0347639b05ee494e8e701c95cfe3 --- src/utils/huffman_encode.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/huffman_encode.h b/src/utils/huffman_encode.h index 7999a61f..fa484b7a 100644 --- a/src/utils/huffman_encode.h +++ b/src/utils/huffman_encode.h @@ -9,8 +9,8 @@ // // Flate like entropy encoding (Huffman) for webp lossless -#ifndef WEBP_UTILS_ENTROPY_ENCODE_H_ -#define WEBP_UTILS_ENTROPY_ENCODE_H_ +#ifndef WEBP_UTILS_HUFFMAN_ENCODE_H_ +#define WEBP_UTILS_HUFFMAN_ENCODE_H_ #include @@ -49,4 +49,4 @@ void ConvertBitDepthsToSymbols(const uint8_t* depth, int len, uint16_t* bits); } #endif -#endif // WEBP_UTILS_ENTROPY_ENCODE_H_ +#endif // WEBP_UTILS_HUFFMAN_ENCODE_H_ From 32714ce3be8e8f2537e677c100c53b8787cfd2ce Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Tue, 10 Apr 2012 03:56:07 +0000 Subject: [PATCH 05/46] Add VP8L prefix to backward ref & histogram methods. Change-Id: I8c14fb219a1d7830d3244aa780c91c9964867330 --- src/enc/backward_references.c | 98 +++++++++++++++---------------- src/enc/backward_references.h | 28 ++++----- src/enc/histogram.c | 106 ++++++++++++++++------------------ src/enc/histogram.h | 69 +++++++++++----------- 4 files changed, 147 insertions(+), 154 deletions(-) diff --git a/src/enc/backward_references.c b/src/enc/backward_references.c index 2561472c..7ed673be 100644 --- a/src/enc/backward_references.c +++ b/src/enc/backward_references.c @@ -32,7 +32,7 @@ static const uint8_t plane_to_code_lut[128] = { static const int kMinLength = 2; -int DistanceToPlaneCode(int xsize, int dist) { +int VP8LDistanceToPlaneCode(int xsize, int dist) { int yoffset = dist / xsize; int xoffset = dist - yoffset * xsize; if (xoffset <= 8 && yoffset < 8) { @@ -193,8 +193,8 @@ static WEBP_INLINE void PushBackCopy(int length, } } -void BackwardReferencesRle(int xsize, int ysize, const uint32_t* argb, - PixOrCopy* stream, int* stream_size) { +void VP8LBackwardReferencesRle(int xsize, int ysize, const uint32_t* argb, + PixOrCopy* stream, int* stream_size) { const int pix_count = xsize * ysize; int streak = 0; int i; @@ -213,10 +213,10 @@ void BackwardReferencesRle(int xsize, int ysize, const uint32_t* argb, } // Returns 1 when successful. -int BackwardReferencesHashChain(int xsize, int ysize, int use_palette, - const uint32_t* argb, int palette_bits, - int quality, - PixOrCopy* stream, int* stream_size) { +int VP8LBackwardReferencesHashChain(int xsize, int ysize, int use_palette, + const uint32_t* argb, int palette_bits, + int quality, PixOrCopy* stream, + int* stream_size) { const int pix_count = xsize * ysize; int i; int ok = 0; @@ -320,7 +320,7 @@ static int CostModel_Build(CostModel* p, int xsize, int ysize, const uint32_t* argb, int palette_bits) { int ok = 0; int stream_size; - Histogram histo; + VP8LHistogram histo; int i; PixOrCopy* stream = (PixOrCopy*)malloc(xsize * ysize * sizeof(*stream)); if (stream == NULL) { @@ -328,34 +328,33 @@ static int CostModel_Build(CostModel* p, int xsize, int ysize, } p->palette_bits_ = palette_bits; if (recursion_level > 0) { - if (!BackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1, - use_palette, argb, - palette_bits, - &stream[0], &stream_size)) { + if (!VP8LBackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1, + use_palette, argb, palette_bits, + &stream[0], &stream_size)) { goto Error; } } else { const int quality = 100; - if (!BackwardReferencesHashChain(xsize, ysize, use_palette, argb, - palette_bits, quality, - &stream[0], &stream_size)) { + if (!VP8LBackwardReferencesHashChain(xsize, ysize, use_palette, argb, + palette_bits, quality, + &stream[0], &stream_size)) { goto Error; } } - HistogramInit(&histo, palette_bits); + VP8LHistogramInit(&histo, palette_bits); for (i = 0; i < stream_size; ++i) { - HistogramAddSinglePixOrCopy(&histo, stream[i]); + VP8LHistogramAddSinglePixOrCopy(&histo, stream[i]); } - ConvertPopulationCountTableToBitEstimates( - HistogramNumPixOrCopyCodes(&histo), + VP8LConvertPopulationCountTableToBitEstimates( + VP8LHistogramNumCodes(&histo), &histo.literal_[0], &p->literal_[0]); - ConvertPopulationCountTableToBitEstimates( + VP8LConvertPopulationCountTableToBitEstimates( VALUES_IN_BYTE, &histo.red_[0], &p->red_[0]); - ConvertPopulationCountTableToBitEstimates( + VP8LConvertPopulationCountTableToBitEstimates( VALUES_IN_BYTE, &histo.blue_[0], &p->blue_[0]); - ConvertPopulationCountTableToBitEstimates( + VP8LConvertPopulationCountTableToBitEstimates( VALUES_IN_BYTE, &histo.alpha_[0], &p->alpha_[0]); - ConvertPopulationCountTableToBitEstimates( + VP8LConvertPopulationCountTableToBitEstimates( DISTANCE_CODES_MAX, &histo.distance_[0], &p->distance_[0]); ok = 1; Error: @@ -440,7 +439,7 @@ static int BackwardReferencesHashChainDistanceOnly( &offset, &len); } if (len >= kMinLength) { - const int code = DistanceToPlaneCode(xsize, offset); + const int code = VP8LDistanceToPlaneCode(xsize, offset); const double distance_cost = prev_cost + CostModel_DistanceCost(cost_model, code); int k; @@ -601,13 +600,13 @@ Error: } // Returns 1 on success. -int BackwardReferencesTraceBackwards(int xsize, int ysize, - int recursive_cost_model, - int use_palette, - const uint32_t* argb, - int palette_bits, - PixOrCopy* stream, - int* stream_size) { +int VP8LBackwardReferencesTraceBackwards(int xsize, int ysize, + int recursive_cost_model, + int use_palette, + const uint32_t* argb, + int palette_bits, + PixOrCopy* stream, + int* stream_size) { int ok = 0; const int dist_array_size = xsize * ysize; uint32_t* chosen_path = NULL; @@ -638,21 +637,22 @@ Error: return ok; } -void BackwardReferences2DLocality(int xsize, int data_size, PixOrCopy* data) { +void VP8LBackwardReferences2DLocality(int xsize, int data_size, + PixOrCopy* data) { int i; for (i = 0; i < data_size; ++i) { if (PixOrCopyIsCopy(&data[i])) { int dist = data[i].argb_or_offset; - int transformed_dist = DistanceToPlaneCode(xsize, dist); + int transformed_dist = VP8LDistanceToPlaneCode(xsize, dist); data[i].argb_or_offset = transformed_dist; } } } -int VerifyBackwardReferences(const uint32_t* argb, int xsize, int ysize, - int palette_bits, - const PixOrCopy* lit, - int lit_size) { +int VP8LVerifyBackwardReferences(const uint32_t* argb, int xsize, int ysize, + int palette_bits, + const PixOrCopy* lit, + int lit_size) { int num_pixels = 0; int i; VP8LColorCache hashers; @@ -717,7 +717,7 @@ int VerifyBackwardReferences(const uint32_t* argb, int xsize, int ysize, // Returns 1 on success. static int ComputePaletteHistogram(const uint32_t* argb, int xsize, int ysize, PixOrCopy* stream, int stream_size, - int palette_bits, Histogram* histo) { + int palette_bits, VP8LHistogram* histo) { int pixel_index = 0; int i; uint32_t k; @@ -732,12 +732,12 @@ static int ComputePaletteHistogram(const uint32_t* argb, int xsize, int ysize, VP8LColorCacheContains(&hashers, argb[pixel_index])) { // push pixel as a palette pixel const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]); - HistogramAddSinglePixOrCopy(histo, PixOrCopyCreatePaletteIx(ix)); + VP8LHistogramAddSinglePixOrCopy(histo, PixOrCopyCreatePaletteIx(ix)); } else { - HistogramAddSinglePixOrCopy(histo, v); + VP8LHistogramAddSinglePixOrCopy(histo, v); } } else { - HistogramAddSinglePixOrCopy(histo, v); + VP8LHistogramAddSinglePixOrCopy(histo, v); } for (k = 0; k < PixOrCopyLength(&v); ++k) { VP8LColorCacheInsert(&hashers, argb[pixel_index]); @@ -752,9 +752,9 @@ static int ComputePaletteHistogram(const uint32_t* argb, int xsize, int ysize, } // Returns how many bits are to be used for a palette. -int CalculateEstimateForPaletteSize(const uint32_t* argb, - int xsize, int ysize, - int* best_palette_bits) { +int VP8LCalculateEstimateForPaletteSize(const uint32_t* argb, + int xsize, int ysize, + int* best_palette_bits) { int ok = 0; int palette_bits; double lowest_entropy = 1e99; @@ -763,17 +763,17 @@ int CalculateEstimateForPaletteSize(const uint32_t* argb, static const double kSmallPenaltyForLargePalette = 4.0; static const int quality = 30; if (stream == NULL || - !BackwardReferencesHashChain(xsize, ysize, - 0, argb, 0, quality, stream, &stream_size)) { + !VP8LBackwardReferencesHashChain(xsize, ysize, 0, argb, 0, quality, + stream, &stream_size)) { goto Error; } for (palette_bits = 0; palette_bits < 12; ++palette_bits) { double cur_entropy; - Histogram histo; - HistogramInit(&histo, palette_bits); + VP8LHistogram histo; + VP8LHistogramInit(&histo, palette_bits); ComputePaletteHistogram(argb, xsize, ysize, &stream[0], stream_size, palette_bits, &histo); - cur_entropy = HistogramEstimateBits(&histo) + + cur_entropy = VP8LHistogramEstimateBits(&histo) + kSmallPenaltyForLargePalette * palette_bits; if (palette_bits == 0 || cur_entropy < lowest_entropy) { *best_palette_bits = palette_bits; diff --git a/src/enc/backward_references.h b/src/enc/backward_references.h index b9a91894..b8d05e6b 100644 --- a/src/enc/backward_references.h +++ b/src/enc/backward_references.h @@ -173,7 +173,7 @@ static WEBP_INLINE void PixOrCopyLengthCodeAndBits( // Ridiculously simple backward references for images where it is unlikely // that there are large backward references (photos). -void BackwardReferencesRle( +void VP8LBackwardReferencesRle( int xsize, int ysize, const uint32_t *argb, @@ -182,7 +182,7 @@ void BackwardReferencesRle( // This is a simple fast function for obtaining backward references // based on simple heuristics. Returns 1 on success. -int BackwardReferencesHashChain( +int VP8LBackwardReferencesHashChain( int xsize, int ysize, int use_palette, @@ -195,7 +195,7 @@ int BackwardReferencesHashChain( // This method looks for a shortest path through the backward reference // network based on a cost model generated by a first round of compression. // Returns 1 on success. -int BackwardReferencesTraceBackwards( +int VP8LBackwardReferencesTraceBackwards( int xsize, int ysize, int recursive_cost_model, @@ -208,24 +208,24 @@ int BackwardReferencesTraceBackwards( // Convert backward references that are of linear distance along // the image scan lines to have a 2d locality indexing where // smaller values are used for backward references that are close by. -void BackwardReferences2DLocality(int xsize, int data_size, - PixOrCopy *data); +void VP8LBackwardReferences2DLocality(int xsize, int data_size, + PixOrCopy *data); // Internals of locality transform exposed for testing use. -int DistanceToPlaneCode(int xsize, int distance); +int VP8LDistanceToPlaneCode(int xsize, int distance); // Returns true if the given backward references actually produce // the image given in tuple (argb, xsize, ysize). -int VerifyBackwardReferences(const uint32_t* argb, - int xsize, int ysize, - int palette_bits, - const PixOrCopy *lit, - int lit_size); +int VP8LVerifyBackwardReferences(const uint32_t* argb, + int xsize, int ysize, + int palette_bits, + const PixOrCopy *lit, + int lit_size); // Produce an estimate for a good emerging palette size for the image. -int CalculateEstimateForPaletteSize(const uint32_t *argb, - int xsize, int ysize, - int *best_palette_bits); +int VP8LCalculateEstimateForPaletteSize(const uint32_t *argb, + int xsize, int ysize, + int *best_palette_bits); #if defined(__cplusplus) || defined(c_plusplus) } diff --git a/src/enc/histogram.c b/src/enc/histogram.c index 528a6452..9d239367 100644 --- a/src/enc/histogram.c +++ b/src/enc/histogram.c @@ -116,7 +116,7 @@ static WEBP_INLINE double FastLog(int v) { return log(v); } -void ConvertPopulationCountTableToBitEstimates( +void VP8LConvertPopulationCountTableToBitEstimates( int num_symbols, const int* const population_counts, double* const output) { @@ -145,7 +145,8 @@ void ConvertPopulationCountTableToBitEstimates( } } -void HistogramAddSinglePixOrCopy(Histogram* const p, const PixOrCopy v) { +void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const p, + const PixOrCopy v) { if (PixOrCopyIsLiteral(&v)) { ++p->alpha_[PixOrCopyLiteral(&v, 3)]; ++p->red_[PixOrCopyLiteral(&v, 2)]; @@ -165,17 +166,17 @@ void HistogramAddSinglePixOrCopy(Histogram* const p, const PixOrCopy v) { } } -void HistogramBuild(Histogram* const p, - const PixOrCopy* const literal_and_length, - int n_literal_and_length) { +void VP8LHistogramCreate(VP8LHistogram* const p, + const PixOrCopy* const literal_and_length, + int n_literal_and_length) { int i; - HistogramClear(p); + VP8LHistogramClear(p); for (i = 0; i < n_literal_and_length; ++i) { - HistogramAddSinglePixOrCopy(p, literal_and_length[i]); + VP8LHistogramAddSinglePixOrCopy(p, literal_and_length[i]); } } -double ShannonEntropy(const int* const array, int n) { +double VP8LShannonEntropy(const int* const array, int n) { int i; double retval = 0; int sum = 0; @@ -208,7 +209,7 @@ static double BitsEntropy(const int* const array, int n) { } } retval -= sum * FastLog(sum); - retval *= -1.4426950408889634; // 1.0 / -FastLog(2); + retval *= -1.4426950408889634; // 1.0 / -Log(2); mix = 0.627; if (nonzeros < 5) { if (nonzeros <= 1) { @@ -240,8 +241,8 @@ static double BitsEntropy(const int* const array, int n) { return retval; } -double HistogramEstimateBitsBulk(const Histogram* const p) { - double retval = BitsEntropy(&p->literal_[0], HistogramNumPixOrCopyCodes(p)) + +double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) { + double retval = BitsEntropy(&p->literal_[0], VP8LHistogramNumCodes(p)) + BitsEntropy(&p->red_[0], 256) + BitsEntropy(&p->blue_[0], 256) + BitsEntropy(&p->alpha_[0], 256) + @@ -258,8 +259,8 @@ double HistogramEstimateBitsBulk(const Histogram* const p) { return retval; } -double HistogramEstimateBits(const Histogram* const p) { - return HistogramEstimateBitsHeader(p) + HistogramEstimateBitsBulk(p); +double VP8LHistogramEstimateBits(const VP8LHistogram* const p) { + return VP8LHistogramEstimateBitsHeader(p) + VP8LHistogramEstimateBitsBulk(p); } // Returns the cost encode the rle-encoded entropy code. @@ -301,35 +302,33 @@ static double HuffmanCost(const int* const population, int length) { return retval; } -double HistogramEstimateBitsHeader(const Histogram* const p) { +double VP8LHistogramEstimateBitsHeader(const VP8LHistogram* const p) { return HuffmanCost(&p->alpha_[0], 256) + HuffmanCost(&p->red_[0], 256) + - HuffmanCost(&p->literal_[0], HistogramNumPixOrCopyCodes(p)) + + HuffmanCost(&p->literal_[0], VP8LHistogramNumCodes(p)) + HuffmanCost(&p->blue_[0], 256) + HuffmanCost(&p->distance_[0], DISTANCE_CODES_MAX); } -int BuildHistogramImage(int xsize, int ysize, - int histobits, - int palettebits, - const PixOrCopy* backward_refs, - int backward_refs_size, - Histogram*** image_arg, - int* image_size) { +int VP8LHistogramBuildImage(int xsize, int ysize, + int histobits, int palettebits, + const PixOrCopy* backward_refs, + int backward_refs_size, + VP8LHistogram*** image_arg, int* image_size) { int histo_xsize = histobits ? (xsize + (1 << histobits) - 1) >> histobits : 1; int histo_ysize = histobits ? (ysize + (1 << histobits) - 1) >> histobits : 1; int i; int x = 0; int y = 0; - Histogram** image; + VP8LHistogram** image; *image_arg = NULL; *image_size = histo_xsize * histo_ysize; - image = (Histogram**)calloc(*image_size, sizeof(*image)); + image = (VP8LHistogram**)calloc(*image_size, sizeof(*image)); if (image == NULL) { return 0; } for (i = 0; i < *image_size; ++i) { - image[i] = (Histogram*)malloc(sizeof(*image[i])); + image[i] = (VP8LHistogram*)malloc(sizeof(*image[i])); if (!image[i]) { int k; for (k = 0; k < *image_size; ++k) { @@ -338,14 +337,14 @@ int BuildHistogramImage(int xsize, int ysize, free(image); return 0; } - HistogramInit(image[i], palettebits); + VP8LHistogramInit(image[i], palettebits); } // x and y trace the position in the image. for (i = 0; i < backward_refs_size; ++i) { const PixOrCopy v = backward_refs[i]; const int ix = histobits ? (y >> histobits) * histo_xsize + (x >> histobits) : 0; - HistogramAddSinglePixOrCopy(image[ix], v); + VP8LHistogramAddSinglePixOrCopy(image[ix], v); x += PixOrCopyLength(&v); while (x >= xsize) { x -= xsize; @@ -356,11 +355,8 @@ int BuildHistogramImage(int xsize, int ysize, return 1; } -int CombineHistogramImage(Histogram** in, - int in_size, - int quality, - Histogram*** out_arg, - int* out_size) { +int VP8LHistogramCombine(VP8LHistogram** in, int in_size, int quality, + VP8LHistogram*** out_arg, int* out_size) { int ok = 0; int i; unsigned int seed = 0; @@ -368,7 +364,7 @@ int CombineHistogramImage(Histogram** in, int inner_iters = 10 + quality / 2; int iter; double* bit_costs = (double*)malloc(in_size * sizeof(*bit_costs)); - Histogram** out = (Histogram**)calloc(in_size, sizeof(*out)); + VP8LHistogram** out = (VP8LHistogram**)calloc(in_size, sizeof(*out)); *out_arg = out; *out_size = in_size; if (bit_costs == NULL || out == NULL) { @@ -376,13 +372,13 @@ int CombineHistogramImage(Histogram** in, } // Copy for (i = 0; i < in_size; ++i) { - Histogram* new_histo = (Histogram*)malloc(sizeof(*new_histo)); + VP8LHistogram* new_histo = (VP8LHistogram*)malloc(sizeof(*new_histo)); if (new_histo == NULL) { goto Error; } *new_histo = *(in[i]); out[i] = new_histo; - bit_costs[i] = HistogramEstimateBits(out[i]); + bit_costs[i] = VP8LHistogramEstimateBits(out[i]); } // Collapse similar histograms. for (iter = 0; iter < in_size * 3 && *out_size >= 2; ++iter) { @@ -394,7 +390,7 @@ int CombineHistogramImage(Histogram** in, for (k = 0; k < inner_iters; ++k) { // Choose two, build a combo out of them. double cost_val; - Histogram* combo; + VP8LHistogram* combo; int ix0 = rand_r(&seed) % *out_size; int ix1; int diff = ((k & 7) + 1) % (*out_size - 1); @@ -405,13 +401,14 @@ int CombineHistogramImage(Histogram** in, if (ix0 == ix1) { continue; } - combo = (Histogram*)malloc(sizeof(*combo)); + combo = (VP8LHistogram*)malloc(sizeof(*combo)); if (combo == NULL) { goto Error; } *combo = *out[ix0]; - HistogramAdd(combo, out[ix1]); - cost_val = HistogramEstimateBits(combo) - bit_costs[ix0] - bit_costs[ix1]; + VP8LHistogramAdd(combo, out[ix1]); + cost_val = + VP8LHistogramEstimateBits(combo) - bit_costs[ix0] - bit_costs[ix1]; if (best_val > cost_val) { best_val = cost_val; best_ix0 = ix0; @@ -420,7 +417,7 @@ int CombineHistogramImage(Histogram** in, free(combo); } if (best_val < 0.0) { - HistogramAdd(out[best_ix0], out[best_ix1]); + VP8LHistogramAdd(out[best_ix0], out[best_ix1]); bit_costs[best_ix0] = best_val + bit_costs[best_ix0] + bit_costs[best_ix1]; // Erase (*out)[best_ix1] @@ -453,42 +450,39 @@ Error: // What is the bit cost of moving square_histogram from // cur_symbol to candidate_symbol. -static double HistogramDistance(const Histogram* const square_histogram, +static double HistogramDistance(const VP8LHistogram* const square_histogram, int cur_symbol, int candidate_symbol, - Histogram** candidate_histograms) { + VP8LHistogram** candidate_histograms) { double new_bit_cost; double previous_bit_cost; - Histogram modified; + VP8LHistogram modified; if (cur_symbol == candidate_symbol) { return 0; // Going nowhere. No savings. } previous_bit_cost = - HistogramEstimateBits(candidate_histograms[candidate_symbol]); + VP8LHistogramEstimateBits(candidate_histograms[candidate_symbol]); if (cur_symbol != -1) { previous_bit_cost += - HistogramEstimateBits(candidate_histograms[cur_symbol]); + VP8LHistogramEstimateBits(candidate_histograms[cur_symbol]); } // Compute the bit cost of the histogram where the data moves to. modified = *candidate_histograms[candidate_symbol]; - HistogramAdd(&modified, square_histogram); - new_bit_cost = HistogramEstimateBits(&modified); + VP8LHistogramAdd(&modified, square_histogram); + new_bit_cost = VP8LHistogramEstimateBits(&modified); // Compute the bit cost of the histogram where the data moves away. if (cur_symbol != -1) { modified = *candidate_histograms[cur_symbol]; - HistogramRemove(&modified, square_histogram); - new_bit_cost += HistogramEstimateBits(&modified); + VP8LHistogramRemove(&modified, square_histogram); + new_bit_cost += VP8LHistogramEstimateBits(&modified); } return new_bit_cost - previous_bit_cost; } -void RefineHistogramImage(Histogram** raw, - int raw_size, - uint32_t* symbols, - int out_size, - Histogram** out) { +void VP8LHistogramRefine(VP8LHistogram** raw, int raw_size, + uint32_t* symbols, int out_size, VP8LHistogram** out) { int i; // Find the best 'out' histogram for each of the raw histograms for (i = 0; i < raw_size; ++i) { @@ -507,9 +501,9 @@ void RefineHistogramImage(Histogram** raw, // Recompute each out based on raw and symbols. for (i = 0; i < out_size; ++i) { - HistogramClear(out[i]); + VP8LHistogramClear(out[i]); } for (i = 0; i < raw_size; ++i) { - HistogramAdd(out[symbols[i]], raw[i]); + VP8LHistogramAdd(out[symbols[i]], raw[i]); } } diff --git a/src/enc/histogram.h b/src/enc/histogram.h index 38af5c36..339f0d22 100644 --- a/src/enc/histogram.h +++ b/src/enc/histogram.h @@ -36,9 +36,9 @@ typedef struct { // Backward reference prefix-code histogram. int distance_[DISTANCE_CODES_MAX]; int palette_code_bits_; -} Histogram; +} VP8LHistogram; -static WEBP_INLINE void HistogramClear(Histogram* const p) { +static WEBP_INLINE void VP8LHistogramClear(VP8LHistogram* const p) { memset(&p->literal_[0], 0, sizeof(p->literal_)); memset(&p->red_[0], 0, sizeof(p->red_)); memset(&p->blue_[0], 0, sizeof(p->blue_)); @@ -46,36 +46,36 @@ static WEBP_INLINE void HistogramClear(Histogram* const p) { memset(&p->distance_[0], 0, sizeof(p->distance_)); } -static WEBP_INLINE void HistogramInit(Histogram* const p, +static WEBP_INLINE void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits) { p->palette_code_bits_ = palette_code_bits; - HistogramClear(p); + VP8LHistogramClear(p); } // Create the histogram. // // The input data is the PixOrCopy data, which models the // literals, stop codes and backward references (both distances and lengths) -void HistogramBuild(Histogram* const p, - const PixOrCopy* const literal_and_length, - int n_literal_and_length); +void VP8LHistogramCreate(VP8LHistogram* const p, + const PixOrCopy* const literal_and_length, + int n_literal_and_length); -void HistogramAddSinglePixOrCopy(Histogram* const p, const PixOrCopy v); +void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const p, const PixOrCopy v); // Estimate how many bits the combined entropy of literals and distance // approximately maps to. -double HistogramEstimateBits(const Histogram* const p); +double VP8LHistogramEstimateBits(const VP8LHistogram* const p); // This function estimates the Huffman dictionary + other block overhead // size for creating a new deflate block. -double HistogramEstimateBitsHeader(const Histogram* const p); +double VP8LHistogramEstimateBitsHeader(const VP8LHistogram* const p); // This function estimates the cost in bits excluding the bits needed to // represent the entropy code itself. -double HistogramEstimateBitsBulk(const Histogram* const p); +double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p); -static WEBP_INLINE void HistogramAdd(Histogram* const p, - const Histogram* const a) { +static WEBP_INLINE void VP8LHistogramAdd(VP8LHistogram* const p, + const VP8LHistogram* const a) { int i; for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) { p->literal_[i] += a->literal_[i]; @@ -90,8 +90,8 @@ static WEBP_INLINE void HistogramAdd(Histogram* const p, } } -static WEBP_INLINE void HistogramRemove(Histogram* const p, - const Histogram* const a) { +static WEBP_INLINE void VP8LHistogramRemove(VP8LHistogram* const p, + const VP8LHistogram* const a) { int i; for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) { p->literal_[i] -= a->literal_[i]; @@ -111,39 +111,38 @@ static WEBP_INLINE void HistogramRemove(Histogram* const p, } } -static WEBP_INLINE int HistogramNumPixOrCopyCodes(const Histogram* const p) { +static WEBP_INLINE int VP8LHistogramNumCodes(const VP8LHistogram* const p) { return 256 + kLengthCodes + (1 << p->palette_code_bits_); } -void ConvertPopulationCountTableToBitEstimates( +void VP8LConvertPopulationCountTableToBitEstimates( int n, const int* const population_counts, double* const output); -double ShannonEntropy(const int* const array, int n); +double VP8LShannonEntropy(const int* const array, int n); // Build a 2d image of histograms, subresolutioned by (1 << histobits) to // the original image. -int BuildHistogramImage(int xsize, int ysize, - int histobits, - int palette_bits, - const PixOrCopy* backward_refs, - int backward_refs_size, - Histogram*** image, - int* histogram_size); +int VP8LHistogramBuildImage(int xsize, int ysize, + int histobits, int palette_bits, + const PixOrCopy* backward_refs, + int backward_refs_size, + VP8LHistogram*** image, + int* histogram_size); // Combines several histograms into fewer histograms. -int CombineHistogramImage(Histogram** in, - int in_size, - int quality, - Histogram*** out, - int* out_size); +int VP8LHistogramCombine(VP8LHistogram** in, + int in_size, + int quality, + VP8LHistogram*** out, + int* out_size); // Moves histograms from one cluster to another if smaller entropy can // be achieved by doing that. -void RefineHistogramImage(Histogram** raw, - int raw_size, - uint32_t* symbols, - int out_size, - Histogram** out); +void VP8LHistogramRefine(VP8LHistogram** raw, + int raw_size, + uint32_t* symbols, + int out_size, + VP8LHistogram** out); #if defined(__cplusplus) || defined(c_plusplus) } From 648be3939ff9e384a308c5925d59b30707a62836 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Tue, 10 Apr 2012 07:00:36 +0000 Subject: [PATCH 06/46] Added implementation for various lossless functions - VP8LEncAnalyze, EvalAndApplySubtractGreen, ApplyPredictFilter, ApplyCrossColorFilter - Added palette handling and transform buffer management in VP8LEncodeImage() - Add Transforms (subtract Green, Predict, cross_color) to dsp/lossless.c. These are more-or-less copied from src/lossless code. After this Change, will implement the EncodeImageInternal() method. Change-Id: Idf71f803c24b3b5ae3b5079b15e019721784611d --- src/dsp/lossless.c | 689 ++++++++++++++++++++++++++++++++++++++++---- src/dsp/lossless.h | 24 +- src/enc/histogram.c | 121 +------- src/enc/histogram.h | 4 +- src/enc/vp8l.c | 339 ++++++++++++++++++++-- src/enc/vp8li.h | 20 +- 6 files changed, 989 insertions(+), 208 deletions(-) diff --git a/src/dsp/lossless.c b/src/dsp/lossless.c index 84160efd..8c1dff3b 100644 --- a/src/dsp/lossless.c +++ b/src/dsp/lossless.c @@ -15,12 +15,115 @@ extern "C" { #endif +#include #include #include "./lossless.h" #include "../dec/vp8li.h" +#include "../enc/histogram.h" + + +// A lookup table for small values of log(int) to be used in entropy +// computation. +// +// ", ".join(["%.16ff" % x for x in [0.0]+[log(x) for x in range(1, 256)]]) +static const float kLogTable[] = { + 0.0000000000000000f, 0.0000000000000000f, 0.6931471805599453f, + 1.0986122886681098f, 1.3862943611198906f, 1.6094379124341003f, + 1.7917594692280550f, 1.9459101490553132f, 2.0794415416798357f, + 2.1972245773362196f, 2.3025850929940459f, 2.3978952727983707f, + 2.4849066497880004f, 2.5649493574615367f, 2.6390573296152584f, + 2.7080502011022101f, 2.7725887222397811f, 2.8332133440562162f, + 2.8903717578961645f, 2.9444389791664403f, 2.9957322735539909f, + 3.0445224377234230f, 3.0910424533583161f, 3.1354942159291497f, + 3.1780538303479458f, 3.2188758248682006f, 3.2580965380214821f, + 3.2958368660043291f, 3.3322045101752038f, 3.3672958299864741f, + 3.4011973816621555f, 3.4339872044851463f, 3.4657359027997265f, + 3.4965075614664802f, 3.5263605246161616f, 3.5553480614894135f, + 3.5835189384561099f, 3.6109179126442243f, 3.6375861597263857f, + 3.6635616461296463f, 3.6888794541139363f, 3.7135720667043080f, + 3.7376696182833684f, 3.7612001156935624f, 3.7841896339182610f, + 3.8066624897703196f, 3.8286413964890951f, 3.8501476017100584f, + 3.8712010109078911f, 3.8918202981106265f, 3.9120230054281460f, + 3.9318256327243257f, 3.9512437185814275f, 3.9702919135521220f, + 3.9889840465642745f, 4.0073331852324712f, 4.0253516907351496f, + 4.0430512678345503f, 4.0604430105464191f, 4.0775374439057197f, + 4.0943445622221004f, 4.1108738641733114f, 4.1271343850450917f, + 4.1431347263915326f, 4.1588830833596715f, 4.1743872698956368f, + 4.1896547420264252f, 4.2046926193909657f, 4.2195077051761070f, + 4.2341065045972597f, 4.2484952420493594f, 4.2626798770413155f, + 4.2766661190160553f, 4.2904594411483910f, 4.3040650932041702f, + 4.3174881135363101f, 4.3307333402863311f, 4.3438054218536841f, + 4.3567088266895917f, 4.3694478524670215f, 4.3820266346738812f, + 4.3944491546724391f, 4.4067192472642533f, 4.4188406077965983f, + 4.4308167988433134f, 4.4426512564903167f, 4.4543472962535073f, + 4.4659081186545837f, 4.4773368144782069f, 4.4886363697321396f, + 4.4998096703302650f, 4.5108595065168497f, 4.5217885770490405f, + 4.5325994931532563f, 4.5432947822700038f, 4.5538768916005408f, + 4.5643481914678361f, 4.5747109785033828f, 4.5849674786705723f, + 4.5951198501345898f, 4.6051701859880918f, 4.6151205168412597f, + 4.6249728132842707f, 4.6347289882296359f, 4.6443908991413725f, + 4.6539603501575231f, 4.6634390941120669f, 4.6728288344619058f, + 4.6821312271242199f, 4.6913478822291435f, 4.7004803657924166f, + 4.7095302013123339f, 4.7184988712950942f, 4.7273878187123408f, + 4.7361984483944957f, 4.7449321283632502f, 4.7535901911063645f, + 4.7621739347977563f, 4.7706846244656651f, 4.7791234931115296f, + 4.7874917427820458f, 4.7957905455967413f, 4.8040210447332568f, + 4.8121843553724171f, 4.8202815656050371f, 4.8283137373023015f, + 4.8362819069514780f, 4.8441870864585912f, 4.8520302639196169f, + 4.8598124043616719f, 4.8675344504555822f, 4.8751973232011512f, + 4.8828019225863706f, 4.8903491282217537f, 4.8978397999509111f, + 4.9052747784384296f, 4.9126548857360524f, 4.9199809258281251f, + 4.9272536851572051f, 4.9344739331306915f, 4.9416424226093039f, + 4.9487598903781684f, 4.9558270576012609f, 4.9628446302599070f, + 4.9698132995760007f, 4.9767337424205742f, 4.9836066217083363f, + 4.9904325867787360f, 4.9972122737641147f, 5.0039463059454592f, + 5.0106352940962555f, 5.0172798368149243f, 5.0238805208462765f, + 5.0304379213924353f, 5.0369526024136295f, 5.0434251169192468f, + 5.0498560072495371f, 5.0562458053483077f, 5.0625950330269669f, + 5.0689042022202315f, 5.0751738152338266f, 5.0814043649844631f, + 5.0875963352323836f, 5.0937502008067623f, 5.0998664278241987f, + 5.1059454739005803f, 5.1119877883565437f, 5.1179938124167554f, + 5.1239639794032588f, 5.1298987149230735f, 5.1357984370502621f, + 5.1416635565026603f, 5.1474944768134527f, 5.1532915944977793f, + 5.1590552992145291f, 5.1647859739235145f, 5.1704839950381514f, + 5.1761497325738288f, 5.1817835502920850f, 5.1873858058407549f, + 5.1929568508902104f, 5.1984970312658261f, 5.2040066870767951f, + 5.2094861528414214f, 5.2149357576089859f, 5.2203558250783244f, + 5.2257466737132017f, 5.2311086168545868f, 5.2364419628299492f, + 5.2417470150596426f, 5.2470240721604862f, 5.2522734280466299f, + 5.2574953720277815f, 5.2626901889048856f, 5.2678581590633282f, + 5.2729995585637468f, 5.2781146592305168f, 5.2832037287379885f, + 5.2882670306945352f, 5.2933048247244923f, 5.2983173665480363f, + 5.3033049080590757f, 5.3082676974012051f, 5.3132059790417872f, + 5.3181199938442161f, 5.3230099791384085f, 5.3278761687895813f, + 5.3327187932653688f, 5.3375380797013179f, 5.3423342519648109f, + 5.3471075307174685f, 5.3518581334760666f, 5.3565862746720123f, + 5.3612921657094255f, 5.3659760150218512f, 5.3706380281276624f, + 5.3752784076841653f, 5.3798973535404597f, 5.3844950627890888f, + 5.3890717298165010f, 5.3936275463523620f, 5.3981627015177525f, + 5.4026773818722793f, 5.4071717714601188f, 5.4116460518550396f, + 5.4161004022044201f, 5.4205349992722862f, 5.4249500174814029f, + 5.4293456289544411f, 5.4337220035542400f, 5.4380793089231956f, + 5.4424177105217932f, 5.4467373716663099f, 5.4510384535657002f, + 5.4553211153577017f, 5.4595855141441589f, 5.4638318050256105f, + 5.4680601411351315f, 5.4722706736714750f, 5.4764635519315110f, + 5.4806389233419912f, 5.4847969334906548f, 5.4889377261566867f, + 5.4930614433405482f, 5.4971682252932021f, 5.5012582105447274f, + 5.5053315359323625f, 5.5093883366279774f, 5.5134287461649825f, + 5.5174528964647074f, 5.5214609178622460f, 5.5254529391317835f, + 5.5294290875114234f, 5.5333894887275203f, 5.5373342670185366f, + 5.5412635451584258f +}; + +double VP8LFastLog(int v) { + if (v < (int)(sizeof(kLogTable) / sizeof(kLogTable[0]))) { + return kLogTable[v]; + } + return log(v); +} //------------------------------------------------------------------------------ -// Inverse image transforms. +// Image transforms. // In-place sum of each component with mod 256. static WEBP_INLINE void AddPixelsEq(uint32_t* a, uint32_t b) { @@ -101,61 +204,82 @@ static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) { //------------------------------------------------------------------------------ // Predictors -static void Predictor0(uint32_t* src, const uint32_t* top) { +static uint32_t Predictor0(const uint32_t* const src, + const uint32_t* const top) { (void)top; - AddPixelsEq(src, ARGB_BLACK); + (void)src; + return ARGB_BLACK; } -static void Predictor1(uint32_t* src, const uint32_t* top) { +static uint32_t Predictor1(const uint32_t* const src, + const uint32_t* const top) { (void)top; - AddPixelsEq(src, src[-1]); // left + return src[-1]; } -static void Predictor2(uint32_t* src, const uint32_t* top) { - AddPixelsEq(src, top[0]); +static uint32_t Predictor2(const uint32_t* const src, + const uint32_t* const top) { + (void)src; + return top[0]; } -static void Predictor3(uint32_t* src, const uint32_t* top) { - AddPixelsEq(src, top[1]); +static uint32_t Predictor3(const uint32_t* const src, + const uint32_t* const top) { + (void)src; + return top[1]; } -static void Predictor4(uint32_t* src, const uint32_t* top) { - AddPixelsEq(src, top[-1]); +static uint32_t Predictor4(const uint32_t* const src, + const uint32_t* const top) { + (void)src; + return top[-1]; } -static void Predictor5(uint32_t* src, const uint32_t* top) { +static uint32_t Predictor5(const uint32_t* const src, + const uint32_t* const top) { const uint32_t pred = Average3(src[-1], top[0], top[1]); - AddPixelsEq(src, pred); + return pred; } -static void Predictor6(uint32_t* src, const uint32_t* top) { +static uint32_t Predictor6(const uint32_t* const src, + const uint32_t* const top) { const uint32_t pred = Average2(src[-1], top[-1]); - AddPixelsEq(src, pred); + return pred; } -static void Predictor7(uint32_t* src, const uint32_t* top) { +static uint32_t Predictor7(const uint32_t* const src, + const uint32_t* const top) { const uint32_t pred = Average2(src[-1], top[0]); - AddPixelsEq(src, pred); + return pred; } -static void Predictor8(uint32_t* src, const uint32_t* top) { +static uint32_t Predictor8(const uint32_t* const src, + const uint32_t* const top) { const uint32_t pred = Average2(top[-1], top[0]); - AddPixelsEq(src, pred); + (void)src; + return pred; } -static void Predictor9(uint32_t* src, const uint32_t* top) { +static uint32_t Predictor9(const uint32_t* const src, + const uint32_t* const top) { const uint32_t pred = Average2(top[0], top[1]); - AddPixelsEq(src, pred); + (void)src; + return pred; } -static void Predictor10(uint32_t* src, const uint32_t* top) { +static uint32_t Predictor10(const uint32_t* const src, + const uint32_t* const top) { const uint32_t pred = Average4(src[-1], top[-1], top[0], top[1]); - AddPixelsEq(src, pred); + return pred; } -static void Predictor11(uint32_t* src, const uint32_t* top) { +static uint32_t Predictor11(const uint32_t* const src, + const uint32_t* const top) { const uint32_t pred = Select(top[0], src[-1], top[-1]); - AddPixelsEq(src, pred); + return pred; } -static void Predictor12(uint32_t* src, const uint32_t* top) { +static uint32_t Predictor12(const uint32_t* const src, + const uint32_t* const top) { const uint32_t pred = ClampedAddSubtractFull(src[-1], top[0], top[-1]); - AddPixelsEq(src, pred); + return pred; } -static void Predictor13(uint32_t* src, const uint32_t* top) { +static uint32_t Predictor13(const uint32_t* const src, + const uint32_t* const top) { const uint32_t pred = ClampedAddSubtractHalf(src[-1], top[0], top[-1]); - AddPixelsEq(src, pred); + return pred; } -typedef void (*PredictorFunc)(uint32_t* src, const uint32_t* top); +typedef uint32_t (*PredictorFunc)(const uint32_t* const src, + const uint32_t* const top); static const PredictorFunc kPredictors[16] = { Predictor0, Predictor1, Predictor2, Predictor3, Predictor4, Predictor5, Predictor6, Predictor7, @@ -164,15 +288,201 @@ static const PredictorFunc kPredictors[16] = { Predictor0, Predictor0 // <- padding security sentinels }; +// TODO(vikasa): Replace 256 etc with defines. +static double PredictionCostSpatial(const int* counts, + int weight_0, double exp_val) { + const int significant_symbols = 16; + const double exp_decay_factor = 0.6; + double bits = weight_0 * counts[0]; + int i; + for (i = 1; i < significant_symbols; ++i) { + bits += exp_val * (counts[i] + counts[256 - i]); + exp_val *= exp_decay_factor; + } + return -0.1 * bits; +} + +// Compute the Shanon's entropy: Sum(p*log2(p)) +static double ShannonEntropy(const int* const array, int n) { + int i; + double retval = 0; + int sum = 0; + for (i = 0; i < n; ++i) { + if (array[i] != 0) { + sum += array[i]; + retval += array[i] * VP8LFastLog(array[i]); + } + } + retval -= sum * VP8LFastLog(sum); + retval *= -1.4426950408889634; // 1.0 / -FastLog(2); + return retval; +} + +static double PredictionCostSpatialHistogram(int accumulated[4][256], + int tile[4][256]) { + int i; + int k; + int combo[256]; + double retval = 0; + for (i = 0; i < 4; ++i) { + const double exp_val = 0.94; + retval += PredictionCostSpatial(&tile[i][0], 1, exp_val); + retval += ShannonEntropy(&tile[i][0], 256); + for (k = 0; k < 256; ++k) { + combo[k] = accumulated[i][k] + tile[i][k]; + } + retval += ShannonEntropy(&combo[0], 256); + } + return retval; +} + +static int GetBestPredictorForTile(int tile_x, int tile_y, int max_tile_size, + int xsize, int ysize, + int accumulated[4][256], + const uint32_t* const argb) { + const int num_pred_modes = 14; + const int tile_y_offset = tile_y * max_tile_size; + const int tile_x_offset = tile_x * max_tile_size; + double cur_diff; + double best_diff = 1e99; + int best_mode = 0; + int mode; + int all_x_max = tile_x_offset + max_tile_size; + int all_y_max = tile_y_offset + max_tile_size; + int histo[4][256]; + if (all_x_max > xsize) { + all_x_max = xsize; + } + if (all_y_max > ysize) { + all_y_max = ysize; + } + for (mode = 0; mode < num_pred_modes; ++mode) { + int all_y; + const PredictorFunc pred_func = kPredictors[mode]; + memset(&histo[0][0], 0, sizeof(histo)); + for (all_y = tile_y_offset; all_y < all_y_max; ++all_y) { + int all_x; + for (all_x = tile_x_offset; all_x < all_x_max; ++all_x) { + uint32_t predict; + uint32_t predict_diff; + if (all_y == 0) { + if (all_x == 0) { + predict = 0xff000000; + } else { + predict = argb[all_x - 1]; // Top Row: Pick Left Element. + } + } else if (all_x == 0) { + predict = argb[(all_y - 1) * xsize]; // First Col: Pick Top Element. + } else { + const uint32_t* src = argb + all_y * xsize + all_x; + predict = pred_func(src, src - xsize); + } + predict_diff = VP8LSubPixels(argb[all_y * xsize + all_x], predict); + ++histo[0][predict_diff >> 24]; + ++histo[1][((predict_diff >> 16) & 0xff)]; + ++histo[2][((predict_diff >> 8) & 0xff)]; + ++histo[3][(predict_diff & 0xff)]; + } + } + cur_diff = PredictionCostSpatialHistogram(accumulated, histo); + if (cur_diff < best_diff) { + best_diff = cur_diff; + best_mode = mode; + } + } + return best_mode; +} + +static void CopyTileWithPrediction(int xsize, int ysize, + int tile_x, int tile_y, int bits, int mode, + uint32_t* const argb) { + int ymax = 1 << bits; + int xmax = 1 << bits; + int y; + const PredictorFunc pred_func = kPredictors[mode]; + if (ymax > ysize - (tile_y << bits)) { + ymax = ysize - (tile_y << bits); + } + if (xmax > xsize - (tile_x << bits)) { + xmax = xsize - (tile_x << bits); + } + for (y = 0; y < ymax; ++y) { + const int all_y = (tile_y << bits) + y; + int x; + for (x = 0; x < xmax; ++x) { + const int all_x = (tile_x << bits) + x; + const int ix = all_y * xsize + all_x; + uint32_t predict; + if (all_y == 0) { + if (all_x == 0) { + predict = ARGB_BLACK; + } else { + predict = argb[ix - 1]; + } + } else if (all_x == 0) { + predict = argb[ix - xsize]; + } else { + predict = pred_func(argb + ix, argb + ix - xsize); + } + argb[ix] = VP8LSubPixels(argb[ix], predict); + } + } +} + +void VP8LResidualImage(int width, int height, int bits, + uint32_t* const argb, uint32_t* const image) { + const int max_tile_size = 1 << bits; + const int tile_xsize = VP8LSubSampleSize(width, bits); + const int tile_ysize = VP8LSubSampleSize(height, bits); + int tile_y; + int histo[4][256]; + memset(histo, 0, sizeof(histo)); + for (tile_y = 0; tile_y < tile_ysize; ++tile_y) { + const int tile_y_offset = tile_y * max_tile_size; + int tile_x; + for (tile_x = 0; tile_x < tile_xsize; ++tile_x) { + int pred; + int y; + const int tile_x_offset = tile_x * max_tile_size; + int all_x_max = tile_x_offset + max_tile_size; + if (all_x_max > width) { + all_x_max = width; + } + pred = GetBestPredictorForTile(tile_x, tile_y, max_tile_size, + width, height, histo, argb); + image[tile_y * tile_xsize + tile_x] = 0xff000000u | (pred << 8); + CopyTileWithPrediction(width, height, tile_x, tile_y, bits, pred, argb); + for (y = 0; y < max_tile_size; ++y) { + int ix; + int all_x; + int all_y = tile_y_offset + y; + if (all_y >= height) { + break; + } + ix = all_y * width + tile_x_offset; + for (all_x = tile_x_offset; all_x < all_x_max; ++all_x, ++ix) { + const uint32_t a = argb[ix]; + ++histo[0][a >> 24]; + ++histo[1][((a >> 16) & 0xff)]; + ++histo[2][((a >> 8) & 0xff)]; + ++histo[3][(a & 0xff)]; + } + } + } + } +} + // Inverse prediction. static void PredictorInverseTransform(const VP8LTransform* const transform, int y_start, int y_end, uint32_t* data) { const int width = transform->xsize_; if (y_start == 0) { // First Row follows the L (mode=1) mode. int x; - Predictor0(data, NULL); + uint32_t pred = Predictor0(data, NULL); + AddPixelsEq(data, pred); for (x = 1; x < width; ++x) { - Predictor1(data + x, NULL); + pred = Predictor1(data + x, NULL); + AddPixelsEq(data + x, pred); } data += width; ++y_start; @@ -186,20 +496,24 @@ static void PredictorInverseTransform(const VP8LTransform* const transform, transform->data_ + (y >> transform->bits_) * tiles_per_row; while (y < y_end) { + int x; + uint32_t pred; const uint32_t* pred_mode_src = pred_mode_base; PredictorFunc pred_func; - int x; // First pixel follows the T (mode=2) mode. - Predictor2(data, data - width); + pred = Predictor2(data, data - width); + AddPixelsEq(data, pred); // .. the rest: pred_func = kPredictors[((*pred_mode_src++) >> 8) & 0xf]; for (x = 1; x < width; ++x) { + uint32_t pred; if ((x & mask) == 0) { // start of tile. Read predictor function. pred_func = kPredictors[((*pred_mode_src++) >> 8) & 0xf]; } - pred_func(data + x, data + x - width); + pred = pred_func(data + x, data + x - width); + AddPixelsEq(data + x, pred); } data += width; ++y; @@ -210,8 +524,19 @@ static void PredictorInverseTransform(const VP8LTransform* const transform, } } -// Add Green to Blue and Red channels (i.e. perform the inverse transform of -// 'Subtract Green'). +void VP8LSubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixs) { + int i; + for (i = 0; i < num_pixs; ++i) { + const uint32_t argb = argb_data[i]; + const uint32_t green = (argb >> 8) & 0xff; + const uint32_t new_r = (((argb >> 16) & 0xff) - green) & 0xff; + const uint32_t new_b = ((argb & 0xff) - green) & 0xff; + argb_data[i] = (argb & 0xff00ff00) | (new_r << 16) | new_b; + } +} + +// Add green to blue and red channels (i.e. perform the inverse transform of +// 'subtract green'). static void AddGreenToBlueAndRed(const VP8LTransform* const transform, int y_start, int y_end, uint32_t* data) { const int width = transform->xsize_; @@ -233,6 +558,13 @@ typedef struct { int red_to_blue_; } Multipliers; +static WEBP_INLINE void MultipliersClear( + Multipliers* m) { + m->green_to_red_ = 0; + m->green_to_blue_ = 0; + m->red_to_blue_ = 0; +} + static WEBP_INLINE uint32_t ColorTransformDelta(int8_t color_pred, int8_t color) { return (uint32_t)((int)(color_pred) * color) >> 5; @@ -245,19 +577,276 @@ static WEBP_INLINE void ColorCodeToMultipliers(uint32_t color_code, m->red_to_blue_ = (color_code >> 16) & 0xff; } -static WEBP_INLINE void TransformColor(const Multipliers* const m, - uint32_t* const argb) { - const uint32_t green = *argb >> 8; - const uint32_t red = *argb >> 16; - uint32_t new_red = red; - uint32_t new_blue = *argb; +static WEBP_INLINE uint32_t MultipliersToColorCode(Multipliers* const m) { + return + 0xff000000u | + ((uint32_t)(m->red_to_blue_) << 16) | + ((uint32_t)(m->green_to_blue_) << 8) | + m->green_to_red_; +} - new_red += ColorTransformDelta(m->green_to_red_, green); - new_red &= 0xff; - new_blue += ColorTransformDelta(m->green_to_blue_, green); - new_blue += ColorTransformDelta(m->red_to_blue_, new_red); - new_blue &= 0xff; - *argb = (*argb & 0xff00ff00u) | (new_red << 16) | (new_blue); +static WEBP_INLINE uint32_t TransformColor(const Multipliers* const m, + uint32_t argb, int inverse) { + const uint32_t green = argb >> 8; + const uint32_t red = argb >> 16; + uint32_t new_red = red; + uint32_t new_blue = argb; + + if (inverse) { + new_red += ColorTransformDelta(m->green_to_red_, green); + new_red &= 0xff; + new_blue += ColorTransformDelta(m->green_to_blue_, green); + new_blue += ColorTransformDelta(m->red_to_blue_, new_red); + new_blue &= 0xff; + } else { + new_red -= ColorTransformDelta(m->green_to_red_, green); + new_red &= 0xff; + new_blue -= ColorTransformDelta(m->green_to_blue_, green); + new_blue -= ColorTransformDelta(m->red_to_blue_, red); + new_blue &= 0xff; + } + return (argb & 0xff00ff00u) | (new_red << 16) | (new_blue); +} + +static WEBP_INLINE int SkipRepeatedPixels(const uint32_t* const argb, + int ix, int xsize) { + const uint32_t v = argb[ix]; + if (ix >= xsize + 3) { + if (v == argb[ix - xsize] && + argb[ix - 1] == argb[ix - xsize - 1] && + argb[ix - 2] == argb[ix - xsize - 2] && + argb[ix - 3] == argb[ix - xsize - 3]) { + return 1; + } + return v == argb[ix - 3] && v == argb[ix - 2] && v == argb[ix - 1]; + } else if (ix >= 3) { + return v == argb[ix - 3] && v == argb[ix - 2] && v == argb[ix - 1]; + } + return 0; +} + +static double PredictionCostCrossColor(const int accumulated[256], + const int counts[256]) { + // Favor low entropy, locally and globally. + int i; + int combo[256]; + for (i = 0; i < 256; ++i) { + combo[i] = accumulated[i] + counts[i]; + } + return + ShannonEntropy(combo, 256) + + ShannonEntropy(counts, 256) + + PredictionCostSpatial(counts, 3, 2.4); // Favor small absolute values. +} + +static Multipliers GetBestColorTransformForTile( + int tile_x, int tile_y, int bits, + Multipliers prevX, + Multipliers prevY, + int step, int xsize, int ysize, + int* accumulated_red_histo, + int* accumulated_blue_histo, + const uint32_t* const argb) { + double best_diff = 1e99; + double cur_diff; + const int halfstep = step / 2; + const int max_tile_size = 1 << bits; + const int tile_y_offset = tile_y * max_tile_size; + const int tile_x_offset = tile_x * max_tile_size; + int green_to_red; + int green_to_blue; + int red_to_blue; + int all_x_max = tile_x_offset + max_tile_size; + int all_y_max = tile_y_offset + max_tile_size; + Multipliers best_tx; + MultipliersClear(&best_tx); + if (all_x_max > xsize) { + all_x_max = xsize; + } + if (all_y_max > ysize) { + all_y_max = ysize; + } + for (green_to_red = -64; green_to_red <= 64; green_to_red += halfstep) { + int histo[256] = { 0 }; + int all_y; + Multipliers tx; + MultipliersClear(&tx); + tx.green_to_red_ = green_to_red & 0xff; + + for (all_y = tile_y_offset; all_y < all_y_max; ++all_y) { + uint32_t predict; + int ix = all_y * xsize + tile_x_offset; + int all_x; + for (all_x = tile_x_offset; all_x < all_x_max; ++all_x, ++ix) { + if (SkipRepeatedPixels(argb, ix, xsize)) { + continue; + } + predict = TransformColor(&tx, argb[ix], 0); + ++histo[(predict >> 16) & 0xff]; // red. + } + } + cur_diff = PredictionCostCrossColor(&accumulated_red_histo[0], &histo[0]); + if (tx.green_to_red_ == prevX.green_to_red_) { + cur_diff -= 3; // favor keeping the areas locally similar + } + if (tx.green_to_red_ == prevY.green_to_red_) { + cur_diff -= 3; // favor keeping the areas locally similar + } + if (tx.green_to_red_ == 0) { + cur_diff -= 3; + } + if (cur_diff < best_diff) { + best_diff = cur_diff; + best_tx = tx; + } + } + best_diff = 1e99; + green_to_red = best_tx.green_to_red_; + for (green_to_blue = -32; green_to_blue <= 32; green_to_blue += step) { + for (red_to_blue = -32; red_to_blue <= 32; red_to_blue += step) { + int all_y; + int histo[256] = { 0 }; + Multipliers tx; + tx.green_to_red_ = green_to_red; + tx.green_to_blue_ = green_to_blue; + tx.red_to_blue_ = red_to_blue; + for (all_y = tile_y_offset; all_y < all_y_max; ++all_y) { + uint32_t predict; + int all_x; + int ix = all_y * xsize + tile_x_offset; + for (all_x = tile_x_offset; all_x < all_x_max; ++all_x, ++ix) { + if (SkipRepeatedPixels(argb, ix, xsize)) { + continue; + } + predict = TransformColor(&tx, argb[ix], 0); + ++histo[predict & 0xff]; // blue. + } + } + cur_diff = + PredictionCostCrossColor(&accumulated_blue_histo[0], &histo[0]); + if (tx.green_to_blue_ == prevX.green_to_blue_) { + cur_diff -= 3; // favor keeping the areas locally similar + } + if (tx.green_to_blue_ == prevY.green_to_blue_) { + cur_diff -= 3; // favor keeping the areas locally similar + } + if (tx.red_to_blue_ == prevX.red_to_blue_) { + cur_diff -= 3; // favor keeping the areas locally similar + } + if (tx.red_to_blue_ == prevY.red_to_blue_) { + cur_diff -= 3; // favor keeping the areas locally similar + } + if (tx.green_to_blue_ == 0) { + cur_diff -= 3; + } + if (tx.red_to_blue_ == 0) { + cur_diff -= 3; + } + if (cur_diff < best_diff) { + best_diff = cur_diff; + best_tx = tx; + } + } + } + return best_tx; +} + +static void CopyTileWithColorTransform(int xsize, int ysize, + int tile_x, int tile_y, int bits, + Multipliers color_transform, + uint32_t* const argb) { + int y; + int xscan = 1 << bits; + int yscan = 1 << bits; + tile_x <<= bits; + tile_y <<= bits; + if (xscan > xsize - tile_x) { + xscan = xsize - tile_x; + } + if (yscan > ysize - tile_y) { + yscan = ysize - tile_y; + } + yscan += tile_y; + for (y = tile_y; y < yscan; ++y) { + int ix = y * xsize + tile_x; + const int end_ix = ix + xscan; + for (; ix < end_ix; ++ix) { + argb[ix] = TransformColor(&color_transform, argb[ix], 0); + } + } +} + +void VP8LColorSpaceTransform(int width, int height, int bits, int quality, + uint32_t* const argb, uint32_t* image) { + const int max_tile_size = 1 << bits; + const int step = (quality == 0) ? 32 : 8; + int tile_xsize = VP8LSubSampleSize(width, bits); + int tile_ysize = VP8LSubSampleSize(height, bits); + int accumulated_red_histo[256] = { 0 }; + int accumulated_blue_histo[256] = { 0 }; + int tile_y; + int tile_x; + Multipliers prevX; + Multipliers prevY; + MultipliersClear(&prevY); + MultipliersClear(&prevX); + for (tile_y = 0; tile_y < tile_ysize; ++tile_y) { + for (tile_x = 0; tile_x < tile_xsize; ++tile_x) { + Multipliers color_transform; + int all_x_max; + int y; + const int tile_y_offset = tile_y * max_tile_size; + const int tile_x_offset = tile_x * max_tile_size; + if (tile_y != 0) { + ColorCodeToMultipliers(image[tile_y * tile_xsize + tile_x - 1], &prevX); + ColorCodeToMultipliers(image[(tile_y - 1) * tile_xsize + tile_x], + &prevY); + } else if (tile_x != 0) { + ColorCodeToMultipliers(image[tile_y * tile_xsize + tile_x - 1], &prevX); + } + color_transform = + GetBestColorTransformForTile(tile_x, tile_y, bits, + prevX, prevY, + step, width, height, + &accumulated_red_histo[0], + &accumulated_blue_histo[0], + argb); + image[tile_y * tile_xsize + tile_x] = + MultipliersToColorCode(&color_transform); + CopyTileWithColorTransform(width, height, tile_x, tile_y, bits, + color_transform, argb); + + // Gather accumulated histogram data. + all_x_max = tile_x_offset + max_tile_size; + if (all_x_max > width) { + all_x_max = width; + } + for (y = 0; y < max_tile_size; ++y) { + int ix; + int all_x; + int all_y = tile_y_offset + y; + if (all_y >= height) { + break; + } + ix = all_y * width + tile_x_offset; + for (all_x = tile_x_offset; all_x < all_x_max; ++all_x, ++ix) { + if (ix >= 2 && + argb[ix] == argb[ix - 2] && + argb[ix] == argb[ix - 1]) { + continue; // repeated pixels are handled by backward references + } + if (ix >= width + 2 && + argb[ix - 2] == argb[ix - width - 2] && + argb[ix - 1] == argb[ix - width - 1] && + argb[ix] == argb[ix - width]) { + continue; // repeated pixels are handled by backward references + } + ++accumulated_red_histo[(argb[ix] >> 16) & 0xff]; + ++accumulated_blue_histo[argb[ix] & 0xff]; + } + } + } + } } // Color space inverse transform. @@ -277,7 +866,7 @@ static void ColorSpaceInverseTransform(const VP8LTransform* const transform, for (x = 0; x < width; ++x) { if ((x & mask) == 0) ColorCodeToMultipliers(*pred++, &m); - TransformColor(&m, data + x); + data[x] = TransformColor(&m, data[x], 1); } data += width; ++y; diff --git a/src/dsp/lossless.h b/src/dsp/lossless.h index 10fdb96c..a787e220 100644 --- a/src/dsp/lossless.h +++ b/src/dsp/lossless.h @@ -21,7 +21,7 @@ extern "C" { #endif //------------------------------------------------------------------------------ -// Inverse image transforms. +// Image transforms. struct VP8LTransform; // Defined in dec/vp8li.h. @@ -33,23 +33,41 @@ void VP8LInverseTransform(const struct VP8LTransform* const transform, int row_start, int row_end, uint32_t* const data_in, uint32_t* const data_out); +// Subtracts green from blue and red channels. +void VP8LSubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixs); + +void VP8LResidualImage(int width, int height, int bits, + uint32_t* const argb, uint32_t* const image); + +void VP8LColorSpaceTransform(int width, int height, int bits, int quality, + uint32_t* const argb, uint32_t* image); //------------------------------------------------------------------------------ // Color space conversion. // Converts from BGRA to other color spaces. void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels, - WEBP_CSP_MODE out_colorspace, - uint8_t* const rgba); + WEBP_CSP_MODE out_colorspace, + uint8_t* const rgba); //------------------------------------------------------------------------------ // Misc methods. +// Faster logarithm for small integers, with the property of log(0) == 0. +double VP8LFastLog(int v); + // Computes sampled size of 'size' when sampling using 'sampling bits'. static WEBP_INLINE uint32_t VP8LSubSampleSize(uint32_t size, uint32_t sampling_bits) { return (size + (1 << sampling_bits) - 1) >> sampling_bits; } +// In-place difference of each component with mod 256. +static WEBP_INLINE uint32_t VP8LSubPixels(uint32_t a, uint32_t b) { + const uint32_t alpha_and_green = (a & 0xff00ff00u) - (b & 0xff00ff00u); + const uint32_t red_and_blue = (a & 0x00ff00ffu) - (b & 0x00ff00ffu); + return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); +} + //------------------------------------------------------------------------------ #if defined(__cplusplus) || defined(c_plusplus) diff --git a/src/enc/histogram.c b/src/enc/histogram.c index 9d239367..6ef92942 100644 --- a/src/enc/histogram.c +++ b/src/enc/histogram.c @@ -14,107 +14,7 @@ #include "./backward_references.h" #include "./histogram.h" - -// A lookup table for small values of log(int) to be used in entropy -// computation. -// -// ", ".join(["%.16ff" % x for x in [0.0]+[log(x) for x in range(1, 256)]]) -static const float kLogTable[] = { - 0.0000000000000000f, 0.0000000000000000f, 0.6931471805599453f, - 1.0986122886681098f, 1.3862943611198906f, 1.6094379124341003f, - 1.7917594692280550f, 1.9459101490553132f, 2.0794415416798357f, - 2.1972245773362196f, 2.3025850929940459f, 2.3978952727983707f, - 2.4849066497880004f, 2.5649493574615367f, 2.6390573296152584f, - 2.7080502011022101f, 2.7725887222397811f, 2.8332133440562162f, - 2.8903717578961645f, 2.9444389791664403f, 2.9957322735539909f, - 3.0445224377234230f, 3.0910424533583161f, 3.1354942159291497f, - 3.1780538303479458f, 3.2188758248682006f, 3.2580965380214821f, - 3.2958368660043291f, 3.3322045101752038f, 3.3672958299864741f, - 3.4011973816621555f, 3.4339872044851463f, 3.4657359027997265f, - 3.4965075614664802f, 3.5263605246161616f, 3.5553480614894135f, - 3.5835189384561099f, 3.6109179126442243f, 3.6375861597263857f, - 3.6635616461296463f, 3.6888794541139363f, 3.7135720667043080f, - 3.7376696182833684f, 3.7612001156935624f, 3.7841896339182610f, - 3.8066624897703196f, 3.8286413964890951f, 3.8501476017100584f, - 3.8712010109078911f, 3.8918202981106265f, 3.9120230054281460f, - 3.9318256327243257f, 3.9512437185814275f, 3.9702919135521220f, - 3.9889840465642745f, 4.0073331852324712f, 4.0253516907351496f, - 4.0430512678345503f, 4.0604430105464191f, 4.0775374439057197f, - 4.0943445622221004f, 4.1108738641733114f, 4.1271343850450917f, - 4.1431347263915326f, 4.1588830833596715f, 4.1743872698956368f, - 4.1896547420264252f, 4.2046926193909657f, 4.2195077051761070f, - 4.2341065045972597f, 4.2484952420493594f, 4.2626798770413155f, - 4.2766661190160553f, 4.2904594411483910f, 4.3040650932041702f, - 4.3174881135363101f, 4.3307333402863311f, 4.3438054218536841f, - 4.3567088266895917f, 4.3694478524670215f, 4.3820266346738812f, - 4.3944491546724391f, 4.4067192472642533f, 4.4188406077965983f, - 4.4308167988433134f, 4.4426512564903167f, 4.4543472962535073f, - 4.4659081186545837f, 4.4773368144782069f, 4.4886363697321396f, - 4.4998096703302650f, 4.5108595065168497f, 4.5217885770490405f, - 4.5325994931532563f, 4.5432947822700038f, 4.5538768916005408f, - 4.5643481914678361f, 4.5747109785033828f, 4.5849674786705723f, - 4.5951198501345898f, 4.6051701859880918f, 4.6151205168412597f, - 4.6249728132842707f, 4.6347289882296359f, 4.6443908991413725f, - 4.6539603501575231f, 4.6634390941120669f, 4.6728288344619058f, - 4.6821312271242199f, 4.6913478822291435f, 4.7004803657924166f, - 4.7095302013123339f, 4.7184988712950942f, 4.7273878187123408f, - 4.7361984483944957f, 4.7449321283632502f, 4.7535901911063645f, - 4.7621739347977563f, 4.7706846244656651f, 4.7791234931115296f, - 4.7874917427820458f, 4.7957905455967413f, 4.8040210447332568f, - 4.8121843553724171f, 4.8202815656050371f, 4.8283137373023015f, - 4.8362819069514780f, 4.8441870864585912f, 4.8520302639196169f, - 4.8598124043616719f, 4.8675344504555822f, 4.8751973232011512f, - 4.8828019225863706f, 4.8903491282217537f, 4.8978397999509111f, - 4.9052747784384296f, 4.9126548857360524f, 4.9199809258281251f, - 4.9272536851572051f, 4.9344739331306915f, 4.9416424226093039f, - 4.9487598903781684f, 4.9558270576012609f, 4.9628446302599070f, - 4.9698132995760007f, 4.9767337424205742f, 4.9836066217083363f, - 4.9904325867787360f, 4.9972122737641147f, 5.0039463059454592f, - 5.0106352940962555f, 5.0172798368149243f, 5.0238805208462765f, - 5.0304379213924353f, 5.0369526024136295f, 5.0434251169192468f, - 5.0498560072495371f, 5.0562458053483077f, 5.0625950330269669f, - 5.0689042022202315f, 5.0751738152338266f, 5.0814043649844631f, - 5.0875963352323836f, 5.0937502008067623f, 5.0998664278241987f, - 5.1059454739005803f, 5.1119877883565437f, 5.1179938124167554f, - 5.1239639794032588f, 5.1298987149230735f, 5.1357984370502621f, - 5.1416635565026603f, 5.1474944768134527f, 5.1532915944977793f, - 5.1590552992145291f, 5.1647859739235145f, 5.1704839950381514f, - 5.1761497325738288f, 5.1817835502920850f, 5.1873858058407549f, - 5.1929568508902104f, 5.1984970312658261f, 5.2040066870767951f, - 5.2094861528414214f, 5.2149357576089859f, 5.2203558250783244f, - 5.2257466737132017f, 5.2311086168545868f, 5.2364419628299492f, - 5.2417470150596426f, 5.2470240721604862f, 5.2522734280466299f, - 5.2574953720277815f, 5.2626901889048856f, 5.2678581590633282f, - 5.2729995585637468f, 5.2781146592305168f, 5.2832037287379885f, - 5.2882670306945352f, 5.2933048247244923f, 5.2983173665480363f, - 5.3033049080590757f, 5.3082676974012051f, 5.3132059790417872f, - 5.3181199938442161f, 5.3230099791384085f, 5.3278761687895813f, - 5.3327187932653688f, 5.3375380797013179f, 5.3423342519648109f, - 5.3471075307174685f, 5.3518581334760666f, 5.3565862746720123f, - 5.3612921657094255f, 5.3659760150218512f, 5.3706380281276624f, - 5.3752784076841653f, 5.3798973535404597f, 5.3844950627890888f, - 5.3890717298165010f, 5.3936275463523620f, 5.3981627015177525f, - 5.4026773818722793f, 5.4071717714601188f, 5.4116460518550396f, - 5.4161004022044201f, 5.4205349992722862f, 5.4249500174814029f, - 5.4293456289544411f, 5.4337220035542400f, 5.4380793089231956f, - 5.4424177105217932f, 5.4467373716663099f, 5.4510384535657002f, - 5.4553211153577017f, 5.4595855141441589f, 5.4638318050256105f, - 5.4680601411351315f, 5.4722706736714750f, 5.4764635519315110f, - 5.4806389233419912f, 5.4847969334906548f, 5.4889377261566867f, - 5.4930614433405482f, 5.4971682252932021f, 5.5012582105447274f, - 5.5053315359323625f, 5.5093883366279774f, 5.5134287461649825f, - 5.5174528964647074f, 5.5214609178622460f, 5.5254529391317835f, - 5.5294290875114234f, 5.5333894887275203f, 5.5373342670185366f, - 5.5412635451584258f, -}; - -// Faster logarithm for small integers, with the property of log(0) == 0. -static WEBP_INLINE double FastLog(int v) { - if (v < (int)(sizeof(kLogTable) / sizeof(kLogTable[0]))) { - return kLogTable[v]; - } - return log(v); -} +#include "../dsp/lossless.h" void VP8LConvertPopulationCountTableToBitEstimates( int num_symbols, @@ -176,21 +76,6 @@ void VP8LHistogramCreate(VP8LHistogram* const p, } } -double VP8LShannonEntropy(const int* const array, int n) { - int i; - double retval = 0; - int sum = 0; - for (i = 0; i < n; ++i) { - if (array[i] != 0) { - sum += array[i]; - retval += array[i] * FastLog(array[i]); - } - } - retval -= sum * FastLog(sum); - retval *= -1.4426950408889634; // 1.0 / -FastLog(2); - return retval; -} - static double BitsEntropy(const int* const array, int n) { double retval = 0; int sum = 0; @@ -202,13 +87,13 @@ static double BitsEntropy(const int* const array, int n) { if (array[i] != 0) { sum += array[i]; ++nonzeros; - retval += array[i] * FastLog(array[i]); + retval += array[i] * VP8LFastLog(array[i]); if (max_val < array[i]) { max_val = array[i]; } } } - retval -= sum * FastLog(sum); + retval -= sum * VP8LFastLog(sum); retval *= -1.4426950408889634; // 1.0 / -Log(2); mix = 0.627; if (nonzeros < 5) { diff --git a/src/enc/histogram.h b/src/enc/histogram.h index 339f0d22..533f5e83 100644 --- a/src/enc/histogram.h +++ b/src/enc/histogram.h @@ -47,7 +47,7 @@ static WEBP_INLINE void VP8LHistogramClear(VP8LHistogram* const p) { } static WEBP_INLINE void VP8LHistogramInit(VP8LHistogram* const p, - int palette_code_bits) { + int palette_code_bits) { p->palette_code_bits_ = palette_code_bits; VP8LHistogramClear(p); } @@ -118,8 +118,6 @@ static WEBP_INLINE int VP8LHistogramNumCodes(const VP8LHistogram* const p) { void VP8LConvertPopulationCountTableToBitEstimates( int n, const int* const population_counts, double* const output); -double VP8LShannonEntropy(const int* const array, int n); - // Build a 2d image of histograms, subresolutioned by (1 << histobits) to // the original image. int VP8LHistogramBuildImage(int xsize, int ysize, diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 40157bc3..9f6bd952 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -13,8 +13,11 @@ #include #include #include + +#include "./backward_references.h" #include "./vp8enci.h" #include "./vp8li.h" +#include "../dsp/lossless.h" #include "../utils/bit_writer.h" #if defined(__cplusplus) || defined(c_plusplus) @@ -23,37 +26,223 @@ extern "C" { static const uint32_t kImageSizeBits = 14; +static int Uint32Order(const void* p1, const void* p2) { + const uint32_t a = *(const uint32_t*)p1; + const uint32_t b = *(const uint32_t*)p2; + if (a < b) { + return -1; + } + if (a == b) { + return 0; + } + return 1; +} + +static int CreatePalette256(const uint32_t* const argb, int num_pix, + uint32_t* const palette, int* const palette_size) { + int i, key; + int current_size = 0; + uint8_t in_use[MAX_PALETTE_SIZE * 4]; + uint32_t colors[MAX_PALETTE_SIZE * 4]; + static const uint32_t kHashMul = 0x1e35a7bd; + + memset(in_use, 0, sizeof(in_use)); + key = (kHashMul * argb[0]) >> PALETTE_KEY_RIGHT_SHIFT; + colors[key] = argb[0]; + in_use[key] = 1; + ++current_size; + + for (i = 1; i < num_pix; ++i) { + if (argb[i] == argb[i - 1]) { + continue; + } + key = (kHashMul * argb[i]) >> PALETTE_KEY_RIGHT_SHIFT; + while (1) { + if (!in_use[key]) { + colors[key] = argb[i]; + in_use[key] = 1; + ++current_size; + if (current_size > MAX_PALETTE_SIZE) { + return 0; + } + break; + } else if (colors[key] == argb[i]) { + // The color is already there. + break; + } else { + // Some other color sits there. + // Do linear conflict resolution. + ++key; + key &= 0x3ff; // key for 1K buffer. + } + } + } + + *palette_size = 0; + for (i = 0; i < (int)sizeof(in_use); ++i) { + if (in_use[i]) { + palette[*palette_size] = colors[i]; + ++(*palette_size); + } + } + + qsort(palette, *palette_size, sizeof(*palette), Uint32Order); + return 1; +} + +static int AnalyzeEntropy(const uint32_t const *argb, int xsize, int ysize, + int* nonpredicted_bits, int* predicted_bits) { + int i; + uint32_t pix_diff; + VP8LHistogram* nonpredicted = NULL; + VP8LHistogram* predicted = (VP8LHistogram*)malloc(2 * sizeof(*predicted)); + if (predicted == NULL) return 0; + nonpredicted = predicted + sizeof(*predicted); + + VP8LHistogramInit(predicted, 0); + VP8LHistogramInit(nonpredicted, 0); + for (i = 1; i < xsize * ysize; ++i) { + if ((argb[i] == argb[i - 1]) || + (i >= xsize && argb[i] == argb[i - xsize])) { + continue; + } + VP8LHistogramAddSinglePixOrCopy(nonpredicted, + PixOrCopyCreateLiteral(argb[i])); + pix_diff = VP8LSubPixels(argb[i], argb[i - 1]); + VP8LHistogramAddSinglePixOrCopy(predicted, + PixOrCopyCreateLiteral(pix_diff)); + } + *nonpredicted_bits = (int)VP8LHistogramEstimateBitsBulk(nonpredicted); + *predicted_bits = (int)VP8LHistogramEstimateBitsBulk(predicted); + free(predicted); + return 1; +} + static int VP8LEncAnalyze(VP8LEncoder* const enc) { - (void)enc; + const WebPPicture* const pic = enc->pic_; + int non_pred_entropy, pred_entropy; + int is_photograph = 0; + assert(pic && pic->argb); + + if (!AnalyzeEntropy(pic->argb, pic->width, pic->height, + &non_pred_entropy, &pred_entropy)) { + return 0; + } + is_photograph = + pred_entropy < (non_pred_entropy - (non_pred_entropy >> 3)); + + if (is_photograph) { + enc->use_predict_ = 1; + enc->use_cross_color_ = 1; + } + + enc->use_palette_ = CreatePalette256(pic->argb, pic->width * pic->height, + enc->palette_, &enc->palette_size_); return 1; } -static int EncodeImageInternal(VP8LEncoder* const enc) { - (void)enc; +// Bundles multiple (2, 4 or 8) pixels into a single pixel. +// Returns the new xsize. +static void BundleColorMap(const uint32_t* const argb, + int width, int height, int xbits, + uint32_t* bundled_argb, int xs) { + int x, y; + const int bit_depth = 1 << (3 - xbits); + uint32_t code = 0; + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + const int xsub = x & ((1 << xbits) - 1); + if (xsub == 0) { + code = 0; + } + code |= (argb[y * width + x] & 0xff00) << (bit_depth * xsub); + bundled_argb[y * xs + (x >> xbits)] = 0xff000000 | code; + } + } +} + +static int EncodeImageInternal(VP8LBitWriter* const bw, + const uint32_t* const argb, + int width, int height, int quality, + int cache_bits, int histogram_bits) { + (void)bw; + (void)argb; + (void)width; + (void)height; + (void)quality; + (void)cache_bits; + (void)histogram_bits; return 1; } -static int CreatePalette(VP8LEncoder* const enc) { - (void)enc; +static int EvalAndApplySubtractGreen(VP8LBitWriter* const bw, + VP8LEncoder* const enc, + int width, int height) { + int i; + VP8LHistogram* before = NULL; + // Check if it would be a good idea to subtract green from red and blue. + VP8LHistogram* after = (VP8LHistogram*)malloc(2 * sizeof(*after)); + if (after == NULL) return 0; + before = after + sizeof(*after); + + VP8LHistogramInit(before, 1); + VP8LHistogramInit(after, 1); + for (i = 0; i < width * height; ++i) { + // We only impact entropy in red and blue components, don't bother + // to look at others. + const uint32_t c = enc->argb_[i]; + const int green = (c >> 8) & 0xff; + ++(before->red_[(c >> 16) & 0xff]); + ++(before->blue_[c & 0xff]); + ++(after->red_[((c >> 16) - green) & 0xff]); + ++(after->blue_[(c - green) & 0xff]); + } + // Check if subtracting green yields low entropy. + if (VP8LHistogramEstimateBits(after) < VP8LHistogramEstimateBits(before)) { + VP8LWriteBits(bw, 1, 1); + VP8LWriteBits(bw, 2, 2); + VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height); + } + free(after); return 1; } -static void EvalSubtractGreen(VP8LEncoder* const enc) { - (void)enc; -} +static int ApplyPredictFilter(VP8LBitWriter* const bw, + VP8LEncoder* const enc, + int width, int height, int quality) { + const int pred_bits = enc->transform_bits_; + const int transform_width = VP8LSubSampleSize(width, pred_bits); + const int transform_height = VP8LSubSampleSize(height, pred_bits); -static int ApplyPredictFilter(VP8LEncoder* const enc) { - (void)enc; + VP8LResidualImage(width, height, pred_bits, enc->argb_, enc->transform_data_); + VP8LWriteBits(bw, 1, 1); + VP8LWriteBits(bw, 2, 0); + VP8LWriteBits(bw, 4, pred_bits); + if (!EncodeImageInternal(bw, enc->transform_data_, + transform_width, transform_height, quality, 0, 0)) { + return 0; + } return 1; } -static int ApplyCrossColorFilter(VP8LEncoder* const enc) { - (void)enc; - return 1; -} +static int ApplyCrossColorFilter(VP8LBitWriter* const bw, + VP8LEncoder* const enc, + int width, int height, int quality) { + const int ccolor_transform_bits = enc->transform_bits_; + const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits); + const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits); -static void EvalColorCache(VP8LEncoder* const enc) { - (void)enc; + VP8LColorSpaceTransform(width, height, ccolor_transform_bits, quality, + enc->argb_, enc->transform_data_); + VP8LWriteBits(bw, 1, 1); + VP8LWriteBits(bw, 2, 1); + VP8LWriteBits(bw, 4, ccolor_transform_bits); + if (!EncodeImageInternal(bw, enc->transform_data_, + transform_width, transform_height, quality, 0, 0)) { + return 0; + } + return 1; } static void PutLE32(uint8_t* const data, uint32_t val) { @@ -125,10 +314,15 @@ static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); return NULL; } + memset(enc, 0, sizeof(*enc)); + enc->pic_ = picture; enc->use_lz77_ = 1; enc->palette_bits_ = 7; + enc->argb_ = NULL; + enc->width_ = picture->width; + // TODO: Use config.quality to initialize histo_bits_ and transform_bits_. enc->histo_bits_ = 4; enc->transform_bits_ = 4; @@ -150,9 +344,32 @@ static void DeleteVP8LEncoder(VP8LEncoder* enc) { free(enc); } +static WebPEncodingError AllocateEncodeBuffer(VP8LEncoder* const enc, + int height, int width) { + WebPEncodingError err = VP8_ENC_OK; + const size_t image_size = height * width; + const size_t transform_data_size = + VP8LSubSampleSize(height, enc->transform_bits_) * + VP8LSubSampleSize(width, enc->transform_bits_); + const size_t total_size = image_size + transform_data_size; + enc->argb_ = (uint32_t*)malloc(total_size * sizeof(*enc->argb_)); + if (enc->argb_ == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + enc->transform_data_ = enc->argb_ + image_size; + + Error: + return err; +} + int VP8LEncodeImage(const WebPConfig* const config, WebPPicture* const picture) { + int i; int ok = 0; + int use_color_cache = 1; + int cache_bits = 7; + int width, height, quality; VP8LEncoder* enc = NULL; WebPEncodingError err = VP8_ENC_OK; VP8LBitWriter bw; @@ -169,42 +386,110 @@ int VP8LEncodeImage(const WebPConfig* const config, err = VP8_ENC_ERROR_NULL_PARAMETER; goto Error; } + width = picture->width; + height = picture->height; + quality = config->quality; - VP8LBitWriterInit(&bw, (picture->width * picture->height) >> 1); + VP8LBitWriterInit(&bw, (width * height) >> 1); // --------------------------------------------------------------------------- // Analyze image (entropy, num_palettes etc) - if (!VP8LEncAnalyze(enc)) goto Error; - - if (enc->use_palette_) { - CreatePalette(enc); + if (!VP8LEncAnalyze(enc)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; } // Write image size. WriteImageSize(enc, &bw); + if (enc->use_palette_) { + uint32_t* argb = picture->argb; + const uint32_t* const palette = enc->palette_; + const int palette_size = enc->palette_size_; + uint32_t argb_palette[MAX_PALETTE_SIZE]; + + for (i = 0; i < width * height; ++i) { + int k; + for (k = 0; k < palette_size; ++k) { + if (argb[i] == palette[k]) { + argb_palette[i] = 0xff000000 | (k << 8); + break; + } + } + } + VP8LWriteBits(&bw, 1, 1); + VP8LWriteBits(&bw, 2, 3); + VP8LWriteBits(&bw, 8, palette_size - 1); + for (i = palette_size - 1; i >= 1; --i) { + argb_palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); + } + if (!EncodeImageInternal(&bw, argb_palette, palette_size, 1, quality, + 0, 0)) { + goto Error; + } + use_color_cache = 0; + if (palette_size <= 16) { + int xbits = 1; + if (palette_size <= 2) { + xbits = 3; + } else if (palette_size <= 4) { + xbits = 2; + } + + // Image can be packed (multiple pixels per uint32). + enc->width_ = VP8LSubSampleSize(width, xbits); + err = AllocateEncodeBuffer(enc, height, enc->width_); + if (err != VP8_ENC_OK) goto Error; + BundleColorMap(argb, width, height, xbits, enc->argb_, enc->width_); + } + } + + // In case image is not packed. + if (enc->argb_ == NULL) { + const size_t image_size = height * enc->width_; + err = AllocateEncodeBuffer(enc, height, enc->width_); + if (err != VP8_ENC_OK) goto Error; + memcpy(enc->argb_, picture->argb, image_size * sizeof(*enc->argb_)); + } + // --------------------------------------------------------------------------- // Apply transforms and write transform data. - EvalSubtractGreen(enc); + if (!EvalAndApplySubtractGreen(&bw, enc, enc->width_, height)) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } if (enc->use_predict_) { - if (!ApplyPredictFilter(enc)) goto Error; + if (!ApplyPredictFilter(&bw, enc, enc->width_, height, quality)) { + err = VP8_ENC_ERROR_INVALID_CONFIGURATION; + goto Error; + } } if (enc->use_cross_color_) { - if (!ApplyCrossColorFilter(enc)) goto Error; + if (!ApplyCrossColorFilter(&bw, enc, enc->width_, height, quality)) { + err = VP8_ENC_ERROR_INVALID_CONFIGURATION; + goto Error; + } } - if (enc->use_color_cache) { - EvalColorCache(enc); + if (use_color_cache) { + if (quality > 25) { + if (!VP8LCalculateEstimateForPaletteSize(enc->argb_, enc->width_, height, + &cache_bits)) { + err = VP8_ENC_ERROR_INVALID_CONFIGURATION; + goto Error; + } + } } // --------------------------------------------------------------------------- // Encode and write the transformed image. - ok = EncodeImageInternal(enc); + ok = EncodeImageInternal(&bw, enc->argb_, enc->width_, height, + quality, cache_bits, enc->histo_bits_); if (!ok) goto Error; err = WriteImage(enc, &bw); diff --git a/src/enc/vp8li.h b/src/enc/vp8li.h index fd2a1bf6..d02e5dba 100644 --- a/src/enc/vp8li.h +++ b/src/enc/vp8li.h @@ -12,6 +12,7 @@ #ifndef WEBP_ENC_VP8LI_H_ #define WEBP_ENC_VP8LI_H_ +#include "./histogram.h" #include "../webp/encode.h" #include "../utils/bit_writer.h" @@ -27,9 +28,16 @@ extern "C" { #define SIGNATURE_SIZE 1 #define LOSSLESS_MAGIC_BYTE 0x64 +#define MAX_PALETTE_SIZE 256 +#define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer. + typedef struct { const WebPConfig* config_; // user configuration and parameters - WebPPicture* pic_; // input / output picture + WebPPicture* pic_; // input picture. + + uint32_t* argb_; // Transformed argb image data. + uint32_t* transform_data_; // Scratch memory for transform data. + int width_; // Packed image width. // Encoding parameters derived from quality parameter. int use_lz77_; @@ -38,13 +46,11 @@ typedef struct { int transform_bits_; // Encoding parameters derived from image characteristics. - int predicted_bits_; - int non_predicted_bits_; - int use_palette_; - int num_palette_colors; - int use_predict_; int use_cross_color_; - int use_color_cache; + int use_predict_; + int use_palette_; + int palette_size_; + uint32_t palette_[MAX_PALETTE_SIZE]; } VP8LEncoder; //------------------------------------------------------------------------------ From 09f7532ccede606b18e257cd6fe583704f5e8f1d Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Wed, 11 Apr 2012 04:20:16 +0000 Subject: [PATCH 07/46] Fix few nits (const qualifiers) Change-Id: I527e82af49956b695ab18625d34e143854067421 --- src/dsp/lossless.c | 26 +++---- src/dsp/lossless.h | 2 +- src/enc/vp8l.c | 169 +++++++++++++++++++++++++-------------------- src/enc/vp8li.h | 2 +- 4 files changed, 108 insertions(+), 91 deletions(-) diff --git a/src/dsp/lossless.c b/src/dsp/lossless.c index 8c1dff3b..cc193bdd 100644 --- a/src/dsp/lossless.c +++ b/src/dsp/lossless.c @@ -478,10 +478,10 @@ static void PredictorInverseTransform(const VP8LTransform* const transform, const int width = transform->xsize_; if (y_start == 0) { // First Row follows the L (mode=1) mode. int x; - uint32_t pred = Predictor0(data, NULL); + const uint32_t pred = Predictor0(data, NULL); AddPixelsEq(data, pred); for (x = 1; x < width; ++x) { - pred = Predictor1(data + x, NULL); + const uint32_t pred = Predictor1(data + x, NULL); AddPixelsEq(data + x, pred); } data += width; @@ -558,8 +558,7 @@ typedef struct { int red_to_blue_; } Multipliers; -static WEBP_INLINE void MultipliersClear( - Multipliers* m) { +static WEBP_INLINE void MultipliersClear(Multipliers* m) { m->green_to_red_ = 0; m->green_to_blue_ = 0; m->red_to_blue_ = 0; @@ -578,11 +577,10 @@ static WEBP_INLINE void ColorCodeToMultipliers(uint32_t color_code, } static WEBP_INLINE uint32_t MultipliersToColorCode(Multipliers* const m) { - return - 0xff000000u | - ((uint32_t)(m->red_to_blue_) << 16) | - ((uint32_t)(m->green_to_blue_) << 8) | - m->green_to_red_; + return 0xff000000u | + ((uint32_t)(m->red_to_blue_) << 16) | + ((uint32_t)(m->green_to_blue_) << 8) | + m->green_to_red_; } static WEBP_INLINE uint32_t TransformColor(const Multipliers* const m, @@ -633,10 +631,9 @@ static double PredictionCostCrossColor(const int accumulated[256], for (i = 0; i < 256; ++i) { combo[i] = accumulated[i] + counts[i]; } - return - ShannonEntropy(combo, 256) + - ShannonEntropy(counts, 256) + - PredictionCostSpatial(counts, 3, 2.4); // Favor small absolute values. + return ShannonEntropy(combo, 256) + + ShannonEntropy(counts, 256) + + PredictionCostSpatial(counts, 3, 2.4); // Favor small absolute values. } static Multipliers GetBestColorTransformForTile( @@ -776,10 +773,9 @@ static void CopyTileWithColorTransform(int xsize, int ysize, } } -void VP8LColorSpaceTransform(int width, int height, int bits, int quality, +void VP8LColorSpaceTransform(int width, int height, int bits, int step, uint32_t* const argb, uint32_t* image) { const int max_tile_size = 1 << bits; - const int step = (quality == 0) ? 32 : 8; int tile_xsize = VP8LSubSampleSize(width, bits); int tile_ysize = VP8LSubSampleSize(height, bits); int accumulated_red_histo[256] = { 0 }; diff --git a/src/dsp/lossless.h b/src/dsp/lossless.h index a787e220..7ec5cae4 100644 --- a/src/dsp/lossless.h +++ b/src/dsp/lossless.h @@ -39,7 +39,7 @@ void VP8LSubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixs); void VP8LResidualImage(int width, int height, int bits, uint32_t* const argb, uint32_t* const image); -void VP8LColorSpaceTransform(int width, int height, int bits, int quality, +void VP8LColorSpaceTransform(int width, int height, int bits, int step, uint32_t* const argb, uint32_t* image); //------------------------------------------------------------------------------ // Color space conversion. diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 9f6bd952..0aeb8ea2 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -26,7 +26,7 @@ extern "C" { static const uint32_t kImageSizeBits = 14; -static int Uint32Order(const void* p1, const void* p2) { +static int CompareColors(const void* p1, const void* p2) { const uint32_t a = *(const uint32_t*)p1; const uint32_t b = *(const uint32_t*)p2; if (a < b) { @@ -39,18 +39,18 @@ static int Uint32Order(const void* p1, const void* p2) { } static int CreatePalette256(const uint32_t* const argb, int num_pix, - uint32_t* const palette, int* const palette_size) { + uint32_t palette[MAX_PALETTE_SIZE], + int* const palette_size) { int i, key; - int current_size = 0; - uint8_t in_use[MAX_PALETTE_SIZE * 4]; + int num_colors = 0; + uint8_t in_use[MAX_PALETTE_SIZE * 4] = { 0 }; uint32_t colors[MAX_PALETTE_SIZE * 4]; static const uint32_t kHashMul = 0x1e35a7bd; - memset(in_use, 0, sizeof(in_use)); key = (kHashMul * argb[0]) >> PALETTE_KEY_RIGHT_SHIFT; colors[key] = argb[0]; in_use[key] = 1; - ++current_size; + ++num_colors; for (i = 1; i < num_pix; ++i) { if (argb[i] == argb[i - 1]) { @@ -61,8 +61,8 @@ static int CreatePalette256(const uint32_t* const argb, int num_pix, if (!in_use[key]) { colors[key] = argb[i]; in_use[key] = 1; - ++current_size; - if (current_size > MAX_PALETTE_SIZE) { + ++num_colors; + if (num_colors > MAX_PALETTE_SIZE) { return 0; } break; @@ -73,35 +73,36 @@ static int CreatePalette256(const uint32_t* const argb, int num_pix, // Some other color sits there. // Do linear conflict resolution. ++key; - key &= 0x3ff; // key for 1K buffer. + key &= (MAX_PALETTE_SIZE * 4 - 1); // key mask for 1K buffer. } } } - *palette_size = 0; - for (i = 0; i < (int)sizeof(in_use); ++i) { + num_colors = 0; + for (i = 0; i < (int)(sizeof(in_use) / sizeof(in_use[0])); ++i) { if (in_use[i]) { - palette[*palette_size] = colors[i]; - ++(*palette_size); + palette[num_colors] = colors[i]; + ++num_colors; } } - qsort(palette, *palette_size, sizeof(*palette), Uint32Order); + qsort(palette, num_colors, sizeof(*palette), CompareColors); + *palette_size = num_colors; return 1; } static int AnalyzeEntropy(const uint32_t const *argb, int xsize, int ysize, int* nonpredicted_bits, int* predicted_bits) { int i; - uint32_t pix_diff; VP8LHistogram* nonpredicted = NULL; VP8LHistogram* predicted = (VP8LHistogram*)malloc(2 * sizeof(*predicted)); if (predicted == NULL) return 0; - nonpredicted = predicted + sizeof(*predicted); + nonpredicted = predicted + 1; VP8LHistogramInit(predicted, 0); VP8LHistogramInit(nonpredicted, 0); for (i = 1; i < xsize * ysize; ++i) { + uint32_t pix_diff; if ((argb[i] == argb[i - 1]) || (i >= xsize && argb[i] == argb[i - xsize])) { continue; @@ -121,17 +122,14 @@ static int AnalyzeEntropy(const uint32_t const *argb, int xsize, int ysize, static int VP8LEncAnalyze(VP8LEncoder* const enc) { const WebPPicture* const pic = enc->pic_; int non_pred_entropy, pred_entropy; - int is_photograph = 0; assert(pic && pic->argb); if (!AnalyzeEntropy(pic->argb, pic->width, pic->height, &non_pred_entropy, &pred_entropy)) { return 0; } - is_photograph = - pred_entropy < (non_pred_entropy - (non_pred_entropy >> 3)); - if (is_photograph) { + if (8 * pred_entropy < 7 * non_pred_entropy) { enc->use_predict_ = 1; enc->use_cross_color_ = 1; } @@ -152,10 +150,12 @@ static void BundleColorMap(const uint32_t* const argb, for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { - const int xsub = x & ((1 << xbits) - 1); + const int mask = (1 << xbits) - 1; + const int xsub = x & mask; if (xsub == 0) { code = 0; } + // TODO(vikasa): simplify the bundling logic. code |= (argb[y * width + x] & 0xff00) << (bit_depth * xsub); bundled_argb[y * xs + (x >> xbits)] = 0xff000000 | code; } @@ -184,7 +184,7 @@ static int EvalAndApplySubtractGreen(VP8LBitWriter* const bw, // Check if it would be a good idea to subtract green from red and blue. VP8LHistogram* after = (VP8LHistogram*)malloc(2 * sizeof(*after)); if (after == NULL) return 0; - before = after + sizeof(*after); + before = after + 1; VP8LHistogramInit(before, 1); VP8LHistogramInit(after, 1); @@ -232,8 +232,9 @@ static int ApplyCrossColorFilter(VP8LBitWriter* const bw, const int ccolor_transform_bits = enc->transform_bits_; const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits); const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits); + const int step = (quality == 0) ? 32 : 8; - VP8LColorSpaceTransform(width, height, ccolor_transform_bits, quality, + VP8LColorSpaceTransform(width, height, ccolor_transform_bits, step, enc->argb_, enc->transform_data_); VP8LWriteBits(bw, 1, 1); VP8LWriteBits(bw, 2, 1); @@ -321,7 +322,6 @@ static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, enc->palette_bits_ = 7; enc->argb_ = NULL; - enc->width_ = picture->width; // TODO: Use config.quality to initialize histo_bits_ and transform_bits_. enc->histo_bits_ = 4; @@ -341,11 +341,17 @@ static void WriteImageSize(VP8LEncoder* const enc, VP8LBitWriter* const bw) { } static void DeleteVP8LEncoder(VP8LEncoder* enc) { + free(enc->argb_); free(enc); } -static WebPEncodingError AllocateEncodeBuffer(VP8LEncoder* const enc, - int height, int width) { +// Allocates the memory for argb (W x H) buffer and transform data. +// Former buffer (argb_) will hold the argb data from successive image +// transformtions and later corresponds to prediction data (uint32) used +// for every image tile corresponding to the transformed argb_. +// The dimension of this square tile is 2^transform_bits_. +static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, + int height, int width) { WebPEncodingError err = VP8_ENC_OK; const size_t image_size = height * width; const size_t transform_data_size = @@ -358,6 +364,56 @@ static WebPEncodingError AllocateEncodeBuffer(VP8LEncoder* const enc, goto Error; } enc->transform_data_ = enc->argb_ + image_size; + enc->current_width_ = width; + + Error: + return err; +} + +static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, + VP8LEncoder* const enc, + int width, int height, int quality) { + WebPEncodingError err = VP8_ENC_OK; + int i; + uint32_t* argb = enc->pic_->argb; + const uint32_t* const palette = enc->palette_; + const int palette_size = enc->palette_size_; + uint32_t argb_palette[MAX_PALETTE_SIZE]; + + for (i = 0; i < width * height; ++i) { + int k; + for (k = 0; k < palette_size; ++k) { + const uint32_t pix = argb[i]; + if (pix == palette[k]) { + argb[i] = 0xff000000u | (k << 8); + break; + } + } + } + VP8LWriteBits(bw, 1, 1); + VP8LWriteBits(bw, 2, 3); + VP8LWriteBits(bw, 8, palette_size - 1); + for (i = palette_size - 1; i >= 1; --i) { + argb_palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); + } + if (!EncodeImageInternal(bw, argb_palette, palette_size, 1, quality, + 0, 0)) { + err = VP8_ENC_ERROR_INVALID_CONFIGURATION; + goto Error; + } + if (palette_size <= 16) { + int xbits = 1; + if (palette_size <= 2) { + xbits = 3; + } else if (palette_size <= 4) { + xbits = 2; + } + + // Image can be packed (multiple pixels per uint32). + err = AllocateTransformBuffer(enc, height, VP8LSubSampleSize(width, xbits)); + if (err != VP8_ENC_OK) goto Error; + BundleColorMap(argb, width, height, xbits, enc->argb_, enc->current_width_); + } Error: return err; @@ -365,7 +421,6 @@ static WebPEncodingError AllocateEncodeBuffer(VP8LEncoder* const enc, int VP8LEncodeImage(const WebPConfig* const config, WebPPicture* const picture) { - int i; int ok = 0; int use_color_cache = 1; int cache_bits = 7; @@ -404,81 +459,47 @@ int VP8LEncodeImage(const WebPConfig* const config, WriteImageSize(enc, &bw); if (enc->use_palette_) { - uint32_t* argb = picture->argb; - const uint32_t* const palette = enc->palette_; - const int palette_size = enc->palette_size_; - uint32_t argb_palette[MAX_PALETTE_SIZE]; - - for (i = 0; i < width * height; ++i) { - int k; - for (k = 0; k < palette_size; ++k) { - if (argb[i] == palette[k]) { - argb_palette[i] = 0xff000000 | (k << 8); - break; - } - } - } - VP8LWriteBits(&bw, 1, 1); - VP8LWriteBits(&bw, 2, 3); - VP8LWriteBits(&bw, 8, palette_size - 1); - for (i = palette_size - 1; i >= 1; --i) { - argb_palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); - } - if (!EncodeImageInternal(&bw, argb_palette, palette_size, 1, quality, - 0, 0)) { - goto Error; - } - use_color_cache = 0; - if (palette_size <= 16) { - int xbits = 1; - if (palette_size <= 2) { - xbits = 3; - } else if (palette_size <= 4) { - xbits = 2; - } - - // Image can be packed (multiple pixels per uint32). - enc->width_ = VP8LSubSampleSize(width, xbits); - err = AllocateEncodeBuffer(enc, height, enc->width_); - if (err != VP8_ENC_OK) goto Error; - BundleColorMap(argb, width, height, xbits, enc->argb_, enc->width_); - } + err = ApplyPalette(&bw, enc, width, height, quality); + if (err != VP8_ENC_OK) goto Error; } // In case image is not packed. if (enc->argb_ == NULL) { - const size_t image_size = height * enc->width_; - err = AllocateEncodeBuffer(enc, height, enc->width_); + const size_t image_size = height * width; + err = AllocateTransformBuffer(enc, height, width); if (err != VP8_ENC_OK) goto Error; memcpy(enc->argb_, picture->argb, image_size * sizeof(*enc->argb_)); + enc->current_width_ = width; } // --------------------------------------------------------------------------- // Apply transforms and write transform data. - if (!EvalAndApplySubtractGreen(&bw, enc, enc->width_, height)) { + if (!EvalAndApplySubtractGreen(&bw, enc, enc->current_width_, height)) { err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; } if (enc->use_predict_) { - if (!ApplyPredictFilter(&bw, enc, enc->width_, height, quality)) { + if (!ApplyPredictFilter(&bw, enc, enc->current_width_, height, quality)) { err = VP8_ENC_ERROR_INVALID_CONFIGURATION; goto Error; } } if (enc->use_cross_color_) { - if (!ApplyCrossColorFilter(&bw, enc, enc->width_, height, quality)) { + if (!ApplyCrossColorFilter(&bw, enc, enc->current_width_, height, + quality)) { err = VP8_ENC_ERROR_INVALID_CONFIGURATION; goto Error; } + use_color_cache = 0; } if (use_color_cache) { if (quality > 25) { - if (!VP8LCalculateEstimateForPaletteSize(enc->argb_, enc->width_, height, - &cache_bits)) { + if (!VP8LCalculateEstimateForPaletteSize(enc->argb_, enc->current_width_, + height, &cache_bits)) { err = VP8_ENC_ERROR_INVALID_CONFIGURATION; goto Error; } @@ -488,7 +509,7 @@ int VP8LEncodeImage(const WebPConfig* const config, // --------------------------------------------------------------------------- // Encode and write the transformed image. - ok = EncodeImageInternal(&bw, enc->argb_, enc->width_, height, + ok = EncodeImageInternal(&bw, enc->argb_, enc->current_width_, height, quality, cache_bits, enc->histo_bits_); if (!ok) goto Error; diff --git a/src/enc/vp8li.h b/src/enc/vp8li.h index d02e5dba..b579dfdf 100644 --- a/src/enc/vp8li.h +++ b/src/enc/vp8li.h @@ -37,7 +37,7 @@ typedef struct { uint32_t* argb_; // Transformed argb image data. uint32_t* transform_data_; // Scratch memory for transform data. - int width_; // Packed image width. + int current_width_; // Corresponds to packed image width. // Encoding parameters derived from quality parameter. int use_lz77_; From 6b38378acbe8f72aaf18a3caa5fcb41b89cad4df Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Wed, 11 Apr 2012 09:52:13 +0000 Subject: [PATCH 08/46] Guard the lossless encoder (in flux) under a flag Change-Id: I6dd8fd17089c199001c06b1afde14233dc3e3234 --- src/dsp/lossless.c | 13 ++++++++++++- src/dsp/lossless.h | 11 ++++++++--- src/enc/backward_references.c | 4 ++++ src/enc/backward_references.h | 4 ++++ src/enc/config.c | 5 +++++ src/enc/histogram.c | 3 +++ src/enc/histogram.h | 4 ++++ src/enc/picture.c | 16 ++++++++++++++++ src/enc/vp8l.c | 4 ++++ src/enc/vp8li.h | 4 ++++ src/enc/webpenc.c | 4 ++++ src/utils/bit_writer.c | 2 ++ src/utils/bit_writer.h | 2 ++ src/utils/color_cache.h | 2 ++ src/utils/huffman_encode.c | 4 ++++ src/utils/huffman_encode.h | 4 ++++ 16 files changed, 82 insertions(+), 4 deletions(-) diff --git a/src/dsp/lossless.c b/src/dsp/lossless.c index cc193bdd..f5da54ba 100644 --- a/src/dsp/lossless.c +++ b/src/dsp/lossless.c @@ -19,8 +19,10 @@ extern "C" { #include #include "./lossless.h" #include "../dec/vp8li.h" -#include "../enc/histogram.h" +#ifdef USE_LOSSLESS_ENCODER + +#include "../enc/histogram.h" // A lookup table for small values of log(int) to be used in entropy // computation. @@ -122,6 +124,8 @@ double VP8LFastLog(int v) { return log(v); } +#endif + //------------------------------------------------------------------------------ // Image transforms. @@ -288,6 +292,7 @@ static const PredictorFunc kPredictors[16] = { Predictor0, Predictor0 // <- padding security sentinels }; +#ifdef USE_LOSSLESS_ENCODER // TODO(vikasa): Replace 256 etc with defines. static double PredictionCostSpatial(const int* counts, int weight_0, double exp_val) { @@ -472,6 +477,8 @@ void VP8LResidualImage(int width, int height, int bits, } } +#endif + // Inverse prediction. static void PredictorInverseTransform(const VP8LTransform* const transform, int y_start, int y_end, uint32_t* data) { @@ -524,6 +531,7 @@ static void PredictorInverseTransform(const VP8LTransform* const transform, } } +#ifdef USE_LOSSLESS_ENCODER void VP8LSubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixs) { int i; for (i = 0; i < num_pixs; ++i) { @@ -534,6 +542,7 @@ void VP8LSubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixs) { argb_data[i] = (argb & 0xff00ff00) | (new_r << 16) | new_b; } } +#endif // Add green to blue and red channels (i.e. perform the inverse transform of // 'subtract green'). @@ -606,6 +615,7 @@ static WEBP_INLINE uint32_t TransformColor(const Multipliers* const m, return (argb & 0xff00ff00u) | (new_red << 16) | (new_blue); } +#ifdef USE_LOSSLESS_ENCODER static WEBP_INLINE int SkipRepeatedPixels(const uint32_t* const argb, int ix, int xsize) { const uint32_t v = argb[ix]; @@ -844,6 +854,7 @@ void VP8LColorSpaceTransform(int width, int height, int bits, int step, } } } +#endif // Color space inverse transform. static void ColorSpaceInverseTransform(const VP8LTransform* const transform, diff --git a/src/dsp/lossless.h b/src/dsp/lossless.h index 7ec5cae4..2ea4e31a 100644 --- a/src/dsp/lossless.h +++ b/src/dsp/lossless.h @@ -33,6 +33,7 @@ void VP8LInverseTransform(const struct VP8LTransform* const transform, int row_start, int row_end, uint32_t* const data_in, uint32_t* const data_out); +#ifdef USE_LOSSLESS_ENCODER // Subtracts green from blue and red channels. void VP8LSubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixs); @@ -41,6 +42,8 @@ void VP8LResidualImage(int width, int height, int bits, void VP8LColorSpaceTransform(int width, int height, int bits, int step, uint32_t* const argb, uint32_t* image); +#endif + //------------------------------------------------------------------------------ // Color space conversion. @@ -52,21 +55,23 @@ void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels, //------------------------------------------------------------------------------ // Misc methods. -// Faster logarithm for small integers, with the property of log(0) == 0. -double VP8LFastLog(int v); - // Computes sampled size of 'size' when sampling using 'sampling bits'. static WEBP_INLINE uint32_t VP8LSubSampleSize(uint32_t size, uint32_t sampling_bits) { return (size + (1 << sampling_bits) - 1) >> sampling_bits; } +#ifdef USE_LOSSLESS_ENCODER +// Faster logarithm for small integers, with the property of log(0) == 0. +double VP8LFastLog(int v); + // In-place difference of each component with mod 256. static WEBP_INLINE uint32_t VP8LSubPixels(uint32_t a, uint32_t b) { const uint32_t alpha_and_green = (a & 0xff00ff00u) - (b & 0xff00ff00u); const uint32_t red_and_blue = (a & 0x00ff00ffu) - (b & 0x00ff00ffu); return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); } +#endif //------------------------------------------------------------------------------ diff --git a/src/enc/backward_references.c b/src/enc/backward_references.c index 7ed673be..ee233d46 100644 --- a/src/enc/backward_references.c +++ b/src/enc/backward_references.c @@ -8,6 +8,8 @@ // Author: Jyrki Alakuijala (jyrki@google.com) // +#ifdef USE_LOSSLESS_ENCODER + #include #include #include @@ -785,3 +787,5 @@ Error: free(stream); return ok; } + +#endif diff --git a/src/enc/backward_references.h b/src/enc/backward_references.h index b8d05e6b..49cca9a7 100644 --- a/src/enc/backward_references.h +++ b/src/enc/backward_references.h @@ -11,6 +11,8 @@ #ifndef WEBP_ENC_BACKWARD_REFERENCES_H_ #define WEBP_ENC_BACKWARD_REFERENCES_H_ +#ifdef USE_LOSSLESS_ENCODER + #include #include @@ -231,4 +233,6 @@ int VP8LCalculateEstimateForPaletteSize(const uint32_t *argb, } #endif +#endif + #endif // WEBP_ENC_BACKWARD_REFERENCES_H_ diff --git a/src/enc/config.c b/src/enc/config.c index 5e1b2540..178d4689 100644 --- a/src/enc/config.c +++ b/src/enc/config.c @@ -117,8 +117,13 @@ int WebPValidateConfig(const WebPConfig* const config) { return 0; if (config->alpha_quality < 0 || config->alpha_quality > 100) return 0; +#ifdef USE_LOSSLESS_ENCODER if (config->lossless < 0 || config->lossless > 1) return 0; +#else + if (config->lossless != 0) + return 0; +#endif return 1; } diff --git a/src/enc/histogram.c b/src/enc/histogram.c index 6ef92942..5c08f97e 100644 --- a/src/enc/histogram.c +++ b/src/enc/histogram.c @@ -8,6 +8,7 @@ // Author: Jyrki Alakuijala (jyrki@google.com) // +#ifdef USE_LOSSLESS_ENCODER #include #include @@ -392,3 +393,5 @@ void VP8LHistogramRefine(VP8LHistogram** raw, int raw_size, VP8LHistogramAdd(out[symbols[i]], raw[i]); } } + +#endif diff --git a/src/enc/histogram.h b/src/enc/histogram.h index 533f5e83..7ce29f7b 100644 --- a/src/enc/histogram.h +++ b/src/enc/histogram.h @@ -12,6 +12,8 @@ #ifndef WEBP_ENC_HISTOGRAM_H_ #define WEBP_ENC_HISTOGRAM_H_ +#ifdef USE_LOSSLESS_ENCODER + #include #include #include @@ -146,4 +148,6 @@ void VP8LHistogramRefine(VP8LHistogram** raw, } #endif +#endif + #endif // WEBP_ENC_HISTOGRAM_H_ diff --git a/src/enc/picture.c b/src/enc/picture.c index 75fc9038..68a3bd6e 100644 --- a/src/enc/picture.c +++ b/src/enc/picture.c @@ -105,6 +105,7 @@ int WebPPictureAlloc(WebPPicture* const picture) { mem += uv0_size; } } else { +#ifdef USE_LOSSLESS_ENCODER const uint64_t argb_size = (uint64_t)width * height; const uint64_t total_size = argb_size * sizeof(*picture->argb); if (width <= 0 || height <= 0 || @@ -116,6 +117,9 @@ int WebPPictureAlloc(WebPPicture* const picture) { picture->argb = (uint32_t*)malloc(total_size); if (picture->argb == NULL) return 0; picture->argb_stride = width; +#else + return 0; +#endif } } return 1; @@ -129,14 +133,18 @@ static void WebPPictureGrabSpecs(const WebPPicture* const src, dst->y = dst->u = dst->v = NULL; dst->u0 = dst->v0 = NULL; dst->a = NULL; +#ifdef USE_LOSSLESS_ENCODER dst->argb = NULL; +#endif } // Release memory owned by 'picture'. void WebPPictureFree(WebPPicture* const picture) { if (picture != NULL) { free(picture->y); +#ifdef USE_LOSSLESS_ENCODER free(picture->argb); +#endif WebPPictureGrabSpecs(NULL, picture); } } @@ -185,9 +193,13 @@ int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst) { } #endif } else { +#ifdef USE_LOSSLESS_ENCODER CopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, (uint8_t*)dst->argb, 4 * dst->argb_stride, 4 * dst->width, dst->height); +#else + return 0; +#endif } return 1; } @@ -524,6 +536,7 @@ static int Import(WebPPicture* const picture, } } } else { +#ifdef USE_LOSSLESS_ENCODER if (!import_alpha) { for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { @@ -551,6 +564,9 @@ static int Import(WebPPicture* const picture, } } } +#else + return 0; +#endif } return 1; } diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 0aeb8ea2..f21cc42f 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -10,6 +10,8 @@ // Author: Vikas Arora (vikaas.arora@gmail.com) // +#ifdef USE_LOSSLESS_ENCODER + #include #include #include @@ -535,3 +537,5 @@ int VP8LEncodeImage(const WebPConfig* const config, #if defined(__cplusplus) || defined(c_plusplus) } // extern "C" #endif + +#endif diff --git a/src/enc/vp8li.h b/src/enc/vp8li.h index b579dfdf..3a6ab927 100644 --- a/src/enc/vp8li.h +++ b/src/enc/vp8li.h @@ -12,6 +12,8 @@ #ifndef WEBP_ENC_VP8LI_H_ #define WEBP_ENC_VP8LI_H_ +#ifdef USE_LOSSLESS_ENCODER + #include "./histogram.h" #include "../webp/encode.h" #include "../utils/bit_writer.h" @@ -70,4 +72,6 @@ int VP8LEncodeImage(const WebPConfig* const config, } // extern "C" #endif +#endif + #endif /* WEBP_ENC_VP8LI_H_ */ diff --git a/src/enc/webpenc.c b/src/enc/webpenc.c index dc183333..dbe9045e 100644 --- a/src/enc/webpenc.c +++ b/src/enc/webpenc.c @@ -364,10 +364,14 @@ int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) { } DeleteVP8Encoder(enc); } else { +#ifdef USE_LOSSLESS_ENCODER if (pic->argb == NULL) return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem. +#else + return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION); +#endif } return ok; diff --git a/src/utils/bit_writer.c b/src/utils/bit_writer.c index 04c0db63..bc6e4097 100644 --- a/src/utils/bit_writer.c +++ b/src/utils/bit_writer.c @@ -187,6 +187,7 @@ void VP8BitWriterWipeOut(VP8BitWriter* const bw) { } } +#ifdef USE_LOSSLESS_ENCODER //------------------------------------------------------------------------------ // VP8LBitWriter @@ -264,6 +265,7 @@ void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits) { } } +#endif //------------------------------------------------------------------------------ diff --git a/src/utils/bit_writer.h b/src/utils/bit_writer.h index f7ca0849..b9cf0b92 100644 --- a/src/utils/bit_writer.h +++ b/src/utils/bit_writer.h @@ -64,6 +64,7 @@ static WEBP_INLINE size_t VP8BitWriterSize(const VP8BitWriter* const bw) { return bw->pos_; } +#ifdef USE_LOSSLESS_ENCODER //------------------------------------------------------------------------------ // VP8LBitWriter // TODO(vikasa): VP8LBitWriter is copied as-is from lossless code. There's scope @@ -115,6 +116,7 @@ void VP8LBitWriterDestroy(VP8LBitWriter* const bw); void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits); //------------------------------------------------------------------------------ +#endif #if defined(__cplusplus) || defined(c_plusplus) } // extern "C" diff --git a/src/utils/color_cache.h b/src/utils/color_cache.h index aece1453..bd83f251 100644 --- a/src/utils/color_cache.h +++ b/src/utils/color_cache.h @@ -39,6 +39,7 @@ static WEBP_INLINE void VP8LColorCacheInsert(const VP8LColorCache* const cc, cc->colors_[key] = argb; } +#ifdef USE_LOSSLESS_ENCODER static WEBP_INLINE int VP8LColorCacheGetIndex(const VP8LColorCache* const cc, uint32_t argb) { return (kHashMul * argb) >> cc->hash_shift_; @@ -49,6 +50,7 @@ static WEBP_INLINE int VP8LColorCacheContains(const VP8LColorCache* const cc, const uint32_t key = (kHashMul * argb) >> cc->hash_shift_; return cc->colors_[key] == argb; } +#endif //------------------------------------------------------------------------------ diff --git a/src/utils/huffman_encode.c b/src/utils/huffman_encode.c index 405ead37..a5e29168 100644 --- a/src/utils/huffman_encode.c +++ b/src/utils/huffman_encode.c @@ -9,6 +9,8 @@ // // Flate like entropy encoding (Huffman) for webp lossless. +#ifdef USE_LOSSLESS_ENCODER + #include "./huffman_encode.h" #include @@ -311,3 +313,5 @@ void ConvertBitDepthsToSymbols(const uint8_t* depth, int len, } } } + +#endif diff --git a/src/utils/huffman_encode.h b/src/utils/huffman_encode.h index fa484b7a..fe85cbc2 100644 --- a/src/utils/huffman_encode.h +++ b/src/utils/huffman_encode.h @@ -12,6 +12,8 @@ #ifndef WEBP_UTILS_HUFFMAN_ENCODE_H_ #define WEBP_UTILS_HUFFMAN_ENCODE_H_ +#ifdef USE_LOSSLESS_ENCODER + #include #if defined(__cplusplus) || defined(c_plusplus) @@ -49,4 +51,6 @@ void ConvertBitDepthsToSymbols(const uint8_t* depth, int len, uint16_t* bits); } #endif +#endif + #endif // WEBP_UTILS_HUFFMAN_ENCODE_H_ From 84547f540c2e4824ea9e4af22745d1e38ced6a2b Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Thu, 12 Apr 2012 11:31:17 +0000 Subject: [PATCH 09/46] Add EncodeImageInternal() method. Most of changes in enc/vp8l.c is cherry-picked from src/lossless/encode.c Change-Id: I27938cb2590eccbfe1db0a454343e856bd483e75 --- src/enc/backward_references.c | 20 +- src/enc/backward_references.h | 2 +- src/enc/vp8l.c | 731 +++++++++++++++++++++++++++++++++- src/utils/color_cache.c | 9 +- src/utils/color_cache.h | 3 + src/utils/huffman_encode.c | 21 +- src/utils/huffman_encode.h | 17 +- 7 files changed, 760 insertions(+), 43 deletions(-) diff --git a/src/enc/backward_references.c b/src/enc/backward_references.c index ee233d46..875ecc5b 100644 --- a/src/enc/backward_references.c +++ b/src/enc/backward_references.c @@ -304,7 +304,7 @@ int VP8LBackwardReferencesHashChain(int xsize, int ysize, int use_palette, Error: VP8LHashChain_Delete(hash_chain); free(hash_chain); - VP8LColorCacheDelete(&hashers); + VP8LColorCacheClear(&hashers); return ok; } @@ -506,7 +506,7 @@ Error: free(hash_chain); free(cost_model); free(cost); - VP8LColorCacheDelete(&hashers); + VP8LColorCacheClear(&hashers); return ok; } @@ -597,7 +597,7 @@ Error: if (hash_chain) { free(hash_chain); } - VP8LColorCacheDelete(&hashers); + VP8LColorCacheClear(&hashers); return ok; } @@ -664,7 +664,7 @@ int VP8LVerifyBackwardReferences(const uint32_t* argb, int xsize, int ysize, if (argb[num_pixels] != PixOrCopyArgb(&lit[i])) { printf("i %d, pixel %d, original: 0x%08x, literal: 0x%08x\n", i, num_pixels, argb[num_pixels], PixOrCopyArgb(&lit[i])); - VP8LColorCacheDelete(&hashers); + VP8LColorCacheClear(&hashers); return 0; } VP8LColorCacheInsert(&hashers, argb[num_pixels]); @@ -677,7 +677,7 @@ int VP8LVerifyBackwardReferences(const uint32_t* argb, int xsize, int ysize, "palette_entry: 0x%08x\n", i, num_pixels, argb[num_pixels], PixOrCopyPaletteIx(&lit[i]), palette_entry); - VP8LColorCacheDelete(&hashers); + VP8LColorCacheClear(&hashers); return 0; } VP8LColorCacheInsert(&hashers, argb[num_pixels]); @@ -686,7 +686,7 @@ int VP8LVerifyBackwardReferences(const uint32_t* argb, int xsize, int ysize, int k; if (PixOrCopyDistance(&lit[i]) == 0) { printf("Bw reference with zero distance.\n"); - VP8LColorCacheDelete(&hashers); + VP8LColorCacheClear(&hashers); return 0; } for (k = 0; k < lit[i].len; ++k) { @@ -696,7 +696,7 @@ int VP8LVerifyBackwardReferences(const uint32_t* argb, int xsize, int ysize, i, num_pixels, argb[num_pixels], argb[num_pixels - PixOrCopyDistance(&lit[i])], PixOrCopyDistance(&lit[i])); - VP8LColorCacheDelete(&hashers); + VP8LColorCacheClear(&hashers); return 0; } VP8LColorCacheInsert(&hashers, argb[num_pixels]); @@ -708,11 +708,11 @@ int VP8LVerifyBackwardReferences(const uint32_t* argb, int xsize, int ysize, const int pix_count = xsize * ysize; if (num_pixels != pix_count) { printf("verify failure: %d != %d\n", num_pixels, pix_count); - VP8LColorCacheDelete(&hashers); + VP8LColorCacheClear(&hashers); return 0; } } - VP8LColorCacheDelete(&hashers); + VP8LColorCacheClear(&hashers); return 1; } @@ -749,7 +749,7 @@ static int ComputePaletteHistogram(const uint32_t* argb, int xsize, int ysize, assert(pixel_index == xsize * ysize); (void)xsize; // xsize is not used in non-debug compilations otherwise. (void)ysize; // ysize is not used in non-debug compilations otherwise. - VP8LColorCacheDelete(&hashers); + VP8LColorCacheClear(&hashers); return 1; } diff --git a/src/enc/backward_references.h b/src/enc/backward_references.h index 49cca9a7..f82ee58a 100644 --- a/src/enc/backward_references.h +++ b/src/enc/backward_references.h @@ -59,7 +59,7 @@ static WEBP_INLINE int BitsLog2Floor(uint32_t n) { } #endif -static WEBP_INLINE int BitsLog2Ceiling(uint32_t n) { +static WEBP_INLINE int VP8LBitsLog2Ceiling(uint32_t n) { int floor = BitsLog2Floor(n); if (n == (n & ~(n - 1))) // zero or a power of two. return floor; diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index f21cc42f..2cf64401 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -21,6 +21,7 @@ #include "./vp8li.h" #include "../dsp/lossless.h" #include "../utils/bit_writer.h" +#include "../utils/huffman_encode.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -164,18 +165,728 @@ static void BundleColorMap(const uint32_t* const argb, } } +static int GetBackwardReferences(int width, int height, + const uint32_t* argb, + int quality, int use_color_cache, + int cache_bits, int use_2d_locality, + PixOrCopy** backward_refs, + int* backward_refs_size) { + int ok = 0; + // Backward Reference using LZ77. + int lz77_is_useful; + int backward_refs_rle_size; + int backward_refs_lz77_size; + const int num_pix = width * height; + VP8LHistogram* histo_rle; + PixOrCopy* backward_refs_lz77 = (PixOrCopy*) + malloc(num_pix * sizeof(*backward_refs_lz77)); + PixOrCopy* backward_refs_rle = (PixOrCopy*) + malloc(num_pix * sizeof(*backward_refs_lz77)); + VP8LHistogram* histo_lz77 = (VP8LHistogram*)malloc(2 * sizeof(*histo_lz77)); + if (backward_refs_lz77 == NULL || backward_refs_rle == NULL || + histo_lz77 == NULL) { + free(backward_refs_lz77); + free(backward_refs_rle); + goto End; + } + *backward_refs = NULL; + histo_rle = histo_lz77 + 1; + + if (!VP8LBackwardReferencesHashChain(width, height, use_color_cache, + argb, cache_bits, quality, + backward_refs_lz77, + &backward_refs_lz77_size)) { + goto End; + } + VP8LHistogramInit(histo_lz77, cache_bits); + VP8LHistogramCreate(histo_lz77, backward_refs_lz77, backward_refs_lz77_size); + + // Backward Reference using RLE only. + VP8LBackwardReferencesRle(width, height, argb, backward_refs_rle, + &backward_refs_rle_size); + + VP8LHistogramInit(histo_rle, cache_bits); + VP8LHistogramCreate(histo_rle, backward_refs_rle, backward_refs_rle_size); + + // Check if LZ77 is useful. + lz77_is_useful = (VP8LHistogramEstimateBits(histo_rle) > + VP8LHistogramEstimateBits(histo_lz77)); + + // Choose appropriate backward reference. + if (quality >= 50 && lz77_is_useful) { + const int recursion_level = (num_pix < 320 * 200) ? 1 : 0; + PixOrCopy* const backward_refs_trace = + (PixOrCopy*)malloc(num_pix * sizeof(*backward_refs_trace)); + int backward_refs_trace_size; + free(backward_refs_rle); + free(backward_refs_lz77); + if (backward_refs_trace == NULL || + !VP8LBackwardReferencesTraceBackwards(width, height, + recursion_level, use_color_cache, + argb, cache_bits, + backward_refs_trace, + &backward_refs_trace_size)) { + free(backward_refs_trace); + goto End; + } + *backward_refs = backward_refs_trace; + *backward_refs_size = backward_refs_trace_size; + } else { + if (lz77_is_useful) { + *backward_refs = backward_refs_lz77; + *backward_refs_size = backward_refs_lz77_size; + free(backward_refs_rle); + } else { + *backward_refs = backward_refs_rle; + *backward_refs_size = backward_refs_rle_size; + free(backward_refs_lz77); + } + } + + if (use_2d_locality) { + // Use backward reference with 2D locality. + VP8LBackwardReferences2DLocality(width, *backward_refs_size, + *backward_refs); + } + ok = 1; + +End: + free(histo_lz77); + if (!ok) { + free(*backward_refs); + *backward_refs = NULL; + } + return ok; +} + +static void DeleteHistograms(int size, VP8LHistogram** histograms) { + if (histograms != NULL) { + int i; + for (i = 0; i < size; ++i) { + free(histograms[i]); + } + free(histograms); + } +} + +static int GetHistImageSymbols(int xsize, int ysize, + PixOrCopy* backward_refs, + int backward_refs_size, + int quality, int histogram_bits, + int cache_bits, + VP8LHistogram*** histogram_image, + int* histogram_image_size, + uint32_t* histogram_symbols) { + // Build histogram image. + int ok = 0; + int i; + int histogram_image_raw_size; + VP8LHistogram** histogram_image_raw = NULL; + + *histogram_image = 0; + if (!VP8LHistogramBuildImage(xsize, ysize, histogram_bits, cache_bits, + backward_refs, backward_refs_size, + &histogram_image_raw, + &histogram_image_raw_size)) { + goto Error; + } + // Collapse similar histograms. + if (!VP8LHistogramCombine(histogram_image_raw, histogram_image_raw_size, + quality, histogram_image, histogram_image_size)) { + goto Error; + } + // Refine histogram image. + for (i = 0; i < histogram_image_raw_size; ++i) { + histogram_symbols[i] = -1; + } + VP8LHistogramRefine(histogram_image_raw, histogram_image_raw_size, + histogram_symbols, *histogram_image_size, + *histogram_image); + ok = 1; + +Error: + if (!ok) { + DeleteHistograms(*histogram_image_size, *histogram_image); + } + DeleteHistograms(histogram_image_raw_size, histogram_image_raw); + return ok; +} + +// Heuristics for selecting the stride ranges to collapse. +static int ValuesShouldBeCollapsedToStrideAverage(int a, int b) { + return abs(a - b) < 4; +} + +// Change the population counts in a way that the consequent +// Hufmann tree compression, especially its rle-part will be more +// likely to compress this data more efficiently. +// +// length contains the size of the histogram. +// data contains the population counts. +static int OptimizeHuffmanForRle(int length, int* counts) { + int stride; + int limit; + int sum; + uint8_t* good_for_rle; + // 1) Let's make the Huffman code more compatible with rle encoding. + int i; + for (; length >= 0; --length) { + if (length == 0) { + return 1; // All zeros. + } + if (counts[length - 1] != 0) { + // Now counts[0..length - 1] does not have trailing zeros. + break; + } + } + // 2) Let's mark all population counts that already can be encoded + // with an rle code. + good_for_rle = (uint8_t*)calloc(length, 1); + if (good_for_rle == NULL) { + return 0; + } + { + // Let's not spoil any of the existing good rle codes. + // Mark any seq of 0's that is longer as 5 as a good_for_rle. + // Mark any seq of non-0's that is longer as 7 as a good_for_rle. + int symbol = counts[0]; + int stride = 0; + for (i = 0; i < length + 1; ++i) { + if (i == length || counts[i] != symbol) { + if ((symbol == 0 && stride >= 5) || + (symbol != 0 && stride >= 7)) { + int k; + for (k = 0; k < stride; ++k) { + good_for_rle[i - k - 1] = 1; + } + } + stride = 1; + if (i != length) { + symbol = counts[i]; + } + } else { + ++stride; + } + } + } + // 3) Let's replace those population counts that lead to more rle codes. + stride = 0; + limit = counts[0]; + sum = 0; + for (i = 0; i < length + 1; ++i) { + if (i == length || good_for_rle[i] || + (i != 0 && good_for_rle[i - 1]) || + !ValuesShouldBeCollapsedToStrideAverage(counts[i], limit)) { + if (stride >= 4 || (stride >= 3 && sum == 0)) { + int k; + // The stride must end, collapse what we have, if we have enough (4). + int count = (sum + stride / 2) / stride; + if (count < 1) { + count = 1; + } + if (sum == 0) { + // Don't make an all zeros stride to be upgraded to ones. + count = 0; + } + for (k = 0; k < stride; ++k) { + // We don't want to change value at counts[i], + // that is already belonging to the next stride. Thus - 1. + counts[i - k - 1] = count; + } + } + stride = 0; + sum = 0; + if (i < length - 3) { + // All interesting strides have a count of at least 4, + // at least when non-zeros. + limit = (counts[i] + counts[i + 1] + + counts[i + 2] + counts[i + 3] + 2) / 4; + } else if (i < length) { + limit = counts[i]; + } else { + limit = 0; + } + } + ++stride; + if (i != length) { + sum += counts[i]; + if (stride >= 4) { + limit = (sum + stride / 2) / stride; + } + } + } + free(good_for_rle); + return 1; +} + +static int GetHuffBitLengthsAndCodes( + int histogram_image_size, VP8LHistogram** histogram_image, + int use_color_cache, int** bit_length_sizes, + uint16_t*** bit_codes, uint8_t*** bit_lengths) { + int i, k; + int ok = 1; + for (i = 0; i < histogram_image_size; ++i) { + const int num_literals = VP8LHistogramNumCodes(histogram_image[i]); + k = 0; + (*bit_length_sizes)[5 * i] = num_literals; + (*bit_lengths)[5 * i] = (uint8_t*)calloc(num_literals, 1); + (*bit_codes)[5 * i] = (uint16_t*) + malloc(num_literals * sizeof(*(*bit_codes)[5 * i])); + if ((*bit_lengths)[5 * i] == NULL || (*bit_codes)[5 * i] == NULL) { + ok = 0; + goto Error; + } + + // For each component, optimize histogram for Huffman with RLE compression. + ok = ok && OptimizeHuffmanForRle(num_literals, + histogram_image[i]->literal_); + if (!use_color_cache) { + // Implies that palette_bits == 0, + // and so number of palette entries = (1 << 0) = 1. + // Optimization might have smeared population count in this single + // palette entry, so zero it out. + histogram_image[i]->literal_[256 + kLengthCodes] = 0; + } + ok = ok && OptimizeHuffmanForRle(256, histogram_image[i]->red_); + ok = ok && OptimizeHuffmanForRle(256, histogram_image[i]->blue_); + ok = ok && OptimizeHuffmanForRle(256, histogram_image[i]->alpha_); + ok = ok && OptimizeHuffmanForRle(DISTANCE_CODES_MAX, + histogram_image[i]->distance_); + + // Create a Huffman tree (in the form of bit lengths) for each component. + ok = ok && VP8LCreateHuffmanTree(histogram_image[i]->literal_, num_literals, + 15, (*bit_lengths)[5 * i]); + for (k = 1; k < 5; ++k) { + int val = 256; + if (k == 4) { + val = DISTANCE_CODES_MAX; + } + (*bit_length_sizes)[5 * i + k] = val; + (*bit_lengths)[5 * i + k] = (uint8_t*)calloc(val, 1); + (*bit_codes)[5 * i + k] = (uint16_t*)calloc(val, sizeof(bit_codes[0])); + if ((*bit_lengths)[5 * i + k] == NULL || + (*bit_codes)[5 * i + k] == NULL) { + ok = 0; + goto Error; + } + } + ok = ok && VP8LCreateHuffmanTree(histogram_image[i]->red_, 256, 15, + (*bit_lengths)[5 * i + 1]) && + VP8LCreateHuffmanTree(histogram_image[i]->blue_, 256, 15, + (*bit_lengths)[5 * i + 2]) && + VP8LCreateHuffmanTree(histogram_image[i]->alpha_, 256, 15, + (*bit_lengths)[5 * i + 3]) && + VP8LCreateHuffmanTree(histogram_image[i]->distance_, + DISTANCE_CODES_MAX, 15, + (*bit_lengths)[5 * i + 4]); + // Create the actual bit codes for the bit lengths. + for (k = 0; k < 5; ++k) { + int ix = 5 * i + k; + VP8LConvertBitDepthsToSymbols((*bit_lengths)[ix], (*bit_length_sizes)[ix], + (*bit_codes)[ix]); + } + } + return ok; + + Error: + { + int idx; + for (idx = 0; idx <= 5 * i + k; ++idx) { + free((*bit_lengths)[idx]); + free((*bit_codes)[idx]); + } + } + return 0; +} + +static void ShiftHistogramImage(uint32_t* image , int image_size) { + int i; + for (i = 0; i < image_size; ++i) { + image[i] <<= 8; + image[i] |= 0xff000000; + } +} + +static int PackLiteralBitLengths(const uint8_t* bit_lengths, + int cache_bits, int use_color_cache, + int* new_length_size, + uint8_t** new_lengths) { + int i; + int num_codes = 256; + const int cache_size = 1 << cache_bits; + *new_length_size = 256 + kLengthCodes; + if (use_color_cache) { + *new_length_size += cache_size; + } + *new_lengths = (uint8_t*)malloc(*new_length_size); + if (*new_lengths == NULL) { + return 0; + } + num_codes += kLengthCodes; + if (use_color_cache) { + num_codes += cache_size; + } + for (i = 0; i < num_codes; ++i) { + (*new_lengths)[i] = bit_lengths[i]; + } + return 1; +} + +static void ClearHuffmanTreeIfOnlyOneSymbol(const int num_symbols, + uint8_t* lengths, + uint16_t* symbols) { + int k; + int count = 0; + for (k = 0; k < num_symbols; ++k) { + if (lengths[k] != 0) ++count; + if (count > 1) return; + } + for (k = 0; k < num_symbols; ++k) { + lengths[k] = 0; + symbols[k] = 0; + } +} + +static void StoreHuffmanTreeOfHuffmanTreeToBitMask( + VP8LBitWriter* const bw, const uint8_t* code_length_bitdepth) { + // RFC 1951 will calm you down if you are worried about this funny sequence. + // This sequence is tuned from that, but more weighted for lower symbol count, + // and more spiking histograms. + int i; + static const uint8_t kStorageOrder[CODE_LENGTH_CODES] = { + 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + }; + // Throw away trailing zeros: + int codes_to_store = sizeof(kStorageOrder); + for (; codes_to_store > 4; --codes_to_store) { + if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) { + break; + } + } + // How many code length codes we write above the first four (see RFC 1951). + VP8LWriteBits(bw, 4, codes_to_store - 4); + for (i = 0; i < codes_to_store; ++i) { + VP8LWriteBits(bw, 3, code_length_bitdepth[kStorageOrder[i]]); + } +} + +static void StoreHuffmanTreeToBitMask( + VP8LBitWriter* const bw, + const uint8_t* huffman_tree, + const uint8_t* huffman_tree_extra_bits, + const int num_symbols, + const uint8_t* code_length_bitdepth, + const uint16_t* code_length_bitdepth_symbols) { + int i; + for (i = 0; i < num_symbols; ++i) { + const int ix = huffman_tree[i]; + VP8LWriteBits(bw, code_length_bitdepth[ix], + code_length_bitdepth_symbols[ix]); + switch (ix) { + case 16: + VP8LWriteBits(bw, 2, huffman_tree_extra_bits[i]); + break; + case 17: + VP8LWriteBits(bw, 3, huffman_tree_extra_bits[i]); + break; + case 18: + VP8LWriteBits(bw, 7, huffman_tree_extra_bits[i]); + break; + } + } +} + +static int StoreHuffmanCode(VP8LBitWriter* const bw, + uint8_t* bit_lengths, int bit_lengths_size) { + int i; + int ok = 0; + int count = 0; + int symbols[2] = { 0, 0 }; + int huffman_tree_size = 0; + uint8_t code_length_bitdepth[CODE_LENGTH_CODES]; + uint16_t code_length_bitdepth_symbols[CODE_LENGTH_CODES]; + int huffman_tree_histogram[CODE_LENGTH_CODES]; + uint8_t* huffman_tree_extra_bits; + uint8_t* huffman_tree = (uint8_t*)malloc(bit_lengths_size * + (sizeof(*huffman_tree) + + sizeof(*huffman_tree_extra_bits))); + + if (huffman_tree == NULL) goto End; + huffman_tree_extra_bits = + huffman_tree + (bit_lengths_size * sizeof(*huffman_tree)); + + for (i = 0; i < bit_lengths_size; ++i) { + if (bit_lengths[i] != 0) { + if (count < 2) symbols[count] = i; + ++count; + } + } + if (count <= 2) { + int num_bits = 4; + // 0, 1 or 2 symbols to encode. + VP8LWriteBits(bw, 1, 1); + if (count == 0) { + VP8LWriteBits(bw, 3, 0); + ok = 1; + goto End; + } + while (symbols[count - 1] >= (1 << num_bits)) num_bits += 2; + VP8LWriteBits(bw, 3, (num_bits - 4) / 2 + 1); + VP8LWriteBits(bw, 1, count - 1); + for (i = 0; i < count; ++i) { + VP8LWriteBits(bw, num_bits, symbols[i]); + } + ok = 1; + goto End; + } + + VP8LWriteBits(bw, 1, 0); + VP8LCreateCompressedHuffmanTree(bit_lengths, bit_lengths_size, + &huffman_tree_size, huffman_tree, + huffman_tree_extra_bits); + memset(huffman_tree_histogram, 0, sizeof(huffman_tree_histogram)); + for (i = 0; i < huffman_tree_size; ++i) { + ++huffman_tree_histogram[huffman_tree[i]]; + } + memset(code_length_bitdepth, 0, sizeof(code_length_bitdepth)); + memset(code_length_bitdepth_symbols, 0, sizeof(code_length_bitdepth_symbols)); + + if (!VP8LCreateHuffmanTree(huffman_tree_histogram, CODE_LENGTH_CODES, + 7, code_length_bitdepth)) { + goto End; + } + VP8LConvertBitDepthsToSymbols(code_length_bitdepth, CODE_LENGTH_CODES, + code_length_bitdepth_symbols); + StoreHuffmanTreeOfHuffmanTreeToBitMask(bw, code_length_bitdepth); + ClearHuffmanTreeIfOnlyOneSymbol(CODE_LENGTH_CODES, + code_length_bitdepth, + code_length_bitdepth_symbols); + { + int num_trailing_zeros = 0; + int trailing_zero_bits = 0; + int trimmed_length; + int write_length; + int length; + for (i = huffman_tree_size; i > 0; --i) { + int ix = huffman_tree[i - 1]; + if (ix == 0 || ix == 17 || ix == 18) { + ++num_trailing_zeros; + trailing_zero_bits += code_length_bitdepth[ix]; + if (ix == 17) trailing_zero_bits += 3; + if (ix == 18) trailing_zero_bits += 7; + } else { + break; + } + } + trimmed_length = huffman_tree_size - num_trailing_zeros; + write_length = (trimmed_length > 1 && trailing_zero_bits > 12); + length = write_length ? trimmed_length : huffman_tree_size; + VP8LWriteBits(bw, 1, write_length); + if (write_length) { + const int nbits = VP8LBitsLog2Ceiling(trimmed_length - 1); + const int nbitpairs = nbits == 0 ? 1 : (nbits + 1) / 2; + VP8LWriteBits(bw, 3, nbitpairs - 1); + VP8LWriteBits(bw, nbitpairs * 2, trimmed_length - 2); + } + StoreHuffmanTreeToBitMask(bw, huffman_tree, huffman_tree_extra_bits, + length, code_length_bitdepth, + code_length_bitdepth_symbols); + } + ok = 1; + + End: + free(huffman_tree); + return ok; +} + +static void StoreImageToBitMask( + VP8LBitWriter* const bw, int width, int histo_bits, + const PixOrCopy* literals, int literals_size, + const uint32_t* histogram_symbols, + uint8_t** const bitdepths, uint16_t** const bit_symbols) { + // x and y trace the position in the image. + int x = 0; + int y = 0; + const int histo_xsize = histo_bits ? VP8LSubSampleSize(width, histo_bits) : 1; + int i; + for (i = 0; i < literals_size; ++i) { + const PixOrCopy v = literals[i]; + const int histogram_ix = histogram_symbols[histo_bits ? + (y >> histo_bits) * histo_xsize + + (x >> histo_bits) : 0]; + if (PixOrCopyIsPaletteIx(&v)) { + const int code = PixOrCopyPaletteIx(&v); + int literal_ix = 256 + kLengthCodes + code; + VP8LWriteBits(bw, bitdepths[5 * histogram_ix][literal_ix], + bit_symbols[5 * histogram_ix][literal_ix]); + } else if (PixOrCopyIsLiteral(&v)) { + static const int order[] = {1, 2, 0, 3}; + int k; + for (k = 0; k < 4; ++k) { + const int code = PixOrCopyLiteral(&v, order[k]); + VP8LWriteBits(bw, bitdepths[5 * histogram_ix + k][code], + bit_symbols[5 * histogram_ix + k][code]); + } + } else { + int bits, n_bits; + int code, distance; + int len_ix; + PixOrCopyLengthCodeAndBits(&v, &code, &n_bits, &bits); + len_ix = 256 + code; + VP8LWriteBits(bw, bitdepths[5 * histogram_ix][len_ix], + bit_symbols[5 * histogram_ix][len_ix]); + VP8LWriteBits(bw, n_bits, bits); + + distance = PixOrCopyDistance(&v); + PrefixEncode(distance, &code, &n_bits, &bits); + VP8LWriteBits(bw, bitdepths[5 * histogram_ix + 4][code], + bit_symbols[5 * histogram_ix + 4][code]); + VP8LWriteBits(bw, n_bits, bits); + } + x += PixOrCopyLength(&v); + while (x >= width) { + x -= width; + ++y; + } + } +} + static int EncodeImageInternal(VP8LBitWriter* const bw, const uint32_t* const argb, int width, int height, int quality, int cache_bits, int histogram_bits) { - (void)bw; - (void)argb; - (void)width; - (void)height; - (void)quality; - (void)cache_bits; - (void)histogram_bits; - return 1; + int i; + int ok = 0; + int histogram_image_size; + int write_histogram_image; + int* bit_lengths_sizes = NULL; + uint8_t** bit_lengths = NULL; + uint16_t** bit_codes = NULL; + const int use_2d_locality = 1; + int backward_refs_size; + const int use_color_cache = cache_bits ? 1 : 0; + const int histogram_image_xysize = VP8LSubSampleSize(width, histogram_bits) * + VP8LSubSampleSize(height, histogram_bits); + VP8LHistogram** histogram_image; + PixOrCopy* backward_refs; + uint32_t* histogram_symbols = (uint32_t*) + calloc(histogram_image_xysize, sizeof(*histogram_symbols)); + + if (histogram_symbols == NULL) goto Error; + + // Calculate backward references from ARGB image. + if (!GetBackwardReferences(width, height, argb, quality, + use_color_cache, cache_bits, use_2d_locality, + &backward_refs, &backward_refs_size)) { + goto Error; + } + // Build histogram image & symbols from backward references. + if (!GetHistImageSymbols(width, height, backward_refs, backward_refs_size, + quality, histogram_bits, cache_bits, + &histogram_image, &histogram_image_size, + histogram_symbols)) { + goto Error; + } + // Create Huffman bit lengths & codes for each histogram image. + bit_lengths_sizes = (int*)calloc(5 * histogram_image_size, + sizeof(*bit_lengths_sizes)); + bit_lengths = (uint8_t**)calloc(5 * histogram_image_size, + sizeof(*bit_lengths)); + bit_codes = (uint16_t**)calloc(5 * histogram_image_size, + sizeof(*bit_codes)); + if (bit_lengths_sizes == NULL || bit_lengths == NULL || bit_codes == NULL || + !GetHuffBitLengthsAndCodes(histogram_image_size, histogram_image, + use_color_cache, &bit_lengths_sizes, + &bit_codes, &bit_lengths)) { + goto Error; + } + + // Huffman image + meta huffman. + write_histogram_image = (histogram_image_size > 1); + VP8LWriteBits(bw, 1, write_histogram_image); + if (write_histogram_image) { + int nbits; + int image_size_bits; + int num_histograms; + uint32_t* histogram_argb = (uint32_t*) + malloc(histogram_image_xysize * sizeof(*histogram_argb)); + if (histogram_argb == NULL) goto Error; + memcpy(histogram_argb, histogram_symbols, + histogram_image_xysize * sizeof(*histogram_argb)); + + ShiftHistogramImage(histogram_argb, histogram_image_xysize); + VP8LWriteBits(bw, 4, histogram_bits); + if (!EncodeImageInternal(bw, histogram_argb, + VP8LSubSampleSize(width, histogram_bits), + VP8LSubSampleSize(height, histogram_bits), + quality, 0, 0)) { + free(histogram_argb); + goto Error; + } + image_size_bits = VP8LBitsLog2Ceiling(histogram_image_size - 1); + VP8LWriteBits(bw, 4, image_size_bits); + VP8LWriteBits(bw, image_size_bits, histogram_image_size - 2); + num_histograms = 5 * histogram_image_size; + nbits = VP8LBitsLog2Ceiling(num_histograms); + VP8LWriteBits(bw, 4, nbits); + for (i = 0; i < num_histograms; ++i) { + VP8LWriteBits(bw, nbits, i); + } + free(histogram_argb); + } + + // Color Cache parameters. + VP8LWriteBits(bw, 1, use_color_cache); + if (use_color_cache) { + VP8LWriteBits(bw, 4, cache_bits); + } + + // Store Huffman codes. + for (i = 0; i < histogram_image_size; ++i) { + int k; + int literal_lengths_size; + uint8_t* literal_lengths; + // TODO(vikasa): Evaluate and remove the call to PackLiteralBitLengths. + if (!PackLiteralBitLengths(bit_lengths[5 * i], cache_bits, use_color_cache, + &literal_lengths_size, &literal_lengths)) { + goto Error; + } + if (!StoreHuffmanCode(bw, literal_lengths, literal_lengths_size)) { + goto Error; + } + free(literal_lengths); + for (k = 1; k < 5; ++k) { + if (!StoreHuffmanCode(bw, bit_lengths[5 * i + k], + bit_lengths_sizes[5 * i + k])) { + goto Error; + } + } + } + // Free combined histograms. + DeleteHistograms(histogram_image_size, histogram_image); + + // Emit no bits if there is only one symbol in the histogram. + // This gives better compression for some images. + for (i = 0; i < 5 * histogram_image_size; ++i) { + ClearHuffmanTreeIfOnlyOneSymbol(bit_lengths_sizes[i], bit_lengths[i], + bit_codes[i]); + } + // Store actual literals. + StoreImageToBitMask(bw, width, histogram_bits, backward_refs, + backward_refs_size, histogram_symbols, + bit_lengths, bit_codes); + ok = 1; + + Error: + for (i = 0; i < 5 * histogram_image_size; ++i) { + free(bit_lengths[i]); + free(bit_codes[i]); + } + free(bit_lengths_sizes); + free(bit_lengths); + free(bit_codes); + free(histogram_symbols); + return ok; } static int EvalAndApplySubtractGreen(VP8LBitWriter* const bw, @@ -310,7 +1021,6 @@ static WebPEncodingError WriteImage(VP8LEncoder* const enc, static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, WebPPicture* const picture) { VP8LEncoder* enc; - (void)config; enc = (VP8LEncoder*)malloc(sizeof(*enc)); if (enc == NULL) { @@ -319,6 +1029,7 @@ static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, } memset(enc, 0, sizeof(*enc)); + enc->config_ = config; enc->pic_ = picture; enc->use_lz77_ = 1; enc->palette_bits_ = 7; @@ -463,6 +1174,7 @@ int VP8LEncodeImage(const WebPConfig* const config, if (enc->use_palette_) { err = ApplyPalette(&bw, enc, width, height, quality); if (err != VP8_ENC_OK) goto Error; + use_color_cache = 0; } // In case image is not packed. @@ -495,7 +1207,6 @@ int VP8LEncodeImage(const WebPConfig* const config, err = VP8_ENC_ERROR_INVALID_CONFIGURATION; goto Error; } - use_color_cache = 0; } if (use_color_cache) { diff --git a/src/utils/color_cache.c b/src/utils/color_cache.c index 06e8a718..9b1be7a5 100644 --- a/src/utils/color_cache.c +++ b/src/utils/color_cache.c @@ -32,14 +32,17 @@ int VP8LColorCacheInit(VP8LColorCache* const cc, int hash_bits) { return 1; } -void VP8LColorCacheDelete(VP8LColorCache* const cc) { +void VP8LColorCacheClear(VP8LColorCache* const cc) { if (cc != NULL) { free(cc->colors_); - free(cc); } } +void VP8LColorCacheDelete(VP8LColorCache* const cc) { + VP8LColorCacheClear(cc); + free(cc); +} + #if defined(__cplusplus) || defined(c_plusplus) } #endif - diff --git a/src/utils/color_cache.h b/src/utils/color_cache.h index bd83f251..64435b5a 100644 --- a/src/utils/color_cache.h +++ b/src/utils/color_cache.h @@ -59,6 +59,9 @@ static WEBP_INLINE int VP8LColorCacheContains(const VP8LColorCache* const cc, int VP8LColorCacheInit(VP8LColorCache* const color_cache, int hash_bits); // Delete the color cache. +void VP8LColorCacheClear(VP8LColorCache* const color_cache); + +// Delete the color_cache object. void VP8LColorCacheDelete(VP8LColorCache* const color_cache); //------------------------------------------------------------------------------ diff --git a/src/utils/huffman_encode.c b/src/utils/huffman_encode.c index a5e29168..8152898f 100644 --- a/src/utils/huffman_encode.c +++ b/src/utils/huffman_encode.c @@ -70,9 +70,9 @@ static void SetDepth(const HuffmanTree* p, // we are not planning to use this with extremely long blocks. // // See http://en.wikipedia.org/wiki/Huffman_coding -int CreateHuffmanTree(const int* const histogram, int histogram_size, - int tree_depth_limit, - uint8_t* const bit_depths) { +int VP8LCreateHuffmanTree(const int* const histogram, int histogram_size, + int tree_depth_limit, + uint8_t* const bit_depths) { HuffmanTree* tree; HuffmanTree* tree_pool; int tree_pool_size; @@ -241,11 +241,11 @@ static void WriteHuffmanTreeRepetitionsZeros( } } -void CreateCompressedHuffmanTree(const uint8_t* depth, - int depth_size, - int* num_symbols, - uint8_t* tree, - uint8_t* extra_bits_data) { +void VP8LCreateCompressedHuffmanTree(const uint8_t* depth, + int depth_size, + int* num_symbols, + uint8_t* tree, + uint8_t* extra_bits_data) { int prev_value = 8; // 8 is the initial value for rle. int i; for (i = 0; i < depth_size;) { @@ -280,8 +280,8 @@ static uint32_t ReverseBits(int num_bits, uint32_t bits) { return retval; } -void ConvertBitDepthsToSymbols(const uint8_t* depth, int len, - uint16_t* bits) { +void VP8LConvertBitDepthsToSymbols(const uint8_t* depth, int len, + uint16_t* bits) { // This function is based on RFC 1951. // // In deflate, all bit depths are [1..15] @@ -313,5 +313,6 @@ void ConvertBitDepthsToSymbols(const uint8_t* depth, int len, } } } +#undef MAX_BITS #endif diff --git a/src/utils/huffman_encode.h b/src/utils/huffman_encode.h index fe85cbc2..9bd717be 100644 --- a/src/utils/huffman_encode.h +++ b/src/utils/huffman_encode.h @@ -31,21 +31,20 @@ extern "C" { // See http://en.wikipedia.org/wiki/Huffman_coding // // Returns 0 when an error has occured. -int CreateHuffmanTree(const int* data, - const int length, - const int tree_limit, - uint8_t* depth); +int VP8LCreateHuffmanTree(const int* data, const int length, + const int tree_limit, uint8_t* depth); // Write a huffman tree from bit depths into the deflate representation // of a Huffman tree. In deflate, the generated Huffman tree is to be // compressed once more using a Huffman tree. -void CreateCompressedHuffmanTree(const uint8_t* depth, int len, - int* num_symbols, - uint8_t* tree, - uint8_t* extra_bits_data); +void VP8LCreateCompressedHuffmanTree(const uint8_t* depth, int len, + int* num_symbols, + uint8_t* tree, + uint8_t* extra_bits_data); // Get the actual bit values for a tree of bit depths. -void ConvertBitDepthsToSymbols(const uint8_t* depth, int len, uint16_t* bits); +void VP8LConvertBitDepthsToSymbols(const uint8_t* depth, int len, + uint16_t* bits); #if defined(__cplusplus) || defined(c_plusplus) } From b2f99465a7d4eabe5ca21ab55d7604598ecd718b Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Fri, 13 Apr 2012 07:01:11 +0000 Subject: [PATCH 10/46] Fix CopyTileWithPrediction() so that it uses original values of left, top etc for prediction rather than the predicted values of the same. Also, do some renaming in the same to make it more readable. Change-Id: I2fe94e35a6700bd437f5c601e2af12323bf32445 --- src/dsp/lossless.c | 56 ++++++++++++++++++++++++++++------------------ src/dsp/lossless.h | 9 +++++--- src/enc/vp8l.c | 30 ++++++++++++------------- src/enc/vp8li.h | 2 ++ 4 files changed, 57 insertions(+), 40 deletions(-) diff --git a/src/dsp/lossless.c b/src/dsp/lossless.c index f5da54ba..657a67bd 100644 --- a/src/dsp/lossless.c +++ b/src/dsp/lossless.c @@ -398,44 +398,55 @@ static int GetBestPredictorForTile(int tile_x, int tile_y, int max_tile_size, return best_mode; } -static void CopyTileWithPrediction(int xsize, int ysize, +static void CopyTileWithPrediction(int width, int height, int tile_x, int tile_y, int bits, int mode, + uint32_t* const argb_scratch, uint32_t* const argb) { - int ymax = 1 << bits; - int xmax = 1 << bits; - int y; + const int col_start = tile_x << bits; + const int row_start = tile_y << bits; + const int transform_size = 1 << bits; + const int ymax = (transform_size <= height - row_start) ? + transform_size : height - row_start; + const int xmax = (transform_size <= width - col_start) ? + transform_size : width - col_start; const PredictorFunc pred_func = kPredictors[mode]; - if (ymax > ysize - (tile_y << bits)) { - ymax = ysize - (tile_y << bits); - } - if (xmax > xsize - (tile_x << bits)) { - xmax = xsize - (tile_x << bits); - } + uint32_t* const top_row = argb_scratch; + uint32_t* const current_row = argb_scratch + width; + + int y; for (y = 0; y < ymax; ++y) { - const int all_y = (tile_y << bits) + y; int x; + const int row = row_start + y; + // Update current_row & top_row. + if (row > 0) { + memcpy(top_row, current_row, width * sizeof(*top_row)); + } + memcpy(current_row, &argb[row * width], width * sizeof(*current_row)); for (x = 0; x < xmax; ++x) { - const int all_x = (tile_x << bits) + x; - const int ix = all_y * xsize + all_x; + const int col = col_start + x; + const int pix = row * width + col; uint32_t predict; - if (all_y == 0) { - if (all_x == 0) { + if (row == 0) { + if (col == 0) { predict = ARGB_BLACK; } else { - predict = argb[ix - 1]; + const uint32_t left = current_row[col - 1]; + predict = left; } - } else if (all_x == 0) { - predict = argb[ix - xsize]; + } else if (col == 0) { + const uint32_t top = top_row[col]; + predict = top; } else { - predict = pred_func(argb + ix, argb + ix - xsize); + predict = pred_func(argb + pix, top_row + col); } - argb[ix] = VP8LSubPixels(argb[ix], predict); + argb[pix] = VP8LSubPixels(argb[pix], predict); } } } void VP8LResidualImage(int width, int height, int bits, - uint32_t* const argb, uint32_t* const image) { + uint32_t* const argb, uint32_t* const argb_scratch, + uint32_t* const image) { const int max_tile_size = 1 << bits; const int tile_xsize = VP8LSubSampleSize(width, bits); const int tile_ysize = VP8LSubSampleSize(height, bits); @@ -456,7 +467,8 @@ void VP8LResidualImage(int width, int height, int bits, pred = GetBestPredictorForTile(tile_x, tile_y, max_tile_size, width, height, histo, argb); image[tile_y * tile_xsize + tile_x] = 0xff000000u | (pred << 8); - CopyTileWithPrediction(width, height, tile_x, tile_y, bits, pred, argb); + CopyTileWithPrediction(width, height, tile_x, tile_y, bits, pred, + argb_scratch, argb); for (y = 0; y < max_tile_size; ++y) { int ix; int all_x; diff --git a/src/dsp/lossless.h b/src/dsp/lossless.h index 2ea4e31a..26c8ab02 100644 --- a/src/dsp/lossless.h +++ b/src/dsp/lossless.h @@ -38,7 +38,8 @@ void VP8LInverseTransform(const struct VP8LTransform* const transform, void VP8LSubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixs); void VP8LResidualImage(int width, int height, int bits, - uint32_t* const argb, uint32_t* const image); + uint32_t* const argb, uint32_t* const argb_scratch, + uint32_t* const image); void VP8LColorSpaceTransform(int width, int height, int bits, int step, uint32_t* const argb, uint32_t* image); @@ -67,8 +68,10 @@ double VP8LFastLog(int v); // In-place difference of each component with mod 256. static WEBP_INLINE uint32_t VP8LSubPixels(uint32_t a, uint32_t b) { - const uint32_t alpha_and_green = (a & 0xff00ff00u) - (b & 0xff00ff00u); - const uint32_t red_and_blue = (a & 0x00ff00ffu) - (b & 0x00ff00ffu); + const uint32_t alpha_and_green = + 0x00ff00ffu + (a & 0xff00ff00u) - (b & 0xff00ff00u); + const uint32_t red_and_blue = + 0xff00ff00u + (a & 0x00ff00ffu) - (b & 0x00ff00ffu); return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); } #endif diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 2cf64401..2b002722 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -928,7 +928,8 @@ static int ApplyPredictFilter(VP8LBitWriter* const bw, const int transform_width = VP8LSubSampleSize(width, pred_bits); const int transform_height = VP8LSubSampleSize(height, pred_bits); - VP8LResidualImage(width, height, pred_bits, enc->argb_, enc->transform_data_); + VP8LResidualImage(width, height, pred_bits, enc->argb_, enc->argb_scratch_, + enc->transform_data_); VP8LWriteBits(bw, 1, 1); VP8LWriteBits(bw, 2, 0); VP8LWriteBits(bw, 4, pred_bits); @@ -1020,9 +1021,7 @@ static WebPEncodingError WriteImage(VP8LEncoder* const enc, static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, WebPPicture* const picture) { - VP8LEncoder* enc; - - enc = (VP8LEncoder*)malloc(sizeof(*enc)); + VP8LEncoder* enc = (VP8LEncoder*)malloc(sizeof(*enc)); if (enc == NULL) { WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); return NULL; @@ -1034,8 +1033,6 @@ static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, enc->use_lz77_ = 1; enc->palette_bits_ = 7; - enc->argb_ = NULL; - // TODO: Use config.quality to initialize histo_bits_ and transform_bits_. enc->histo_bits_ = 4; enc->transform_bits_ = 4; @@ -1058,25 +1055,28 @@ static void DeleteVP8LEncoder(VP8LEncoder* enc) { free(enc); } -// Allocates the memory for argb (W x H) buffer and transform data. -// Former buffer (argb_) will hold the argb data from successive image -// transformtions and later corresponds to prediction data (uint32) used -// for every image tile corresponding to the transformed argb_. -// The dimension of this square tile is 2^transform_bits_. +// Allocates the memory for argb (W x H) buffer, 2 rows of context for +// prediction and transform data. static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, int height, int width) { WebPEncodingError err = VP8_ENC_OK; const size_t image_size = height * width; + const size_t argb_scratch_size = 2 * width; const size_t transform_data_size = VP8LSubSampleSize(height, enc->transform_bits_) * VP8LSubSampleSize(width, enc->transform_bits_); - const size_t total_size = image_size + transform_data_size; - enc->argb_ = (uint32_t*)malloc(total_size * sizeof(*enc->argb_)); - if (enc->argb_ == NULL) { + const size_t total_size = + image_size + argb_scratch_size + transform_data_size; + uint32_t* mem = (uint32_t*)malloc(total_size * sizeof(*mem)); + if (mem == NULL) { err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; } - enc->transform_data_ = enc->argb_ + image_size; + enc->argb_ = mem; + mem += image_size; + enc->argb_scratch_ = mem; + mem += argb_scratch_size; + enc->transform_data_ = mem; enc->current_width_ = width; Error: diff --git a/src/enc/vp8li.h b/src/enc/vp8li.h index 3a6ab927..a83b3993 100644 --- a/src/enc/vp8li.h +++ b/src/enc/vp8li.h @@ -38,6 +38,8 @@ typedef struct { WebPPicture* pic_; // input picture. uint32_t* argb_; // Transformed argb image data. + uint32_t* argb_scratch_; // Scratch memory for current and top row. + // (used for prediction). uint32_t* transform_data_; // Scratch memory for transform data. int current_width_; // Corresponds to packed image width. From d673b6b9a0368067ce07ca46c8343156290878cb Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Fri, 13 Apr 2012 07:13:15 +0000 Subject: [PATCH 11/46] Change the predictor function to pass left pixel instead of pointer to the source. Change-Id: Ia2c8e17c3140709a825c2f85a88c5e31bd6e462f --- src/dsp/lossless.c | 85 +++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 50 deletions(-) diff --git a/src/dsp/lossless.c b/src/dsp/lossless.c index 657a67bd..7aed1fd1 100644 --- a/src/dsp/lossless.c +++ b/src/dsp/lossless.c @@ -208,82 +208,67 @@ static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) { //------------------------------------------------------------------------------ // Predictors -static uint32_t Predictor0(const uint32_t* const src, - const uint32_t* const top) { +static uint32_t Predictor0(uint32_t left, const uint32_t* const top) { (void)top; - (void)src; + (void)left; return ARGB_BLACK; } -static uint32_t Predictor1(const uint32_t* const src, - const uint32_t* const top) { +static uint32_t Predictor1(uint32_t left, const uint32_t* const top) { (void)top; - return src[-1]; + return left; } -static uint32_t Predictor2(const uint32_t* const src, - const uint32_t* const top) { - (void)src; +static uint32_t Predictor2(uint32_t left, const uint32_t* const top) { + (void)left; return top[0]; } -static uint32_t Predictor3(const uint32_t* const src, - const uint32_t* const top) { - (void)src; +static uint32_t Predictor3(uint32_t left, const uint32_t* const top) { + (void)left; return top[1]; } -static uint32_t Predictor4(const uint32_t* const src, - const uint32_t* const top) { - (void)src; +static uint32_t Predictor4(uint32_t left, const uint32_t* const top) { + (void)left; return top[-1]; } -static uint32_t Predictor5(const uint32_t* const src, - const uint32_t* const top) { - const uint32_t pred = Average3(src[-1], top[0], top[1]); +static uint32_t Predictor5(uint32_t left, const uint32_t* const top) { + const uint32_t pred = Average3(left, top[0], top[1]); return pred; } -static uint32_t Predictor6(const uint32_t* const src, - const uint32_t* const top) { - const uint32_t pred = Average2(src[-1], top[-1]); +static uint32_t Predictor6(uint32_t left, const uint32_t* const top) { + const uint32_t pred = Average2(left, top[-1]); return pred; } -static uint32_t Predictor7(const uint32_t* const src, - const uint32_t* const top) { - const uint32_t pred = Average2(src[-1], top[0]); +static uint32_t Predictor7(uint32_t left, const uint32_t* const top) { + const uint32_t pred = Average2(left, top[0]); return pred; } -static uint32_t Predictor8(const uint32_t* const src, - const uint32_t* const top) { +static uint32_t Predictor8(uint32_t left, const uint32_t* const top) { const uint32_t pred = Average2(top[-1], top[0]); - (void)src; + (void)left; return pred; } -static uint32_t Predictor9(const uint32_t* const src, - const uint32_t* const top) { +static uint32_t Predictor9(uint32_t left, const uint32_t* const top) { const uint32_t pred = Average2(top[0], top[1]); - (void)src; + (void)left; return pred; } -static uint32_t Predictor10(const uint32_t* const src, - const uint32_t* const top) { - const uint32_t pred = Average4(src[-1], top[-1], top[0], top[1]); +static uint32_t Predictor10(uint32_t left, const uint32_t* const top) { + const uint32_t pred = Average4(left, top[-1], top[0], top[1]); return pred; } -static uint32_t Predictor11(const uint32_t* const src, - const uint32_t* const top) { - const uint32_t pred = Select(top[0], src[-1], top[-1]); +static uint32_t Predictor11(uint32_t left, const uint32_t* const top) { + const uint32_t pred = Select(top[0], left, top[-1]); return pred; } -static uint32_t Predictor12(const uint32_t* const src, - const uint32_t* const top) { - const uint32_t pred = ClampedAddSubtractFull(src[-1], top[0], top[-1]); +static uint32_t Predictor12(uint32_t left, const uint32_t* const top) { + const uint32_t pred = ClampedAddSubtractFull(left, top[0], top[-1]); return pred; } -static uint32_t Predictor13(const uint32_t* const src, - const uint32_t* const top) { - const uint32_t pred = ClampedAddSubtractHalf(src[-1], top[0], top[-1]); +static uint32_t Predictor13(uint32_t left, const uint32_t* const top) { + const uint32_t pred = ClampedAddSubtractHalf(left, top[0], top[-1]); return pred; } -typedef uint32_t (*PredictorFunc)(const uint32_t* const src, - const uint32_t* const top); +typedef uint32_t (*PredictorFunc)(uint32_t left, const uint32_t* const top); static const PredictorFunc kPredictors[16] = { Predictor0, Predictor1, Predictor2, Predictor3, Predictor4, Predictor5, Predictor6, Predictor7, @@ -380,7 +365,7 @@ static int GetBestPredictorForTile(int tile_x, int tile_y, int max_tile_size, predict = argb[(all_y - 1) * xsize]; // First Col: Pick Top Element. } else { const uint32_t* src = argb + all_y * xsize + all_x; - predict = pred_func(src, src - xsize); + predict = pred_func(src[-1], src - xsize); } predict_diff = VP8LSubPixels(argb[all_y * xsize + all_x], predict); ++histo[0][predict_diff >> 24]; @@ -437,7 +422,7 @@ static void CopyTileWithPrediction(int width, int height, const uint32_t top = top_row[col]; predict = top; } else { - predict = pred_func(argb + pix, top_row + col); + predict = pred_func(argb[pix - 1], top_row + col); } argb[pix] = VP8LSubPixels(argb[pix], predict); } @@ -497,10 +482,10 @@ static void PredictorInverseTransform(const VP8LTransform* const transform, const int width = transform->xsize_; if (y_start == 0) { // First Row follows the L (mode=1) mode. int x; - const uint32_t pred = Predictor0(data, NULL); + const uint32_t pred = Predictor0(data[-1], NULL); AddPixelsEq(data, pred); for (x = 1; x < width; ++x) { - const uint32_t pred = Predictor1(data + x, NULL); + const uint32_t pred = Predictor1(data[x - 1], NULL); AddPixelsEq(data + x, pred); } data += width; @@ -521,7 +506,7 @@ static void PredictorInverseTransform(const VP8LTransform* const transform, PredictorFunc pred_func; // First pixel follows the T (mode=2) mode. - pred = Predictor2(data, data - width); + pred = Predictor2(data[-1], data - width); AddPixelsEq(data, pred); // .. the rest: @@ -531,7 +516,7 @@ static void PredictorInverseTransform(const VP8LTransform* const transform, if ((x & mask) == 0) { // start of tile. Read predictor function. pred_func = kPredictors[((*pred_mode_src++) >> 8) & 0xf]; } - pred = pred_func(data + x, data + x - width); + pred = pred_func(data[x - 1], data + x - width); AddPixelsEq(data + x, pred); } data += width; From 352a4f49aba02a5e31bc812c91db6d6dc71df482 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Fri, 13 Apr 2012 07:14:16 +0000 Subject: [PATCH 12/46] Get rid of PackLiteralBitLengths() [and in turn a malloc]. Also, a few related const fixes. Change-Id: I229519b1c34d41c78d9ad2403f1e25feab3c9d93 --- src/enc/vp8l.c | 49 +++++++------------------------------- src/utils/huffman_encode.c | 2 +- src/utils/huffman_encode.h | 5 ++-- 3 files changed, 12 insertions(+), 44 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 2b002722..762d0456 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -507,31 +507,6 @@ static void ShiftHistogramImage(uint32_t* image , int image_size) { } } -static int PackLiteralBitLengths(const uint8_t* bit_lengths, - int cache_bits, int use_color_cache, - int* new_length_size, - uint8_t** new_lengths) { - int i; - int num_codes = 256; - const int cache_size = 1 << cache_bits; - *new_length_size = 256 + kLengthCodes; - if (use_color_cache) { - *new_length_size += cache_size; - } - *new_lengths = (uint8_t*)malloc(*new_length_size); - if (*new_lengths == NULL) { - return 0; - } - num_codes += kLengthCodes; - if (use_color_cache) { - num_codes += cache_size; - } - for (i = 0; i < num_codes; ++i) { - (*new_lengths)[i] = bit_lengths[i]; - } - return 1; -} - static void ClearHuffmanTreeIfOnlyOneSymbol(const int num_symbols, uint8_t* lengths, uint16_t* symbols) { @@ -597,7 +572,8 @@ static void StoreHuffmanTreeToBitMask( } static int StoreHuffmanCode(VP8LBitWriter* const bw, - uint8_t* bit_lengths, int bit_lengths_size) { + const uint8_t* const bit_lengths, + int bit_lengths_size) { int i; int ok = 0; int count = 0; @@ -844,24 +820,17 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, // Store Huffman codes. for (i = 0; i < histogram_image_size; ++i) { int k; - int literal_lengths_size; - uint8_t* literal_lengths; - // TODO(vikasa): Evaluate and remove the call to PackLiteralBitLengths. - if (!PackLiteralBitLengths(bit_lengths[5 * i], cache_bits, use_color_cache, - &literal_lengths_size, &literal_lengths)) { - goto Error; - } - if (!StoreHuffmanCode(bw, literal_lengths, literal_lengths_size)) { - goto Error; - } - free(literal_lengths); - for (k = 1; k < 5; ++k) { - if (!StoreHuffmanCode(bw, bit_lengths[5 * i + k], - bit_lengths_sizes[5 * i + k])) { + for (k = 0; k < 5; ++k) { + const uint8_t* const cur_bit_lengths = bit_lengths[5 * i + k]; + const int cur_bit_lengths_size = (k == 0) ? + 256 + kLengthCodes + (1 << cache_bits) : + bit_lengths_sizes[5 * i + k]; + if (!StoreHuffmanCode(bw, cur_bit_lengths, cur_bit_lengths_size)) { goto Error; } } } + // Free combined histograms. DeleteHistograms(histogram_image_size, histogram_image); diff --git a/src/utils/huffman_encode.c b/src/utils/huffman_encode.c index 8152898f..7d88187f 100644 --- a/src/utils/huffman_encode.c +++ b/src/utils/huffman_encode.c @@ -241,7 +241,7 @@ static void WriteHuffmanTreeRepetitionsZeros( } } -void VP8LCreateCompressedHuffmanTree(const uint8_t* depth, +void VP8LCreateCompressedHuffmanTree(const uint8_t* const depth, int depth_size, int* num_symbols, uint8_t* tree, diff --git a/src/utils/huffman_encode.h b/src/utils/huffman_encode.h index 9bd717be..c931142d 100644 --- a/src/utils/huffman_encode.h +++ b/src/utils/huffman_encode.h @@ -34,10 +34,9 @@ extern "C" { int VP8LCreateHuffmanTree(const int* data, const int length, const int tree_limit, uint8_t* depth); -// Write a huffman tree from bit depths into the deflate representation -// of a Huffman tree. In deflate, the generated Huffman tree is to be +// Write a huffman tree from bit depths. The generated Huffman tree is // compressed once more using a Huffman tree. -void VP8LCreateCompressedHuffmanTree(const uint8_t* depth, int len, +void VP8LCreateCompressedHuffmanTree(const uint8_t* const depth, int len, int* num_symbols, uint8_t* tree, uint8_t* extra_bits_data); From 36dabdadf86c81200c17d37019ed9be642c52335 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Fri, 13 Apr 2012 07:18:09 +0000 Subject: [PATCH 13/46] Fix memory leak in method EncodeImageInternal for histogram_image. Change-Id: Ia1cfb96d9e6c120630732e2b5f39688376d1d208 --- src/enc/vp8l.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 762d0456..184a418b 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -215,11 +215,12 @@ static int GetBackwardReferences(int width, int height, // Choose appropriate backward reference. if (quality >= 50 && lz77_is_useful) { const int recursion_level = (num_pix < 320 * 200) ? 1 : 0; - PixOrCopy* const backward_refs_trace = - (PixOrCopy*)malloc(num_pix * sizeof(*backward_refs_trace)); int backward_refs_trace_size; + PixOrCopy* backward_refs_trace; free(backward_refs_rle); free(backward_refs_lz77); + backward_refs_trace = + (PixOrCopy*)malloc(num_pix * sizeof(*backward_refs_trace)); if (backward_refs_trace == NULL || !VP8LBackwardReferencesTraceBackwards(width, height, recursion_level, use_color_cache, @@ -259,7 +260,7 @@ End: return ok; } -static void DeleteHistograms(int size, VP8LHistogram** histograms) { +static void DeleteHistograms(VP8LHistogram** histograms, int size) { if (histograms != NULL) { int i; for (i = 0; i < size; ++i) { @@ -283,7 +284,7 @@ static int GetHistImageSymbols(int xsize, int ysize, int histogram_image_raw_size; VP8LHistogram** histogram_image_raw = NULL; - *histogram_image = 0; + *histogram_image = NULL; if (!VP8LHistogramBuildImage(xsize, ysize, histogram_bits, cache_bits, backward_refs, backward_refs_size, &histogram_image_raw, @@ -306,9 +307,9 @@ static int GetHistImageSymbols(int xsize, int ysize, Error: if (!ok) { - DeleteHistograms(*histogram_image_size, *histogram_image); + DeleteHistograms(*histogram_image, *histogram_image_size); } - DeleteHistograms(histogram_image_raw_size, histogram_image_raw); + DeleteHistograms(histogram_image_raw, histogram_image_raw_size); return ok; } @@ -419,6 +420,7 @@ static int OptimizeHuffmanForRle(int length, int* counts) { return 1; } +// TODO(vikasa): Wrap bit_codes and bit_lengths in a Struct. static int GetHuffBitLengthsAndCodes( int histogram_image_size, VP8LHistogram** histogram_image, int use_color_cache, int** bit_length_sizes, @@ -428,6 +430,7 @@ static int GetHuffBitLengthsAndCodes( for (i = 0; i < histogram_image_size; ++i) { const int num_literals = VP8LHistogramNumCodes(histogram_image[i]); k = 0; + // TODO(vikasa): Alloc one big buffer instead of allocating in the loop. (*bit_length_sizes)[5 * i] = num_literals; (*bit_lengths)[5 * i] = (uint8_t*)calloc(num_literals, 1); (*bit_codes)[5 * i] = (uint16_t*) @@ -832,7 +835,8 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, } // Free combined histograms. - DeleteHistograms(histogram_image_size, histogram_image); + DeleteHistograms(histogram_image, histogram_image_size); + histogram_image = NULL; // Emit no bits if there is only one symbol in the histogram. // This gives better compression for some images. @@ -847,6 +851,9 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, ok = 1; Error: + if (!ok) { + DeleteHistograms(histogram_image, histogram_image_size); + } for (i = 0; i < 5 * histogram_image_size; ++i) { free(bit_lengths[i]); free(bit_codes[i]); From 4f0c5caf677d2ac67399deee9bd3dd778320001f Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Tue, 17 Apr 2012 08:29:05 +0000 Subject: [PATCH 14/46] Fix prediction transform in lossless encoder. (Keep one tile as a scratch buffer). Change-Id: If112ada29bfd0bdc81b82e849a566b30dd331d2f --- src/dsp/lossless.c | 122 ++++++++++++++++++++++----------------------- src/enc/vp8l.c | 3 +- src/enc/vp8li.h | 2 +- 3 files changed, 63 insertions(+), 64 deletions(-) diff --git a/src/dsp/lossless.c b/src/dsp/lossless.c index 7aed1fd1..6a7fee33 100644 --- a/src/dsp/lossless.c +++ b/src/dsp/lossless.c @@ -326,48 +326,44 @@ static double PredictionCostSpatialHistogram(int accumulated[4][256], return retval; } -static int GetBestPredictorForTile(int tile_x, int tile_y, int max_tile_size, - int xsize, int ysize, +static int GetBestPredictorForTile(int width, int height, + int tile_x, int tile_y, int bits, int accumulated[4][256], const uint32_t* const argb) { - const int num_pred_modes = 14; - const int tile_y_offset = tile_y * max_tile_size; - const int tile_x_offset = tile_x * max_tile_size; - double cur_diff; + const int kNumPredModes = 14; + const int col_start = tile_x << bits; + const int row_start = tile_y << bits; + const int tile_size = 1 << bits; + const int ymax = (tile_size <= height - row_start) ? + tile_size : height - row_start; + const int xmax = (tile_size <= width - col_start) ? + tile_size : width - col_start; + int histo[4][256]; double best_diff = 1e99; int best_mode = 0; + int mode; - int all_x_max = tile_x_offset + max_tile_size; - int all_y_max = tile_y_offset + max_tile_size; - int histo[4][256]; - if (all_x_max > xsize) { - all_x_max = xsize; - } - if (all_y_max > ysize) { - all_y_max = ysize; - } - for (mode = 0; mode < num_pred_modes; ++mode) { - int all_y; + for (mode = 0; mode < kNumPredModes; ++mode) { const PredictorFunc pred_func = kPredictors[mode]; + double cur_diff; + int y; memset(&histo[0][0], 0, sizeof(histo)); - for (all_y = tile_y_offset; all_y < all_y_max; ++all_y) { - int all_x; - for (all_x = tile_x_offset; all_x < all_x_max; ++all_x) { + for (y = 0; y < ymax; ++y) { + int x; + const int row = row_start + y; + for (x = 0; x < xmax; ++x) { + const int col = col_start + x; + const int pix = row * width + col; uint32_t predict; uint32_t predict_diff; - if (all_y == 0) { - if (all_x == 0) { - predict = 0xff000000; - } else { - predict = argb[all_x - 1]; // Top Row: Pick Left Element. - } - } else if (all_x == 0) { - predict = argb[(all_y - 1) * xsize]; // First Col: Pick Top Element. + if (row == 0) { + predict = (col == 0) ? ARGB_BLACK : argb[pix - 1]; // Left. + } else if (col == 0) { + predict = argb[pix - width]; // Top. } else { - const uint32_t* src = argb + all_y * xsize + all_x; - predict = pred_func(src[-1], src - xsize); + predict = pred_func(argb[pix - 1], argb + pix - width); } - predict_diff = VP8LSubPixels(argb[all_y * xsize + all_x], predict); + predict_diff = VP8LSubPixels(argb[pix], predict); ++histo[0][predict_diff >> 24]; ++histo[1][((predict_diff >> 16) & 0xff)]; ++histo[2][((predict_diff >> 8) & 0xff)]; @@ -380,6 +376,7 @@ static int GetBestPredictorForTile(int tile_x, int tile_y, int max_tile_size, best_mode = mode; } } + return best_mode; } @@ -389,59 +386,60 @@ static void CopyTileWithPrediction(int width, int height, uint32_t* const argb) { const int col_start = tile_x << bits; const int row_start = tile_y << bits; - const int transform_size = 1 << bits; - const int ymax = (transform_size <= height - row_start) ? - transform_size : height - row_start; - const int xmax = (transform_size <= width - col_start) ? - transform_size : width - col_start; + const int tile_size = 1 << bits; + const int ymax = (tile_size <= height - row_start) ? + tile_size : height - row_start; + const int xmax = (tile_size <= width - col_start) ? + tile_size : width - col_start; const PredictorFunc pred_func = kPredictors[mode]; - uint32_t* const top_row = argb_scratch; - uint32_t* const current_row = argb_scratch + width; - int y; + + // Apply prediction filter to tile and save it in argb_scratch. for (y = 0; y < ymax; ++y) { - int x; const int row = row_start + y; - // Update current_row & top_row. - if (row > 0) { - memcpy(top_row, current_row, width * sizeof(*top_row)); - } - memcpy(current_row, &argb[row * width], width * sizeof(*current_row)); + int x; for (x = 0; x < xmax; ++x) { const int col = col_start + x; const int pix = row * width + col; + const int idx = y * tile_size + x; uint32_t predict; if (row == 0) { - if (col == 0) { - predict = ARGB_BLACK; - } else { - const uint32_t left = current_row[col - 1]; - predict = left; - } + predict = (col == 0) ? ARGB_BLACK : argb[pix - 1]; // Left. } else if (col == 0) { - const uint32_t top = top_row[col]; - predict = top; + predict = argb[pix - width]; // Top. } else { - predict = pred_func(argb[pix - 1], top_row + col); + predict = pred_func(argb[pix - 1], argb + pix - width); } - argb[pix] = VP8LSubPixels(argb[pix], predict); + argb_scratch[idx] = VP8LSubPixels(argb[pix], predict); } } + + // Copy back predicted tile to argb. + // Note: There may be a possibility of reducing argb_scratch size by + // integrating this loop with the previous one, but that would make the code + // much more complicated. + for (y = 0; y < ymax; ++y) { + const int row = row_start + y; + const uint32_t* const src = argb_scratch + y * tile_size; + uint32_t* const dst = argb + row * width + col_start; + memcpy(dst, src, xmax * sizeof(*dst)); + } } void VP8LResidualImage(int width, int height, int bits, uint32_t* const argb, uint32_t* const argb_scratch, uint32_t* const image) { const int max_tile_size = 1 << bits; - const int tile_xsize = VP8LSubSampleSize(width, bits); - const int tile_ysize = VP8LSubSampleSize(height, bits); + const int tiles_per_row = VP8LSubSampleSize(width, bits); + const int tiles_per_col = VP8LSubSampleSize(height, bits); int tile_y; int histo[4][256]; memset(histo, 0, sizeof(histo)); - for (tile_y = 0; tile_y < tile_ysize; ++tile_y) { + // We perform prediction in reverse scan-line order. + for (tile_y = tiles_per_col - 1; tile_y >= 0; --tile_y) { const int tile_y_offset = tile_y * max_tile_size; int tile_x; - for (tile_x = 0; tile_x < tile_xsize; ++tile_x) { + for (tile_x = tiles_per_row - 1; tile_x >= 0; --tile_x) { int pred; int y; const int tile_x_offset = tile_x * max_tile_size; @@ -449,9 +447,9 @@ void VP8LResidualImage(int width, int height, int bits, if (all_x_max > width) { all_x_max = width; } - pred = GetBestPredictorForTile(tile_x, tile_y, max_tile_size, - width, height, histo, argb); - image[tile_y * tile_xsize + tile_x] = 0xff000000u | (pred << 8); + pred = GetBestPredictorForTile(width, height, tile_x, tile_y, bits, histo, + argb); + image[tile_y * tiles_per_row + tile_x] = 0xff000000u | (pred << 8); CopyTileWithPrediction(width, height, tile_x, tile_y, bits, pred, argb_scratch, argb); for (y = 0; y < max_tile_size; ++y) { diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 184a418b..0a62e57a 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -1036,8 +1036,9 @@ static void DeleteVP8LEncoder(VP8LEncoder* enc) { static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, int height, int width) { WebPEncodingError err = VP8_ENC_OK; + const size_t tile_size = 1 << enc->transform_bits_; const size_t image_size = height * width; - const size_t argb_scratch_size = 2 * width; + const size_t argb_scratch_size = tile_size * tile_size; const size_t transform_data_size = VP8LSubSampleSize(height, enc->transform_bits_) * VP8LSubSampleSize(width, enc->transform_bits_); diff --git a/src/enc/vp8li.h b/src/enc/vp8li.h index a83b3993..2f36d8f6 100644 --- a/src/enc/vp8li.h +++ b/src/enc/vp8li.h @@ -38,7 +38,7 @@ typedef struct { WebPPicture* pic_; // input picture. uint32_t* argb_; // Transformed argb image data. - uint32_t* argb_scratch_; // Scratch memory for current and top row. + uint32_t* argb_scratch_; // Scratch memory for one argb tile // (used for prediction). uint32_t* transform_data_; // Scratch memory for transform data. int current_width_; // Corresponds to packed image width. From 54dad7e553c6a831ef83b1181b96835d5e9dfbc8 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Tue, 17 Apr 2012 10:07:34 +0000 Subject: [PATCH 15/46] Color cache size should be counted as 0 when cache bits = 0 Change-Id: I1d05e0561a92aebaf62162fe11ffc4b12496d698 --- src/enc/vp8l.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 0a62e57a..eb59e097 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -743,7 +743,8 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, uint16_t** bit_codes = NULL; const int use_2d_locality = 1; int backward_refs_size; - const int use_color_cache = cache_bits ? 1 : 0; + const int use_color_cache = (cache_bits > 0) ? 1 : 0; + const int color_cache_size = use_color_cache ? (1 << cache_bits) : 0; const int histogram_image_xysize = VP8LSubSampleSize(width, histogram_bits) * VP8LSubSampleSize(height, histogram_bits); VP8LHistogram** histogram_image; @@ -826,7 +827,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, for (k = 0; k < 5; ++k) { const uint8_t* const cur_bit_lengths = bit_lengths[5 * i + k]; const int cur_bit_lengths_size = (k == 0) ? - 256 + kLengthCodes + (1 << cache_bits) : + 256 + kLengthCodes + color_cache_size : bit_lengths_sizes[5 * i + k]; if (!StoreHuffmanCode(bw, cur_bit_lengths, cur_bit_lengths_size)) { goto Error; From b96d8740c9a59a8808d46efe1be22a7a31c4278e Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Wed, 18 Apr 2012 08:58:01 +0000 Subject: [PATCH 16/46] Need to write a '0' bit at the end of transforms. Also convert an 'if' to 'assert'. Change-Id: Ia1129ad9ddb027c27b4f4fc1da4bbaf53a0a8f76 --- src/enc/vp8l.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index eb59e097..a1e29971 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -743,7 +743,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, uint16_t** bit_codes = NULL; const int use_2d_locality = 1; int backward_refs_size; - const int use_color_cache = (cache_bits > 0) ? 1 : 0; + const int use_color_cache = (cache_bits > 0); const int color_cache_size = use_color_cache ? (1 << cache_bits) : 0; const int histogram_image_xysize = VP8LSubSampleSize(width, histogram_bits) * VP8LSubSampleSize(height, histogram_bits); @@ -1197,6 +1197,8 @@ int VP8LEncodeImage(const WebPConfig* const config, } } + VP8LWriteBits(&bw, 1, 0); // No more transforms. + // --------------------------------------------------------------------------- // Encode and write the transformed image. @@ -1214,8 +1216,7 @@ int VP8LEncodeImage(const WebPConfig* const config, VP8LBitWriterDestroy(&bw); DeleteVP8LEncoder(enc); if (!ok) { - // TODO(vikasa): err is not set for all error paths. Set default err. - if (err == VP8_ENC_OK) err = VP8_ENC_ERROR_BAD_WRITE; + assert(err != VP8_ENC_OK); WebPEncodingSetError(picture, err); } return ok; From afd2102f43586916092836a793f46a7131dc2eee Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Wed, 18 Apr 2012 11:31:07 +0000 Subject: [PATCH 17/46] Fix cross-color transform in lossless encoder make elements of "Multiplier" struct unsigned, so that any negative values are automatically converted to "mod 256" values. Change-Id: Iab4f9bacc50dcd94a557944727d9338dbb0982f7 --- src/dsp/lossless.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dsp/lossless.c b/src/dsp/lossless.c index 6a7fee33..c05f6d46 100644 --- a/src/dsp/lossless.c +++ b/src/dsp/lossless.c @@ -557,9 +557,11 @@ static void AddGreenToBlueAndRed(const VP8LTransform* const transform, } typedef struct { - int green_to_red_; - int green_to_blue_; - int red_to_blue_; + // Note: the members are uint8_t, so that any negative values are + // automatically converted to "mod 256" values. + uint8_t green_to_red_; + uint8_t green_to_blue_; + uint8_t red_to_blue_; } Multipliers; static WEBP_INLINE void MultipliersClear(Multipliers* m) { From 0993a611cd0d0c72ce0c4adee5f1371db8bc3567 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Thu, 19 Apr 2012 12:11:28 +0000 Subject: [PATCH 18/46] Full and final fix for prediction transform use (tile_size + 1) rows of scratch area. Change-Id: I06d612fff1794fc045ba76275e94e7210802c332 --- src/dsp/lossless.c | 63 +++++++++++++++++++++++----------------------- src/enc/vp8l.c | 2 +- src/enc/vp8li.h | 2 +- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/dsp/lossless.c b/src/dsp/lossless.c index c05f6d46..8fbb9a95 100644 --- a/src/dsp/lossless.c +++ b/src/dsp/lossless.c @@ -329,7 +329,7 @@ static double PredictionCostSpatialHistogram(int accumulated[4][256], static int GetBestPredictorForTile(int width, int height, int tile_x, int tile_y, int bits, int accumulated[4][256], - const uint32_t* const argb) { + const uint32_t* const argb_scratch) { const int kNumPredModes = 14; const int col_start = tile_x << bits; const int row_start = tile_y << bits; @@ -344,6 +344,7 @@ static int GetBestPredictorForTile(int width, int height, int mode; for (mode = 0; mode < kNumPredModes; ++mode) { + const uint32_t* current_row = argb_scratch; const PredictorFunc pred_func = kPredictors[mode]; double cur_diff; int y; @@ -351,19 +352,20 @@ static int GetBestPredictorForTile(int width, int height, for (y = 0; y < ymax; ++y) { int x; const int row = row_start + y; + const uint32_t* const upper_row = current_row; + current_row = upper_row + width; for (x = 0; x < xmax; ++x) { const int col = col_start + x; - const int pix = row * width + col; uint32_t predict; uint32_t predict_diff; if (row == 0) { - predict = (col == 0) ? ARGB_BLACK : argb[pix - 1]; // Left. + predict = (col == 0) ? ARGB_BLACK : current_row[col - 1]; // Left. } else if (col == 0) { - predict = argb[pix - width]; // Top. + predict = upper_row[col]; // Top. } else { - predict = pred_func(argb[pix - 1], argb + pix - width); + predict = pred_func(current_row[col - 1], upper_row + col); } - predict_diff = VP8LSubPixels(argb[pix], predict); + predict_diff = VP8LSubPixels(current_row[col], predict); ++histo[0][predict_diff >> 24]; ++histo[1][((predict_diff >> 16) & 0xff)]; ++histo[2][((predict_diff >> 8) & 0xff)]; @@ -382,7 +384,7 @@ static int GetBestPredictorForTile(int width, int height, static void CopyTileWithPrediction(int width, int height, int tile_x, int tile_y, int bits, int mode, - uint32_t* const argb_scratch, + const uint32_t* const argb_scratch, uint32_t* const argb) { const int col_start = tile_x << bits; const int row_start = tile_y << bits; @@ -392,38 +394,28 @@ static void CopyTileWithPrediction(int width, int height, const int xmax = (tile_size <= width - col_start) ? tile_size : width - col_start; const PredictorFunc pred_func = kPredictors[mode]; - int y; + const uint32_t* current_row = argb_scratch; - // Apply prediction filter to tile and save it in argb_scratch. + int y; for (y = 0; y < ymax; ++y) { - const int row = row_start + y; int x; + const int row = row_start + y; + const uint32_t* const upper_row = current_row; + current_row = upper_row + width; for (x = 0; x < xmax; ++x) { const int col = col_start + x; const int pix = row * width + col; - const int idx = y * tile_size + x; uint32_t predict; if (row == 0) { - predict = (col == 0) ? ARGB_BLACK : argb[pix - 1]; // Left. + predict = (col == 0) ? ARGB_BLACK : current_row[col - 1]; // Left. } else if (col == 0) { - predict = argb[pix - width]; // Top. + predict = upper_row[col]; // Top. } else { - predict = pred_func(argb[pix - 1], argb + pix - width); + predict = pred_func(current_row[col - 1], upper_row + col); } - argb_scratch[idx] = VP8LSubPixels(argb[pix], predict); + argb[pix] = VP8LSubPixels(current_row[col], predict); } } - - // Copy back predicted tile to argb. - // Note: There may be a possibility of reducing argb_scratch size by - // integrating this loop with the previous one, but that would make the code - // much more complicated. - for (y = 0; y < ymax; ++y) { - const int row = row_start + y; - const uint32_t* const src = argb_scratch + y * tile_size; - uint32_t* const dst = argb + row * width + col_start; - memcpy(dst, src, xmax * sizeof(*dst)); - } } void VP8LResidualImage(int width, int height, int bits, @@ -432,14 +424,23 @@ void VP8LResidualImage(int width, int height, int bits, const int max_tile_size = 1 << bits; const int tiles_per_row = VP8LSubSampleSize(width, bits); const int tiles_per_col = VP8LSubSampleSize(height, bits); + uint32_t* const upper_row = argb_scratch; + uint32_t* const current_tile_rows = argb_scratch + width; int tile_y; int histo[4][256]; memset(histo, 0, sizeof(histo)); - // We perform prediction in reverse scan-line order. - for (tile_y = tiles_per_col - 1; tile_y >= 0; --tile_y) { + for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) { const int tile_y_offset = tile_y * max_tile_size; + const int this_tile_height = + (tile_y < tiles_per_col - 1) ? max_tile_size : height - tile_y_offset; int tile_x; - for (tile_x = tiles_per_row - 1; tile_x >= 0; --tile_x) { + if (tile_y > 0) { + memcpy(upper_row, current_tile_rows + (max_tile_size - 1) * width, + width * sizeof(*upper_row)); + } + memcpy(current_tile_rows, &argb[tile_y_offset * width], + this_tile_height * width * sizeof(*current_tile_rows)); + for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) { int pred; int y; const int tile_x_offset = tile_x * max_tile_size; @@ -448,7 +449,7 @@ void VP8LResidualImage(int width, int height, int bits, all_x_max = width; } pred = GetBestPredictorForTile(width, height, tile_x, tile_y, bits, histo, - argb); + argb_scratch); image[tile_y * tiles_per_row + tile_x] = 0xff000000u | (pred << 8); CopyTileWithPrediction(width, height, tile_x, tile_y, bits, pred, argb_scratch, argb); @@ -571,7 +572,7 @@ static WEBP_INLINE void MultipliersClear(Multipliers* m) { } static WEBP_INLINE uint32_t ColorTransformDelta(int8_t color_pred, - int8_t color) { + int8_t color) { return (uint32_t)((int)(color_pred) * color) >> 5; } diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index a1e29971..304a4088 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -1039,7 +1039,7 @@ static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, WebPEncodingError err = VP8_ENC_OK; const size_t tile_size = 1 << enc->transform_bits_; const size_t image_size = height * width; - const size_t argb_scratch_size = tile_size * tile_size; + const size_t argb_scratch_size = (tile_size + 1) * width; const size_t transform_data_size = VP8LSubSampleSize(height, enc->transform_bits_) * VP8LSubSampleSize(width, enc->transform_bits_); diff --git a/src/enc/vp8li.h b/src/enc/vp8li.h index 2f36d8f6..2a07e48d 100644 --- a/src/enc/vp8li.h +++ b/src/enc/vp8li.h @@ -38,7 +38,7 @@ typedef struct { WebPPicture* pic_; // input picture. uint32_t* argb_; // Transformed argb image data. - uint32_t* argb_scratch_; // Scratch memory for one argb tile + uint32_t* argb_scratch_; // Scratch memory for argb rows // (used for prediction). uint32_t* transform_data_; // Scratch memory for transform data. int current_width_; // Corresponds to packed image width. From 4a6362357a9bb266ea1ed2459fae2dbb7bc8c133 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Thu, 19 Apr 2012 15:44:40 +0000 Subject: [PATCH 19/46] fix a memleak in EncodeImageInternal() Change-Id: I55cd013211f192188b54c694ef0837af0c01b53c --- src/enc/vp8l.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 304a4088..98bcee4b 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -855,6 +855,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, if (!ok) { DeleteHistograms(histogram_image, histogram_image_size); } + free(backward_refs); for (i = 0; i < 5 * histogram_image_size; ++i) { free(bit_lengths[i]); free(bit_codes[i]); From 4d02d5863f5a818b4c4f6abb812b4067441c6a1a Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Fri, 20 Apr 2012 14:43:01 +0000 Subject: [PATCH 20/46] Lossless encoder: correction in Palette storage (Essentially, there was no need of a separate 'argb_palette' array. And argb_palette[0] was never being set). Change-Id: Id0a8c7e063d3af41e39fc9b8661611b51ccc55cd --- src/enc/vp8l.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 98bcee4b..dffcd778 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -1068,10 +1068,10 @@ static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, WebPEncodingError err = VP8_ENC_OK; int i; uint32_t* argb = enc->pic_->argb; - const uint32_t* const palette = enc->palette_; + uint32_t* const palette = enc->palette_; const int palette_size = enc->palette_size_; - uint32_t argb_palette[MAX_PALETTE_SIZE]; + // Replace each input pixel by corresponding palette index. for (i = 0; i < width * height; ++i) { int k; for (k = 0; k < palette_size; ++k) { @@ -1082,26 +1082,27 @@ static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, } } } + + // Save palette to bitstream. VP8LWriteBits(bw, 1, 1); VP8LWriteBits(bw, 2, 3); VP8LWriteBits(bw, 8, palette_size - 1); for (i = palette_size - 1; i >= 1; --i) { - argb_palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); + palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); } - if (!EncodeImageInternal(bw, argb_palette, palette_size, 1, quality, - 0, 0)) { + if (!EncodeImageInternal(bw, palette, palette_size, 1, quality, 0, 0)) { err = VP8_ENC_ERROR_INVALID_CONFIGURATION; goto Error; } + if (palette_size <= 16) { + // Image can be packed (multiple pixels per uint32_t). int xbits = 1; if (palette_size <= 2) { xbits = 3; } else if (palette_size <= 4) { xbits = 2; } - - // Image can be packed (multiple pixels per uint32). err = AllocateTransformBuffer(enc, height, VP8LSubSampleSize(width, xbits)); if (err != VP8_ENC_OK) goto Error; BundleColorMap(argb, width, height, xbits, enc->argb_, enc->current_width_); From 3d33ecd12b98b1860e8a1af82cbcef8ba9acaefc Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Mon, 23 Apr 2012 07:40:34 +0000 Subject: [PATCH 21/46] Some renaming/comments related to palette in lossless encoder. Change-Id: Iaab32912f4c31e809d7a49fd748099d8c0c3e7d9 --- src/enc/vp8l.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index dffcd778..58ab8b06 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -41,9 +41,11 @@ static int CompareColors(const void* p1, const void* p2) { return 1; } -static int CreatePalette256(const uint32_t* const argb, int num_pix, - uint32_t palette[MAX_PALETTE_SIZE], - int* const palette_size) { +// If number of colors in the image is less than or equal to MAX_PALETTE_SIZE, +// creates a palette and returns true, else returns false. +static int AnalyzeAndCreatePalette(const uint32_t* const argb, int num_pix, + uint32_t palette[MAX_PALETTE_SIZE], + int* const palette_size) { int i, key; int num_colors = 0; uint8_t in_use[MAX_PALETTE_SIZE * 4] = { 0 }; @@ -137,8 +139,9 @@ static int VP8LEncAnalyze(VP8LEncoder* const enc) { enc->use_cross_color_ = 1; } - enc->use_palette_ = CreatePalette256(pic->argb, pic->width * pic->height, - enc->palette_, &enc->palette_size_); + enc->use_palette_ = + AnalyzeAndCreatePalette(pic->argb, pic->width * pic->height, + enc->palette_, &enc->palette_size_); return 1; } @@ -1062,6 +1065,9 @@ static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, return err; } +// Note: Expects "enc->palette_" to be set properly. +// Also, "enc->palette_" will be modified after this call and should not be used +// later. static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, VP8LEncoder* const enc, int width, int height, int quality) { From 6f4a16ea00d4fe2efae83763447badf9df9adba1 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Mon, 23 Apr 2012 10:13:22 +0000 Subject: [PATCH 22/46] Removing the indirection of meta-huffman tables. Now, the indexing refers directly to 5 huffman codes that must be encoded separately. Change-Id: I92e10ccf8ca464aa7259867d5fae2869343e3b3c --- src/enc/vp8l.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 58ab8b06..e7cceeef 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -788,9 +788,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, write_histogram_image = (histogram_image_size > 1); VP8LWriteBits(bw, 1, write_histogram_image); if (write_histogram_image) { - int nbits; int image_size_bits; - int num_histograms; uint32_t* histogram_argb = (uint32_t*) malloc(histogram_image_xysize * sizeof(*histogram_argb)); if (histogram_argb == NULL) goto Error; @@ -809,12 +807,6 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, image_size_bits = VP8LBitsLog2Ceiling(histogram_image_size - 1); VP8LWriteBits(bw, 4, image_size_bits); VP8LWriteBits(bw, image_size_bits, histogram_image_size - 2); - num_histograms = 5 * histogram_image_size; - nbits = VP8LBitsLog2Ceiling(num_histograms); - VP8LWriteBits(bw, 4, nbits); - for (i = 0; i < num_histograms; ++i) { - VP8LWriteBits(bw, nbits, i); - } free(histogram_argb); } From e38b40a9967b76eea12d05b51e94b4a941097f59 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Mon, 23 Apr 2012 11:56:23 +0000 Subject: [PATCH 23/46] Factorize code for clearing HtreeGroup. Change-Id: I29de6dab7383b8cf071eec155e01340d5fdadee5 --- src/dec/vp8l.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/dec/vp8l.c b/src/dec/vp8l.c index 92e62d22..35720d71 100644 --- a/src/dec/vp8l.c +++ b/src/dec/vp8l.c @@ -293,6 +293,19 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, return 1; } +static void DeleteHtreeGroups(HTreeGroup* htree_groups, int num_htree_groups) { + if (htree_groups != NULL) { + int i, j; + for (i = 0; i < num_htree_groups; ++i) { + HuffmanTree* const htrees = htree_groups[i].htrees_; + for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { + HuffmanTreeRelease(&htrees[j]); + } + } + free(htree_groups); + } +} + static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, int* const color_cache_bits_ptr) { int ok = 0; @@ -341,12 +354,13 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, ok = !br->error_; for (i = 0; ok && i < num_htree_groups; ++i) { + HuffmanTree* const htrees = htree_groups[i].htrees_; for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { int alphabet_size = kAlphabetSize[j]; if (j == 0) { alphabet_size += color_cache_size; } - ok = ReadHuffmanCode(alphabet_size, dec, &htree_groups[i].htrees_[j]); + ok = ReadHuffmanCode(alphabet_size, dec, &htrees[j]); ok = ok && !br->error_; } } @@ -360,14 +374,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, Error: free(huffman_image); - if (htree_groups != NULL) { - for (i = 0; i < num_htree_groups; ++i) { - for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { - HuffmanTreeRelease(&htree_groups[i].htrees_[j]); - } - } - free(htree_groups); - } + DeleteHtreeGroups(htree_groups, num_htree_groups); return 0; } @@ -498,6 +505,7 @@ static WEBP_INLINE HTreeGroup* GetHtreeGroupForPos(VP8LMetadata* const hdr, int x, int y) { const int meta_index = GetMetaIndex(hdr->huffman_image_, hdr->huffman_xsize_, hdr->huffman_subsample_bits_, x, y); + assert(meta_index < hdr->num_htree_groups_); return hdr->htree_groups_ + meta_index; } @@ -770,16 +778,7 @@ static void ClearMetadata(VP8LMetadata* const hdr) { assert(hdr); free(hdr->huffman_image_); - if (hdr->htree_groups_ != NULL) { - int i, j; - for (i = 0; i < hdr->num_htree_groups_; ++i) { - for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { - HuffmanTreeRelease(&hdr->htree_groups_[i].htrees_[j]); - } - } - free(hdr->htree_groups_); - } - + DeleteHtreeGroups(hdr->htree_groups_, hdr->num_htree_groups_); VP8LColorCacheDelete(hdr->color_cache_); InitMetadata(hdr); } From 0e6fa06595801872aa7b01fbf0a76cdb03f9d830 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Mon, 23 Apr 2012 12:15:39 +0000 Subject: [PATCH 24/46] cache_bits passed to EncodeImageInternal() should be 0 when not using color cache. Change-Id: Id15c9b6bfbeb7c69a0189fa4c411df2763f9dead --- src/enc/vp8l.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index e7cceeef..b1c0fcd3 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -1113,8 +1113,7 @@ static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, int VP8LEncodeImage(const WebPConfig* const config, WebPPicture* const picture) { int ok = 0; - int use_color_cache = 1; - int cache_bits = 7; + int cache_bits = 7; // If equal to 0, don't use color cache. int width, height, quality; VP8LEncoder* enc = NULL; WebPEncodingError err = VP8_ENC_OK; @@ -1152,7 +1151,7 @@ int VP8LEncodeImage(const WebPConfig* const config, if (enc->use_palette_) { err = ApplyPalette(&bw, enc, width, height, quality); if (err != VP8_ENC_OK) goto Error; - use_color_cache = 0; + cache_bits = 0; // Don't use color cache. } // In case image is not packed. @@ -1187,18 +1186,23 @@ int VP8LEncodeImage(const WebPConfig* const config, } } - if (use_color_cache) { + VP8LWriteBits(&bw, 1, 0); // No more transforms. + + // --------------------------------------------------------------------------- + // Estimate the color cache size. + + if (cache_bits > 0) { if (quality > 25) { if (!VP8LCalculateEstimateForPaletteSize(enc->argb_, enc->current_width_, height, &cache_bits)) { err = VP8_ENC_ERROR_INVALID_CONFIGURATION; goto Error; } + } else { + cache_bits = 0; // Don't use color cache. } } - VP8LWriteBits(&bw, 1, 0); // No more transforms. - // --------------------------------------------------------------------------- // Encode and write the transformed image. From 149b5098a95a82891bcf9728a6551d4d7d4bd8dd Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Mon, 23 Apr 2012 18:23:18 +0000 Subject: [PATCH 25/46] Update lossless encoder strategy: Don't use any other transform when using palette. Change-Id: I488ac546869677f1b6e4eed80e973569c757e997 --- src/enc/vp8l.c | 77 ++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index b1c0fcd3..57e3eca6 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -126,22 +126,23 @@ static int AnalyzeEntropy(const uint32_t const *argb, int xsize, int ysize, static int VP8LEncAnalyze(VP8LEncoder* const enc) { const WebPPicture* const pic = enc->pic_; - int non_pred_entropy, pred_entropy; assert(pic && pic->argb); - if (!AnalyzeEntropy(pic->argb, pic->width, pic->height, - &non_pred_entropy, &pred_entropy)) { - return 0; - } - - if (8 * pred_entropy < 7 * non_pred_entropy) { - enc->use_predict_ = 1; - enc->use_cross_color_ = 1; - } - enc->use_palette_ = - AnalyzeAndCreatePalette(pic->argb, pic->width * pic->height, - enc->palette_, &enc->palette_size_); + AnalyzeAndCreatePalette(pic->argb, pic->width * pic->height, + enc->palette_, &enc->palette_size_); + if (!enc->use_palette_) { + int non_pred_entropy, pred_entropy; + if (!AnalyzeEntropy(pic->argb, pic->width, pic->height, + &non_pred_entropy, &pred_entropy)) { + return 0; + } + + if (8 * pred_entropy < 7 * non_pred_entropy) { + enc->use_predict_ = 1; + enc->use_cross_color_ = 1; + } + } return 1; } @@ -865,32 +866,34 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, static int EvalAndApplySubtractGreen(VP8LBitWriter* const bw, VP8LEncoder* const enc, int width, int height) { - int i; - VP8LHistogram* before = NULL; - // Check if it would be a good idea to subtract green from red and blue. - VP8LHistogram* after = (VP8LHistogram*)malloc(2 * sizeof(*after)); - if (after == NULL) return 0; - before = after + 1; + if (!enc->use_palette_) { + int i; + VP8LHistogram* before = NULL; + // Check if it would be a good idea to subtract green from red and blue. + VP8LHistogram* after = (VP8LHistogram*)malloc(2 * sizeof(*after)); + if (after == NULL) return 0; + before = after + 1; - VP8LHistogramInit(before, 1); - VP8LHistogramInit(after, 1); - for (i = 0; i < width * height; ++i) { - // We only impact entropy in red and blue components, don't bother - // to look at others. - const uint32_t c = enc->argb_[i]; - const int green = (c >> 8) & 0xff; - ++(before->red_[(c >> 16) & 0xff]); - ++(before->blue_[c & 0xff]); - ++(after->red_[((c >> 16) - green) & 0xff]); - ++(after->blue_[(c - green) & 0xff]); + VP8LHistogramInit(before, 1); + VP8LHistogramInit(after, 1); + for (i = 0; i < width * height; ++i) { + // We only impact entropy in red and blue components, don't bother + // to look at others. + const uint32_t c = enc->argb_[i]; + const int green = (c >> 8) & 0xff; + ++(before->red_[(c >> 16) & 0xff]); + ++(before->blue_[c & 0xff]); + ++(after->red_[((c >> 16) - green) & 0xff]); + ++(after->blue_[(c - green) & 0xff]); + } + // Check if subtracting green yields low entropy. + if (VP8LHistogramEstimateBits(after) < VP8LHistogramEstimateBits(before)) { + VP8LWriteBits(bw, 1, 1); + VP8LWriteBits(bw, 2, 2); + VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height); + } + free(after); } - // Check if subtracting green yields low entropy. - if (VP8LHistogramEstimateBits(after) < VP8LHistogramEstimateBits(before)) { - VP8LWriteBits(bw, 1, 1); - VP8LWriteBits(bw, 2, 2); - VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height); - } - free(after); return 1; } From 412222c88c219bce7da4d4105709f54f21cc653e Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Tue, 24 Apr 2012 08:46:24 +0000 Subject: [PATCH 26/46] Make histo_bits and transform_bits function of quality. Change-Id: Ic34e40853604811abc63a38e09d6a01961649efc --- src/enc/vp8l.c | 10 ++++++---- src/enc/vp8li.h | 1 - 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 57e3eca6..e423eb76 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -997,6 +997,7 @@ static WebPEncodingError WriteImage(VP8LEncoder* const enc, static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, WebPPicture* const picture) { + int sampling_bits = 9 - (((int)config->quality + 8) >> 4); VP8LEncoder* enc = (VP8LEncoder*)malloc(sizeof(*enc)); if (enc == NULL) { WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); @@ -1007,11 +1008,12 @@ static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, enc->config_ = config; enc->pic_ = picture; enc->use_lz77_ = 1; - enc->palette_bits_ = 7; - // TODO: Use config.quality to initialize histo_bits_ and transform_bits_. - enc->histo_bits_ = 4; - enc->transform_bits_ = 4; + if (sampling_bits > 8) sampling_bits = 8; + if (sampling_bits < 3) sampling_bits = 3; + + enc->histo_bits_ = sampling_bits; + enc->transform_bits_ = sampling_bits; return enc; } diff --git a/src/enc/vp8li.h b/src/enc/vp8li.h index 2a07e48d..305c3451 100644 --- a/src/enc/vp8li.h +++ b/src/enc/vp8li.h @@ -45,7 +45,6 @@ typedef struct { // Encoding parameters derived from quality parameter. int use_lz77_; - int palette_bits_; int histo_bits_; int transform_bits_; From c6ac4dfbb46f72cda61f0c637c74e71968d20ffc Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Tue, 24 Apr 2012 09:55:19 +0000 Subject: [PATCH 27/46] Run TraceBackwards for higher qualities. Also reduce the iteration count in function VP8LHashChain_FindCopy. Change-Id: I73e3811e142e81314515587fd655ab3bfa74d099 --- src/enc/backward_references.c | 7 ++--- src/enc/vp8l.c | 55 +++++++++++++++++++---------------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/enc/backward_references.c b/src/enc/backward_references.c index 875ecc5b..9044bcdc 100644 --- a/src/enc/backward_references.c +++ b/src/enc/backward_references.c @@ -121,7 +121,7 @@ static int VP8LHashChain_FindCopy(VP8LHashChain* p, const uint64_t hash_code = GetHash64(next_two_pixels); int prev_length = 0; int64_t best_val = 0; - int give_up = quality * 3 / 4 + 25; + int give_up = 10 + (quality >> 1); const int min_pos = (index > kWindowSize) ? index - kWindowSize : 0; int32_t pos; int64_t length; @@ -134,8 +134,7 @@ static int VP8LHashChain_FindCopy(VP8LHashChain* p, pos >= min_pos; pos = p->chain_[pos]) { if (give_up < 0) { - if (give_up < -quality * 8 || - best_val >= 0xff0000) { + if (give_up < -quality * 2 || best_val >= 0xff0000) { break; } } @@ -219,9 +218,9 @@ int VP8LBackwardReferencesHashChain(int xsize, int ysize, int use_palette, const uint32_t* argb, int palette_bits, int quality, PixOrCopy* stream, int* stream_size) { - const int pix_count = xsize * ysize; int i; int ok = 0; + const int pix_count = xsize * ysize; VP8LHashChain* hash_chain = (VP8LHashChain*)malloc(sizeof(*hash_chain)); VP8LColorCache hashers; if (!hash_chain || diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index e423eb76..744684db 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -217,35 +217,40 @@ static int GetBackwardReferences(int width, int height, VP8LHistogramEstimateBits(histo_lz77)); // Choose appropriate backward reference. - if (quality >= 50 && lz77_is_useful) { - const int recursion_level = (num_pix < 320 * 200) ? 1 : 0; - int backward_refs_trace_size; - PixOrCopy* backward_refs_trace; + if (lz77_is_useful) { + // TraceBackwards is costly. Run it for higher qualities. + const int try_lz77_trace_backwards = (quality >= 75); free(backward_refs_rle); - free(backward_refs_lz77); - backward_refs_trace = - (PixOrCopy*)malloc(num_pix * sizeof(*backward_refs_trace)); - if (backward_refs_trace == NULL || - !VP8LBackwardReferencesTraceBackwards(width, height, - recursion_level, use_color_cache, - argb, cache_bits, - backward_refs_trace, - &backward_refs_trace_size)) { - free(backward_refs_trace); - goto End; - } - *backward_refs = backward_refs_trace; - *backward_refs_size = backward_refs_trace_size; - } else { - if (lz77_is_useful) { + if (try_lz77_trace_backwards) { + const int recursion_level = (num_pix < 320 * 200) ? 1 : 0; + int backward_refs_trace_size; + PixOrCopy* backward_refs_trace; + backward_refs_trace = + (PixOrCopy*)malloc(num_pix * sizeof(*backward_refs_trace)); + if (backward_refs_trace == NULL) { + free(backward_refs_lz77); + goto End; + } + if (VP8LBackwardReferencesTraceBackwards(width, height, recursion_level, + use_color_cache, argb, + cache_bits, backward_refs_trace, + &backward_refs_trace_size)) { + free(backward_refs_lz77); + *backward_refs = backward_refs_trace; + *backward_refs_size = backward_refs_trace_size; + } else { + free(backward_refs_trace); + *backward_refs = backward_refs_lz77; + *backward_refs_size = backward_refs_lz77_size; + } + } else { *backward_refs = backward_refs_lz77; *backward_refs_size = backward_refs_lz77_size; - free(backward_refs_rle); - } else { - *backward_refs = backward_refs_rle; - *backward_refs_size = backward_refs_rle_size; - free(backward_refs_lz77); } + } else { + free(backward_refs_lz77); + *backward_refs = backward_refs_rle; + *backward_refs_size = backward_refs_rle_size; } if (use_2d_locality) { From 72920caa61e9e4b41b0a9778e0b8c5632533d323 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Tue, 24 Apr 2012 10:59:08 +0000 Subject: [PATCH 28/46] introduce -lossless option, protected by USE_LOSSLESS_ENCODER Change-Id: Ic5707082fefd964b9bcab0c9f9aa276992c22b06 --- examples/cwebp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/cwebp.c b/examples/cwebp.c index df14b83f..a04264db 100644 --- a/examples/cwebp.c +++ b/examples/cwebp.c @@ -838,6 +838,11 @@ int main(int argc, const char *argv[]) { } } else if (!strcmp(argv[c], "-noalpha")) { keep_alpha = 0; +#ifdef USE_LOSSLESS_ENCODER + } else if (!strcmp(argv[c], "-lossless")) { + config.lossless = 1; + picture.use_argb_input = 1; +#endif } else if (!strcmp(argv[c], "-size") && c < argc - 1) { config.target_size = strtol(argv[++c], NULL, 0); } else if (!strcmp(argv[c], "-psnr") && c < argc - 1) { From 83332b3c1636ae210e5b97ca7f403b1724bcf21a Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Wed, 25 Apr 2012 02:54:06 +0000 Subject: [PATCH 29/46] Make transform bits a function of encode method (-m). Change-Id: Idc392f7cba6e160ea068eacd7f82be4ebc971eaa --- src/enc/vp8l.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 744684db..e4472776 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -1002,7 +1002,8 @@ static WebPEncodingError WriteImage(VP8LEncoder* const enc, static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, WebPPicture* const picture) { - int sampling_bits = 9 - (((int)config->quality + 8) >> 4); + const int method = config->method; + const int histo_bits = 9 - (int)(config->quality / 16.f + .5f); VP8LEncoder* enc = (VP8LEncoder*)malloc(sizeof(*enc)); if (enc == NULL) { WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); @@ -1014,11 +1015,9 @@ static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, enc->pic_ = picture; enc->use_lz77_ = 1; - if (sampling_bits > 8) sampling_bits = 8; - if (sampling_bits < 3) sampling_bits = 3; - - enc->histo_bits_ = sampling_bits; - enc->transform_bits_ = sampling_bits; + enc->histo_bits_ = + (histo_bits < 3) ? 3 : (histo_bits > 8) ? 8 : histo_bits; + enc->transform_bits_ = (method < 4) ? 5 : (method > 4) ? 3 : 4; return enc; } From e491729905b2aef26cf2498a0b2d2fb0c8654549 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Wed, 25 Apr 2012 07:33:57 +0000 Subject: [PATCH 30/46] A quick pass of cleanup in backward reference code const correctness, renaming, cosmetics etc. Change-Id: I432befbb22f0eafd9a613f5f632398b6ef03c0f6 --- src/enc/backward_references.c | 333 ++++++++++++++++------------------ src/enc/backward_references.h | 107 +++++------ src/enc/histogram.c | 4 +- src/enc/vp8l.c | 10 +- 4 files changed, 207 insertions(+), 247 deletions(-) diff --git a/src/enc/backward_references.c b/src/enc/backward_references.c index 9044bcdc..6b948871 100644 --- a/src/enc/backward_references.c +++ b/src/enc/backward_references.c @@ -12,7 +12,6 @@ #include #include -#include #include #include "./backward_references.h" @@ -35,8 +34,8 @@ static const uint8_t plane_to_code_lut[128] = { static const int kMinLength = 2; int VP8LDistanceToPlaneCode(int xsize, int dist) { - int yoffset = dist / xsize; - int xoffset = dist - yoffset * xsize; + const int yoffset = dist / xsize; + const int xoffset = dist - yoffset * xsize; if (xoffset <= 8 && yoffset < 8) { return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1; } else if (xoffset > xsize - 8 && yoffset < 7) { @@ -45,14 +44,14 @@ int VP8LDistanceToPlaneCode(int xsize, int dist) { return dist + 120; } -static WEBP_INLINE int FindMatchLength(const uint32_t* array1, - const uint32_t* array2, +static WEBP_INLINE int FindMatchLength(const uint32_t* const array1, + const uint32_t* const array2, const int max_limit) { - int matched = 0; - while (matched < max_limit && array1[matched] == array2[matched]) { - ++matched; + int match_len = 0; + while (match_len < max_limit && array1[match_len] == array2[match_len]) { + ++match_len; } - return matched; + return match_len; } #define HASH_BITS 18 @@ -69,7 +68,7 @@ static WEBP_INLINE uint64_t GetHash64(uint64_t num) { return num; } -static WEBP_INLINE uint64_t GetPixPair(const uint32_t* argb) { +static WEBP_INLINE uint64_t GetPixPair(const uint32_t* const argb) { return ((uint64_t)(argb[1]) << 32) | argb[0]; } @@ -81,7 +80,7 @@ typedef struct { int32_t* chain_; } VP8LHashChain; -static int VP8LHashChain_Init(VP8LHashChain* p, int size) { +static int VP8LHashChainInit(VP8LHashChain* const p, int size) { int i; p->chain_ = (int*)malloc(size * sizeof(*p->chain_)); if (!p->chain_) { @@ -96,27 +95,25 @@ static int VP8LHashChain_Init(VP8LHashChain* p, int size) { return 1; } -static void VP8LHashChain_Delete(VP8LHashChain* p) { +static void VP8LHashChainClear(VP8LHashChain* const p) { if (p != NULL) { free(p->chain_); } } -static void VP8LHashChain_Insert(VP8LHashChain* p, - const uint32_t* argb, int32_t ix) { +static void VP8LHashChainInsert(VP8LHashChain* const p, + const uint32_t* const argb, int32_t pos) { // Insertion of two pixels at a time. const uint64_t key = GetPixPair(argb); const uint64_t hash_code = GetHash64(key); - p->chain_[ix] = p->hash_to_first_index_[hash_code]; - p->hash_to_first_index_[hash_code] = ix; + p->chain_[pos] = p->hash_to_first_index_[hash_code]; + p->hash_to_first_index_[hash_code] = pos; } -static int VP8LHashChain_FindCopy(VP8LHashChain* p, - int quality, - int index, int xsize, - const uint32_t* argb, - int maxlen, int* offset_out, - int* len_out) { +static int VP8LHashChainFindCopy( + const VP8LHashChain* const p, int quality, int index, int xsize, + const uint32_t* const argb, int maxlen, int* const distance_ptr, + int* const length_ptr) { const uint64_t next_two_pixels = GetPixPair(&argb[index]); const uint64_t hash_code = GetHash64(next_two_pixels); int prev_length = 0; @@ -124,33 +121,32 @@ static int VP8LHashChain_FindCopy(VP8LHashChain* p, int give_up = 10 + (quality >> 1); const int min_pos = (index > kWindowSize) ? index - kWindowSize : 0; int32_t pos; - int64_t length; int64_t val; - int x; - int y; - int len = 0; - int offset = 0; + int best_length = 0; + int best_distance = 0; for (pos = p->hash_to_first_index_[hash_code]; pos >= min_pos; pos = p->chain_[pos]) { + int curr_length; if (give_up < 0) { if (give_up < -quality * 2 || best_val >= 0xff0000) { break; } } --give_up; - if (len != 0 && argb[pos + len - 1] != argb[index + len - 1]) { + if (best_length != 0 && + argb[pos + best_length - 1] != argb[index + best_length - 1]) { continue; } - length = FindMatchLength(argb + pos, argb + index, maxlen); - if (length < prev_length) { + curr_length = FindMatchLength(argb + pos, argb + index, maxlen); + if (curr_length < prev_length) { continue; } - val = 65536 * length; + val = 65536 * curr_length; // Favoring 2d locality here gives savings for certain images. if (index - pos < 9 * xsize) { - y = (index - pos) / xsize; - x = (index - pos) % xsize; + const int y = (index - pos) / xsize; + int x = (index - pos) % xsize; if (x > xsize / 2) { x = xsize - x; } @@ -163,26 +159,26 @@ static int VP8LHashChain_FindCopy(VP8LHashChain* p, val -= 9 * 9 + 9 * 9; } if (best_val < val) { - prev_length = length; + prev_length = curr_length; best_val = val; - len = length; - offset = index - pos; - if (length >= kMaxLength) { + best_length = curr_length; + best_distance = index - pos; + if (curr_length >= kMaxLength) { break; } - if ((offset == 1 || offset == xsize) && len >= 128) { + if ((best_distance == 1 || best_distance == xsize) && + best_length >= 128) { break; } } } - *offset_out = offset; - *len_out = len; - return len >= kMinLength; + *distance_ptr = best_distance; + *length_ptr = best_length; + return best_length >= kMinLength; } -static WEBP_INLINE void PushBackCopy(int length, - PixOrCopy* stream, - int* stream_size) { +static WEBP_INLINE void PushBackCopy(int length, PixOrCopy* const stream, + int* const stream_size) { while (length >= kMaxLength) { stream[*stream_size] = PixOrCopyCreateCopy(1, kMaxLength); ++(*stream_size); @@ -194,38 +190,39 @@ static WEBP_INLINE void PushBackCopy(int length, } } -void VP8LBackwardReferencesRle(int xsize, int ysize, const uint32_t* argb, - PixOrCopy* stream, int* stream_size) { +void VP8LBackwardReferencesRle( + int xsize, int ysize, const uint32_t* const argb, PixOrCopy* const stream, + int* const stream_size) { const int pix_count = xsize * ysize; - int streak = 0; + int match_len = 0; int i; *stream_size = 0; for (i = 0; i < pix_count; ++i) { if (i >= 1 && argb[i] == argb[i - 1]) { - ++streak; + ++match_len; } else { - PushBackCopy(streak, stream, stream_size); - streak = 0; + PushBackCopy(match_len, stream, stream_size); + match_len = 0; stream[*stream_size] = PixOrCopyCreateLiteral(argb[i]); ++(*stream_size); } } - PushBackCopy(streak, stream, stream_size); + PushBackCopy(match_len, stream, stream_size); } // Returns 1 when successful. -int VP8LBackwardReferencesHashChain(int xsize, int ysize, int use_palette, - const uint32_t* argb, int palette_bits, - int quality, PixOrCopy* stream, - int* stream_size) { +int VP8LBackwardReferencesHashChain( + int xsize, int ysize, int use_color_cache, const uint32_t* const argb, + int cache_bits, int quality, PixOrCopy* const stream, + int* const stream_size) { int i; int ok = 0; const int pix_count = xsize * ysize; VP8LHashChain* hash_chain = (VP8LHashChain*)malloc(sizeof(*hash_chain)); VP8LColorCache hashers; if (!hash_chain || - !VP8LColorCacheInit(&hashers, palette_bits) || - !VP8LHashChain_Init(hash_chain, pix_count)) { + !VP8LColorCacheInit(&hashers, cache_bits) || + !VP8LHashChainInit(hash_chain, pix_count)) { goto Error; } *stream_size = 0; @@ -238,8 +235,8 @@ int VP8LBackwardReferencesHashChain(int xsize, int ysize, int use_palette, if (maxlen > kMaxLength) { maxlen = kMaxLength; } - VP8LHashChain_FindCopy(hash_chain, quality, - i, xsize, argb, maxlen, &offset, &len); + VP8LHashChainFindCopy(hash_chain, quality, i, xsize, argb, maxlen, + &offset, &len); } if (len >= kMinLength) { // Alternative#2: Insert the pixel at 'i' as literal, and code the @@ -247,19 +244,19 @@ int VP8LBackwardReferencesHashChain(int xsize, int ysize, int use_palette, int offset2 = 0; int len2 = 0; int k; - VP8LHashChain_Insert(hash_chain, &argb[i], i); + VP8LHashChainInsert(hash_chain, &argb[i], i); if (i < pix_count - 2) { // FindCopy(i+1,..) reads [i + 1] and [i + 2]. int maxlen = pix_count - (i + 1); if (maxlen > kMaxLength) { maxlen = kMaxLength; } - VP8LHashChain_FindCopy(hash_chain, quality, + VP8LHashChainFindCopy(hash_chain, quality, i + 1, xsize, argb, maxlen, &offset2, &len2); if (len2 > len + 1) { // Alternative#2 is a better match. So push pixel at 'i' as literal. - if (use_palette && VP8LColorCacheContains(&hashers, argb[i])) { + if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) { const int ix = VP8LColorCacheGetIndex(&hashers, argb[i]); - stream[*stream_size] = PixOrCopyCreatePaletteIx(ix); + stream[*stream_size] = PixOrCopyCreateCacheIdx(ix); } else { stream[*stream_size] = PixOrCopyCreateLiteral(argb[i]); } @@ -279,29 +276,29 @@ int VP8LBackwardReferencesHashChain(int xsize, int ysize, int use_palette, VP8LColorCacheInsert(&hashers, argb[i + k]); if (k != 0 && i + k + 1 < pix_count) { // Add to the hash_chain (but cannot add the last pixel). - VP8LHashChain_Insert(hash_chain, &argb[i + k], i + k); + VP8LHashChainInsert(hash_chain, &argb[i + k], i + k); } } i += len; } else { - if (use_palette && VP8LColorCacheContains(&hashers, argb[i])) { - // push pixel as a palette pixel + if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) { + // push pixel as a PixOrCopyCreateCacheIdx pixel int ix = VP8LColorCacheGetIndex(&hashers, argb[i]); - stream[*stream_size] = PixOrCopyCreatePaletteIx(ix); + stream[*stream_size] = PixOrCopyCreateCacheIdx(ix); } else { stream[*stream_size] = PixOrCopyCreateLiteral(argb[i]); } ++(*stream_size); VP8LColorCacheInsert(&hashers, argb[i]); if (i + 1 < pix_count) { - VP8LHashChain_Insert(hash_chain, &argb[i], i); + VP8LHashChainInsert(hash_chain, &argb[i], i); } ++i; } } ok = 1; Error: - VP8LHashChain_Delete(hash_chain); + VP8LHashChainClear(hash_chain); free(hash_chain); VP8LColorCacheClear(&hashers); return ok; @@ -313,12 +310,12 @@ typedef struct { double literal_[PIX_OR_COPY_CODES_MAX]; double blue_[VALUES_IN_BYTE]; double distance_[DISTANCE_CODES_MAX]; - int palette_bits_; + int cache_bits_; } CostModel; -static int CostModel_Build(CostModel* p, int xsize, int ysize, - int recursion_level, int use_palette, - const uint32_t* argb, int palette_bits) { +static int CostModelBuild(CostModel* const p, int xsize, int ysize, + int recursion_level, int use_color_cache, + const uint32_t* const argb, int cache_bits) { int ok = 0; int stream_size; VP8LHistogram histo; @@ -327,22 +324,22 @@ static int CostModel_Build(CostModel* p, int xsize, int ysize, if (stream == NULL) { goto Error; } - p->palette_bits_ = palette_bits; + p->cache_bits_ = cache_bits; if (recursion_level > 0) { if (!VP8LBackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1, - use_palette, argb, palette_bits, + use_color_cache, argb, cache_bits, &stream[0], &stream_size)) { goto Error; } } else { const int quality = 100; - if (!VP8LBackwardReferencesHashChain(xsize, ysize, use_palette, argb, - palette_bits, quality, + if (!VP8LBackwardReferencesHashChain(xsize, ysize, use_color_cache, argb, + cache_bits, quality, &stream[0], &stream_size)) { goto Error; } } - VP8LHistogramInit(&histo, palette_bits); + VP8LHistogramInit(&histo, cache_bits); for (i = 0; i < stream_size; ++i) { VP8LHistogramAddSinglePixOrCopy(&histo, stream[i]); } @@ -363,41 +360,35 @@ Error: return ok; } -static WEBP_INLINE double CostModel_LiteralCost(const CostModel* p, - uint32_t v) { +static WEBP_INLINE double GetLiteralCost(const CostModel* const p, uint32_t v) { return p->alpha_[v >> 24] + p->red_[(v >> 16) & 0xff] + p->literal_[(v >> 8) & 0xff] + p->blue_[v & 0xff]; } -static WEBP_INLINE double CostModel_PaletteCost(const CostModel* p, - uint32_t ix) { - int literal_ix = VALUES_IN_BYTE + kLengthCodes + ix; - return p->literal_[literal_ix]; +static WEBP_INLINE double GetCacheCost(const CostModel* const p, uint32_t idx) { + const int literal_idx = VALUES_IN_BYTE + kLengthCodes + idx; + return p->literal_[literal_idx]; } -static WEBP_INLINE double CostModel_LengthCost(const CostModel* p, - uint32_t len) { +static WEBP_INLINE double GetLengthCost(const CostModel* const p, + uint32_t length) { int code, extra_bits_count, extra_bits_value; - PrefixEncode(len, &code, &extra_bits_count, &extra_bits_value); + PrefixEncode(length, &code, &extra_bits_count, &extra_bits_value); return p->literal_[VALUES_IN_BYTE + code] + extra_bits_count; } -static WEBP_INLINE double CostModel_DistanceCost(const CostModel* p, - uint32_t distance) { +static WEBP_INLINE double GetDistanceCost(const CostModel* const p, + uint32_t distance) { int code, extra_bits_count, extra_bits_value; PrefixEncode(distance, &code, &extra_bits_count, &extra_bits_value); return p->distance_[code] + extra_bits_count; } static int BackwardReferencesHashChainDistanceOnly( - int xsize, int ysize, - int recursive_cost_model, - int use_palette, - const uint32_t* argb, - int palette_bits, - uint32_t* dist_array) { + int xsize, int ysize, int recursive_cost_model, int use_color_cache, + const uint32_t* const argb, int cache_bits, uint32_t* const dist_array) { const int quality = 100; const int pix_count = xsize * ysize; double* cost = (double*)malloc(pix_count * sizeof(*cost)); @@ -410,12 +401,12 @@ static int BackwardReferencesHashChainDistanceOnly( if (cost == NULL || cost_model == NULL || hash_chain == NULL || - !VP8LColorCacheInit(&hashers, palette_bits)) { + !VP8LColorCacheInit(&hashers, cache_bits)) { goto Error; } - VP8LHashChain_Init(hash_chain, pix_count); - CostModel_Build(cost_model, xsize, ysize, recursive_cost_model, - use_palette, argb, palette_bits); + VP8LHashChainInit(hash_chain, pix_count); + CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, + use_color_cache, argb, cache_bits); for (i = 0; i < pix_count; ++i) { cost[i] = 1e100; } @@ -436,17 +427,17 @@ static int BackwardReferencesHashChainDistanceOnly( if (maxlen > pix_count - i) { maxlen = pix_count - i; } - VP8LHashChain_FindCopy(hash_chain, quality, i, xsize, argb, maxlen, + VP8LHashChainFindCopy(hash_chain, quality, i, xsize, argb, maxlen, &offset, &len); } if (len >= kMinLength) { const int code = VP8LDistanceToPlaneCode(xsize, offset); const double distance_cost = - prev_cost + CostModel_DistanceCost(cost_model, code); + prev_cost + GetDistanceCost(cost_model, code); int k; for (k = 1; k < len; ++k) { const double cost_val = - distance_cost + CostModel_LengthCost(cost_model, k); + distance_cost + GetLengthCost(cost_model, k); if (cost[i + k] > cost_val) { cost[i + k] = cost_val; dist_array[i + k] = k + 1; @@ -462,7 +453,7 @@ static int BackwardReferencesHashChainDistanceOnly( VP8LColorCacheInsert(&hashers, argb[i + k]); if (i + k + 1 < pix_count) { // Add to the hash_chain (but cannot add the last pixel). - VP8LHashChain_Insert(hash_chain, &argb[i + k], i + k); + VP8LHashChainInsert(hash_chain, &argb[i + k], i + k); } } // 2) jump. @@ -472,7 +463,7 @@ static int BackwardReferencesHashChainDistanceOnly( } } if (i < pix_count - 1) { - VP8LHashChain_Insert(hash_chain, &argb[i], i); + VP8LHashChainInsert(hash_chain, &argb[i], i); } { // inserting a literal pixel @@ -483,11 +474,11 @@ static int BackwardReferencesHashChainDistanceOnly( mul0 = 0.68; mul1 = 0.82; } - if (use_palette && VP8LColorCacheContains(&hashers, argb[i])) { + if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) { int ix = VP8LColorCacheGetIndex(&hashers, argb[i]); - cost_val += CostModel_PaletteCost(cost_model, ix) * mul0; + cost_val += GetCacheCost(cost_model, ix) * mul0; } else { - cost_val += CostModel_LiteralCost(cost_model, argb[i]) * mul1; + cost_val += GetLiteralCost(cost_model, argb[i]) * mul1; } if (cost[i] > cost_val) { cost[i] = cost_val; @@ -501,7 +492,7 @@ static int BackwardReferencesHashChainDistanceOnly( // through cheaper means already. ok = 1; Error: - if (hash_chain) VP8LHashChain_Delete(hash_chain); + if (hash_chain) VP8LHashChainClear(hash_chain); free(hash_chain); free(cost_model); free(cost); @@ -509,8 +500,9 @@ Error: return ok; } -static void TraceBackwards(const uint32_t* dist_array, int dist_array_size, - uint32_t** chosen_path, int* chosen_path_size) { +static void TraceBackwards( + const uint32_t* const dist_array, int dist_array_size, + uint32_t** const chosen_path, int* const chosen_path_size) { int i; // Count how many. int count = 0; @@ -533,15 +525,9 @@ static void TraceBackwards(const uint32_t* dist_array, int dist_array_size, } static int BackwardReferencesHashChainFollowChosenPath( - int xsize, - int ysize, - int use_palette, - const uint32_t* argb, - int palette_bits, - uint32_t* chosen_path, - int chosen_path_size, - PixOrCopy* stream, - int* stream_size) { + int xsize, int ysize, int use_color_cache, const uint32_t* const argb, + int cache_bits, const uint32_t* const chosen_path, int chosen_path_size, + PixOrCopy* const stream, int* const stream_size) { const int quality = 100; const int pix_count = xsize * ysize; int i = 0; @@ -550,9 +536,9 @@ static int BackwardReferencesHashChainFollowChosenPath( int ok = 0; VP8LColorCache hashers; VP8LHashChain* hash_chain = (VP8LHashChain*)malloc(sizeof(*hash_chain)); - VP8LHashChain_Init(hash_chain, pix_count); + VP8LHashChainInit(hash_chain, pix_count); if (hash_chain == NULL || - !VP8LColorCacheInit(&hashers, palette_bits)) { + !VP8LColorCacheInit(&hashers, cache_bits)) { goto Error; } *stream_size = 0; @@ -561,7 +547,7 @@ static int BackwardReferencesHashChainFollowChosenPath( int len = 0; int maxlen = chosen_path[ix]; if (maxlen != 1) { - VP8LHashChain_FindCopy(hash_chain, quality, + VP8LHashChainFindCopy(hash_chain, quality, i, xsize, argb, maxlen, &offset, &len); assert(len == maxlen); stream[*stream_size] = PixOrCopyCreateCopy(offset, len); @@ -570,29 +556,29 @@ static int BackwardReferencesHashChainFollowChosenPath( VP8LColorCacheInsert(&hashers, argb[i + k]); if (i + k + 1 < pix_count) { // Add to the hash_chain (but cannot add the last pixel). - VP8LHashChain_Insert(hash_chain, &argb[i + k], i + k); + VP8LHashChainInsert(hash_chain, &argb[i + k], i + k); } } i += len; } else { - if (use_palette && VP8LColorCacheContains(&hashers, argb[i])) { - // push pixel as a palette pixel + if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) { + // push pixel as a color cache index int ix = VP8LColorCacheGetIndex(&hashers, argb[i]); - stream[*stream_size] = PixOrCopyCreatePaletteIx(ix); + stream[*stream_size] = PixOrCopyCreateCacheIdx(ix); } else { stream[*stream_size] = PixOrCopyCreateLiteral(argb[i]); } ++(*stream_size); VP8LColorCacheInsert(&hashers, argb[i]); if (i + 1 < pix_count) { - VP8LHashChain_Insert(hash_chain, &argb[i], i); + VP8LHashChainInsert(hash_chain, &argb[i], i); } ++i; } } ok = 1; Error: - VP8LHashChain_Delete(hash_chain); + VP8LHashChainClear(hash_chain); if (hash_chain) { free(hash_chain); } @@ -601,25 +587,22 @@ Error: } // Returns 1 on success. -int VP8LBackwardReferencesTraceBackwards(int xsize, int ysize, - int recursive_cost_model, - int use_palette, - const uint32_t* argb, - int palette_bits, - PixOrCopy* stream, - int* stream_size) { +int VP8LBackwardReferencesTraceBackwards( + int xsize, int ysize, int recursive_cost_model, int use_color_cache, + const uint32_t* const argb, int cache_bits, PixOrCopy* const stream, + int* const stream_size) { int ok = 0; const int dist_array_size = xsize * ysize; uint32_t* chosen_path = NULL; int chosen_path_size = 0; - uint32_t* const dist_array = (uint32_t*) - malloc(dist_array_size * sizeof(*dist_array)); + uint32_t* const dist_array = + (uint32_t*)malloc(dist_array_size * sizeof(*dist_array)); if (dist_array == NULL) { goto Error; } *stream_size = 0; if (!BackwardReferencesHashChainDistanceOnly( - xsize, ysize, recursive_cost_model, use_palette, argb, palette_bits, + xsize, ysize, recursive_cost_model, use_color_cache, argb, cache_bits, dist_array)) { free(dist_array); goto Error; @@ -627,7 +610,7 @@ int VP8LBackwardReferencesTraceBackwards(int xsize, int ysize, TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size); free(dist_array); if (!BackwardReferencesHashChainFollowChosenPath( - xsize, ysize, use_palette, argb, palette_bits, + xsize, ysize, use_color_cache, argb, cache_bits, chosen_path, chosen_path_size, stream, stream_size)) { goto Error; @@ -639,25 +622,24 @@ Error: } void VP8LBackwardReferences2DLocality(int xsize, int data_size, - PixOrCopy* data) { + PixOrCopy* const data) { int i; for (i = 0; i < data_size; ++i) { if (PixOrCopyIsCopy(&data[i])) { - int dist = data[i].argb_or_offset; + int dist = data[i].argb_or_distance; int transformed_dist = VP8LDistanceToPlaneCode(xsize, dist); - data[i].argb_or_offset = transformed_dist; + data[i].argb_or_distance = transformed_dist; } } } -int VP8LVerifyBackwardReferences(const uint32_t* argb, int xsize, int ysize, - int palette_bits, - const PixOrCopy* lit, - int lit_size) { +int VP8LVerifyBackwardReferences( + const uint32_t* const argb, int xsize, int ysize, int cache_bits, + const PixOrCopy* const lit, int lit_size) { int num_pixels = 0; int i; VP8LColorCache hashers; - VP8LColorCacheInit(&hashers, palette_bits); + VP8LColorCacheInit(&hashers, cache_bits); for (i = 0; i < lit_size; ++i) { if (PixOrCopyIsLiteral(&lit[i])) { if (argb[num_pixels] != PixOrCopyArgb(&lit[i])) { @@ -668,14 +650,14 @@ int VP8LVerifyBackwardReferences(const uint32_t* argb, int xsize, int ysize, } VP8LColorCacheInsert(&hashers, argb[num_pixels]); ++num_pixels; - } else if (PixOrCopyIsPaletteIx(&lit[i])) { - uint32_t palette_entry = - VP8LColorCacheLookup(&hashers, PixOrCopyPaletteIx(&lit[i])); - if (argb[num_pixels] != palette_entry) { - printf("i %d, pixel %d, original: 0x%08x, palette_ix: %d, " - "palette_entry: 0x%08x\n", - i, num_pixels, argb[num_pixels], PixOrCopyPaletteIx(&lit[i]), - palette_entry); + } else if (PixOrCopyIsCacheIdx(&lit[i])) { + uint32_t cache_entry = + VP8LColorCacheLookup(&hashers, PixOrCopyCacheIdx(&lit[i])); + if (argb[num_pixels] != cache_entry) { + printf("i %d, pixel %d, original: 0x%08x, cache_idx: %d, " + "cache_entry: 0x%08x\n", + i, num_pixels, argb[num_pixels], PixOrCopyCacheIdx(&lit[i]), + cache_entry); VP8LColorCacheClear(&hashers); return 0; } @@ -716,24 +698,25 @@ int VP8LVerifyBackwardReferences(const uint32_t* argb, int xsize, int ysize, } // Returns 1 on success. -static int ComputePaletteHistogram(const uint32_t* argb, int xsize, int ysize, - PixOrCopy* stream, int stream_size, - int palette_bits, VP8LHistogram* histo) { +static int ComputeCacheHistogram( + const uint32_t* const argb, int xsize, int ysize, + const PixOrCopy* const stream, int stream_size, int cache_bits, + VP8LHistogram* const histo) { int pixel_index = 0; int i; uint32_t k; VP8LColorCache hashers; - if (!VP8LColorCacheInit(&hashers, palette_bits)) { + if (!VP8LColorCacheInit(&hashers, cache_bits)) { return 0; } for (i = 0; i < stream_size; ++i) { const PixOrCopy v = stream[i]; if (PixOrCopyIsLiteral(&v)) { - if (palette_bits != 0 && + if (cache_bits != 0 && VP8LColorCacheContains(&hashers, argb[pixel_index])) { - // push pixel as a palette pixel + // push pixel as a cache index const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]); - VP8LHistogramAddSinglePixOrCopy(histo, PixOrCopyCreatePaletteIx(ix)); + VP8LHistogramAddSinglePixOrCopy(histo, PixOrCopyCreateCacheIdx(ix)); } else { VP8LHistogramAddSinglePixOrCopy(histo, v); } @@ -752,32 +735,32 @@ static int ComputePaletteHistogram(const uint32_t* argb, int xsize, int ysize, return 1; } -// Returns how many bits are to be used for a palette. -int VP8LCalculateEstimateForPaletteSize(const uint32_t* argb, - int xsize, int ysize, - int* best_palette_bits) { +// Returns how many bits are to be used for a color cache. +int VP8LCalculateEstimateForCacheSize( + const uint32_t* const argb, int xsize, int ysize, + int* const best_cache_bits) { int ok = 0; - int palette_bits; + int cache_bits; double lowest_entropy = 1e99; PixOrCopy* stream = (PixOrCopy*)malloc(xsize * ysize * sizeof(*stream)); int stream_size; - static const double kSmallPenaltyForLargePalette = 4.0; + static const double kSmallPenaltyForLargeCache = 4.0; static const int quality = 30; if (stream == NULL || !VP8LBackwardReferencesHashChain(xsize, ysize, 0, argb, 0, quality, stream, &stream_size)) { goto Error; } - for (palette_bits = 0; palette_bits < 12; ++palette_bits) { + for (cache_bits = 0; cache_bits < 12; ++cache_bits) { double cur_entropy; VP8LHistogram histo; - VP8LHistogramInit(&histo, palette_bits); - ComputePaletteHistogram(argb, xsize, ysize, &stream[0], stream_size, - palette_bits, &histo); + VP8LHistogramInit(&histo, cache_bits); + ComputeCacheHistogram(argb, xsize, ysize, &stream[0], stream_size, + cache_bits, &histo); cur_entropy = VP8LHistogramEstimateBits(&histo) + - kSmallPenaltyForLargePalette * palette_bits; - if (palette_bits == 0 || cur_entropy < lowest_entropy) { - *best_palette_bits = palette_bits; + kSmallPenaltyForLargeCache * cache_bits; + if (cache_bits == 0 || cur_entropy < lowest_entropy) { + *best_cache_bits = cache_bits; lowest_entropy = cur_entropy; } } diff --git a/src/enc/backward_references.h b/src/enc/backward_references.h index f82ee58a..22e53a4a 100644 --- a/src/enc/backward_references.h +++ b/src/enc/backward_references.h @@ -15,7 +15,6 @@ #include #include - #include "../webp/types.h" #if defined(__cplusplus) || defined(c_plusplus) @@ -28,7 +27,7 @@ extern "C" { // Compression constants #define CODE_LENGTH_CODES 19 static const int kLengthCodes = 24; -static const int kPaletteCodeBitsMax = 11; +static const int kColorCacheBitsMax = 11; #define PIX_OR_COPY_CODES_MAX (256 + 24 + (1 << 11)) static const int kMaxLength = 4096; @@ -89,7 +88,7 @@ static WEBP_INLINE void PrefixEncode( enum Mode { kLiteral, - kPaletteIx, + kCacheIdx, kCopy, kNone, }; @@ -98,24 +97,24 @@ typedef struct { // mode as uint8_t to make the memory layout to be exactly 8 bytes. uint8_t mode; uint16_t len; - uint32_t argb_or_offset; + uint32_t argb_or_distance; } PixOrCopy; -static WEBP_INLINE PixOrCopy PixOrCopyCreateCopy(uint32_t offset, +static WEBP_INLINE PixOrCopy PixOrCopyCreateCopy(uint32_t distance, uint16_t len) { PixOrCopy retval; retval.mode = kCopy; - retval.argb_or_offset = offset; + retval.argb_or_distance = distance; retval.len = len; return retval; } -static WEBP_INLINE PixOrCopy PixOrCopyCreatePaletteIx(int ix) { +static WEBP_INLINE PixOrCopy PixOrCopyCreateCacheIdx(int idx) { PixOrCopy retval; - assert(ix >= 0); - assert(ix < (1 << kPaletteCodeBitsMax)); - retval.mode = kPaletteIx; - retval.argb_or_offset = ix; + assert(idx >= 0); + assert(idx < (1 << kColorCacheBitsMax)); + retval.mode = kCacheIdx; + retval.argb_or_distance = idx; retval.len = 1; return retval; } @@ -123,111 +122,89 @@ static WEBP_INLINE PixOrCopy PixOrCopyCreatePaletteIx(int ix) { static WEBP_INLINE PixOrCopy PixOrCopyCreateLiteral(uint32_t argb) { PixOrCopy retval; retval.mode = kLiteral; - retval.argb_or_offset = argb; + retval.argb_or_distance = argb; retval.len = 1; return retval; } -static WEBP_INLINE int PixOrCopyIsLiteral(const PixOrCopy *p) { +static WEBP_INLINE int PixOrCopyIsLiteral(const PixOrCopy* const p) { return p->mode == kLiteral; } -static WEBP_INLINE int PixOrCopyIsPaletteIx(const PixOrCopy *p) { - return p->mode == kPaletteIx; +static WEBP_INLINE int PixOrCopyIsCacheIdx(const PixOrCopy* const p) { + return p->mode == kCacheIdx; } -static WEBP_INLINE int PixOrCopyIsCopy(const PixOrCopy *p) { +static WEBP_INLINE int PixOrCopyIsCopy(const PixOrCopy* const p) { return p->mode == kCopy; } -static WEBP_INLINE uint32_t PixOrCopyLiteral(const PixOrCopy *p, +static WEBP_INLINE uint32_t PixOrCopyLiteral(const PixOrCopy* const p, int component) { assert(p->mode == kLiteral); - return (p->argb_or_offset >> (component * 8)) & 0xff; + return (p->argb_or_distance >> (component * 8)) & 0xff; } -static WEBP_INLINE uint32_t PixOrCopyLength(const PixOrCopy *p) { +static WEBP_INLINE uint32_t PixOrCopyLength(const PixOrCopy* const p) { return p->len; } -static WEBP_INLINE uint32_t PixOrCopyArgb(const PixOrCopy *p) { +static WEBP_INLINE uint32_t PixOrCopyArgb(const PixOrCopy* const p) { assert(p->mode == kLiteral); - return p->argb_or_offset; + return p->argb_or_distance; } -static WEBP_INLINE uint32_t PixOrCopyPaletteIx(const PixOrCopy *p) { - assert(p->mode == kPaletteIx); - assert(p->argb_or_offset < (1 << kPaletteCodeBitsMax)); - return p->argb_or_offset; +static WEBP_INLINE uint32_t PixOrCopyCacheIdx(const PixOrCopy* const p) { + assert(p->mode == kCacheIdx); + assert(p->argb_or_distance < (1 << kColorCacheBitsMax)); + return p->argb_or_distance; } -static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy *p) { +static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy* const p) { assert(p->mode == kCopy); - return p->argb_or_offset; + return p->argb_or_distance; } -static WEBP_INLINE void PixOrCopyLengthCodeAndBits( - const PixOrCopy *p, int *code, int *n_bits, int *bits) { - assert(p->len >= 1 && p->len <= kMaxLength); - PrefixEncode(p->len, code, n_bits, bits); -} - - // Ridiculously simple backward references for images where it is unlikely // that there are large backward references (photos). void VP8LBackwardReferencesRle( - int xsize, - int ysize, - const uint32_t *argb, - PixOrCopy *stream, - int *stream_size); + int xsize, int ysize, const uint32_t* const argb, PixOrCopy* const stream, + int* const stream_size); // This is a simple fast function for obtaining backward references // based on simple heuristics. Returns 1 on success. int VP8LBackwardReferencesHashChain( - int xsize, - int ysize, - int use_palette, - const uint32_t *argb, - int palette_bits, - int quality, - PixOrCopy *stream, - int *stream_size); + int xsize, int ysize, int use_color_cache, const uint32_t* const argb, + int cache_bits, int quality, PixOrCopy* const stream, + int* const stream_size); // This method looks for a shortest path through the backward reference // network based on a cost model generated by a first round of compression. // Returns 1 on success. int VP8LBackwardReferencesTraceBackwards( - int xsize, - int ysize, - int recursive_cost_model, - int use_palette, - const uint32_t *argb, - int palette_bits, - PixOrCopy *stream, - int *stream_size); + int xsize, int ysize, int recursive_cost_model, int use_color_cache, + const uint32_t* const argb, int cache_bits, PixOrCopy* const stream, + int* const stream_size); // Convert backward references that are of linear distance along // the image scan lines to have a 2d locality indexing where // smaller values are used for backward references that are close by. void VP8LBackwardReferences2DLocality(int xsize, int data_size, - PixOrCopy *data); + PixOrCopy* const data); // Internals of locality transform exposed for testing use. int VP8LDistanceToPlaneCode(int xsize, int distance); // Returns true if the given backward references actually produce // the image given in tuple (argb, xsize, ysize). -int VP8LVerifyBackwardReferences(const uint32_t* argb, - int xsize, int ysize, - int palette_bits, - const PixOrCopy *lit, - int lit_size); +int VP8LVerifyBackwardReferences( + const uint32_t* const argb, int xsize, int ysize, int cache_bits, + const PixOrCopy* const lit, int lit_size); -// Produce an estimate for a good emerging palette size for the image. -int VP8LCalculateEstimateForPaletteSize(const uint32_t *argb, - int xsize, int ysize, - int *best_palette_bits); +// Produce an estimate for a good color cache size for the image. +int VP8LCalculateEstimateForCacheSize( + const uint32_t* const argb, int xsize, int ysize, + int* const best_cache_bits); #if defined(__cplusplus) || defined(c_plusplus) } diff --git a/src/enc/histogram.c b/src/enc/histogram.c index 5c08f97e..26244849 100644 --- a/src/enc/histogram.c +++ b/src/enc/histogram.c @@ -53,8 +53,8 @@ void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const p, ++p->red_[PixOrCopyLiteral(&v, 2)]; ++p->literal_[PixOrCopyLiteral(&v, 1)]; ++p->blue_[PixOrCopyLiteral(&v, 0)]; - } else if (PixOrCopyIsPaletteIx(&v)) { - int literal_ix = 256 + kLengthCodes + PixOrCopyPaletteIx(&v); + } else if (PixOrCopyIsCacheIdx(&v)) { + int literal_ix = 256 + kLengthCodes + PixOrCopyCacheIdx(&v); ++p->literal_[literal_ix]; } else { int code, extra_bits_count, extra_bits_value; diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index e4472776..69b009e5 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -702,8 +702,8 @@ static void StoreImageToBitMask( const int histogram_ix = histogram_symbols[histo_bits ? (y >> histo_bits) * histo_xsize + (x >> histo_bits) : 0]; - if (PixOrCopyIsPaletteIx(&v)) { - const int code = PixOrCopyPaletteIx(&v); + if (PixOrCopyIsCacheIdx(&v)) { + const int code = PixOrCopyCacheIdx(&v); int literal_ix = 256 + kLengthCodes + code; VP8LWriteBits(bw, bitdepths[5 * histogram_ix][literal_ix], bit_symbols[5 * histogram_ix][literal_ix]); @@ -719,7 +719,7 @@ static void StoreImageToBitMask( int bits, n_bits; int code, distance; int len_ix; - PixOrCopyLengthCodeAndBits(&v, &code, &n_bits, &bits); + PrefixEncode(v.len, &code, &n_bits, &bits); len_ix = 256 + code; VP8LWriteBits(bw, bitdepths[5 * histogram_ix][len_ix], bit_symbols[5 * histogram_ix][len_ix]); @@ -1202,8 +1202,8 @@ int VP8LEncodeImage(const WebPConfig* const config, if (cache_bits > 0) { if (quality > 25) { - if (!VP8LCalculateEstimateForPaletteSize(enc->argb_, enc->current_width_, - height, &cache_bits)) { + if (!VP8LCalculateEstimateForCacheSize(enc->argb_, enc->current_width_, + height, &cache_bits)) { err = VP8_ENC_ERROR_INVALID_CONFIGURATION; goto Error; } From 8415ddf3beebbefe4895619510c3936f34b800b4 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Wed, 25 Apr 2012 09:40:41 +0000 Subject: [PATCH 31/46] further simplification for the meta-Huffman coding * don't transmit the number of Huffman tree group explicitly * move color-cache information before the meta-Huffman block * also add a check that color_cache_bits is in [1..11] range, as per spec. Change-Id: I81d7711068653b509cdbc1151d93e229c4254580 --- src/dec/vp8l.c | 57 ++++++++++++++++++++------------------- src/enc/vp8l.c | 73 ++++++++++++++++++++------------------------------ 2 files changed, 58 insertions(+), 72 deletions(-) diff --git a/src/dec/vp8l.c b/src/dec/vp8l.c index 35720d71..d7f21ca0 100644 --- a/src/dec/vp8l.c +++ b/src/dec/vp8l.c @@ -31,6 +31,7 @@ static const int kCodeLengthRepeatOffsets[3] = { 3, 3, 11 }; #define NUM_LENGTH_CODES 24 #define NUM_DISTANCE_CODES 40 #define DEFAULT_CODE_LENGTH 8 +#define MAX_CACHE_BITS 11 // ----------------------------------------------------------------------------- // Five Huffman codes are used at each meta code: @@ -307,10 +308,9 @@ static void DeleteHtreeGroups(HTreeGroup* htree_groups, int num_htree_groups) { } static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, - int* const color_cache_bits_ptr) { + int color_cache_bits) { int ok = 0; int i, j; - int color_cache_size; VP8LBitReader* const br = &dec->br_; VP8LMetadata* const hdr = &dec->hdr_; uint32_t* huffman_image = NULL; @@ -318,11 +318,11 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, int num_htree_groups = 1; if (VP8LReadBits(br, 1)) { // use meta Huffman codes - int meta_codes_nbits; const int huffman_precision = VP8LReadBits(br, 4); const int huffman_xsize = VP8LSubSampleSize(xsize, huffman_precision); const int huffman_ysize = VP8LSubSampleSize(ysize, huffman_precision); const int huffman_pixs = huffman_xsize * huffman_ysize; + if (!DecodeImageStream(huffman_xsize, huffman_ysize, 0, dec, &huffman_image)) { dec->status_ = VP8_STATUS_BITSTREAM_ERROR; @@ -331,19 +331,12 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, hdr->huffman_subsample_bits_ = huffman_precision; for (i = 0; i < huffman_pixs; ++i) { // The huffman data is stored in red and green bytes. - huffman_image[i] = (huffman_image[i] >> 8) & 0xffff; + const int index = (huffman_image[i] >> 8) & 0xffff; + huffman_image[i] = index; + if (index >= num_htree_groups) { + num_htree_groups = index + 1; + } } - - meta_codes_nbits = VP8LReadBits(br, 4); - num_htree_groups = 2 + VP8LReadBits(br, meta_codes_nbits); - } - - if (VP8LReadBits(br, 1)) { // use color cache - *color_cache_bits_ptr = VP8LReadBits(br, 4); - color_cache_size = 1 << *color_cache_bits_ptr; - } else { - *color_cache_bits_ptr = 0; - color_cache_size = 0; } htree_groups = (HTreeGroup*)calloc(num_htree_groups, sizeof(*htree_groups)); @@ -357,10 +350,10 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, HuffmanTree* const htrees = htree_groups[i].htrees_; for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { int alphabet_size = kAlphabetSize[j]; - if (j == 0) { - alphabet_size += color_cache_size; + if (j == 0 && color_cache_bits > 0) { + alphabet_size += 1 << color_cache_bits; } - ok = ReadHuffmanCode(alphabet_size, dec, &htrees[j]); + ok = ReadHuffmanCode(alphabet_size, dec, htrees + j); ok = ok && !br->error_; } } @@ -835,29 +828,38 @@ static int DecodeImageStream(int xsize, int ysize, int ok = 1; int transform_xsize = xsize; int transform_ysize = ysize; + VP8LBitReader* const br = &dec->br_; VP8LMetadata* const hdr = &dec->hdr_; uint32_t* data = NULL; + const int transform_start_idx = dec->next_transform_; int color_cache_bits = 0; - VP8LBitReader* const br = &dec->br_; - int transform_start_idx = dec->next_transform_; - - // Step#1: Read the transforms (may recurse). + // Read the transforms (may recurse). if (is_level0) { while (ok && VP8LReadBits(br, 1)) { ok = ReadTransform(&transform_xsize, &transform_ysize, dec); } } - // Step#2: Read the Huffman codes (may recurse). - ok = ok && ReadHuffmanCodes(dec, transform_xsize, transform_ysize, - &color_cache_bits); + // Color cache + if (ok && VP8LReadBits(br, 1)) { + color_cache_bits = VP8LReadBits(br, 4); + ok = (color_cache_bits >= 1 && color_cache_bits <= MAX_CACHE_BITS); + if (!ok) { + dec->status_ = VP8_STATUS_BITSTREAM_ERROR; + goto End; + } + } + // Read the Huffman codes (may recurse). + ok = ok && ReadHuffmanCodes(dec, transform_xsize, transform_ysize, + color_cache_bits); if (!ok) { dec->status_ = VP8_STATUS_BITSTREAM_ERROR; goto End; } + // Finish setting up the color-cache if (color_cache_bits > 0) { hdr->color_cache_size_ = 1 << color_cache_bits; hdr->color_cache_ = (VP8LColorCache*)malloc(sizeof(*hdr->color_cache_)); @@ -868,7 +870,6 @@ static int DecodeImageStream(int xsize, int ysize, goto End; } } - UpdateDecoder(dec, transform_xsize, transform_ysize); if (is_level0) { // level 0 complete @@ -883,11 +884,11 @@ static int DecodeImageStream(int xsize, int ysize, goto End; } - // Step#3: Use the Huffman trees to decode the LZ77 encoded data. + // Use the Huffman trees to decode the LZ77 encoded data. ok = DecodeImageData(dec, data, transform_xsize, transform_ysize, 0); ok = ok && !br->error_; - // Step#4: Apply transforms on the decoded data. + // Apply transforms on the decoded data. if (ok) ApplyInverseTransforms(dec, transform_start_idx, data); End: diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 69b009e5..d7825671 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -27,18 +27,12 @@ extern "C" { #endif -static const uint32_t kImageSizeBits = 14; +static const int kImageSizeBits = 14; static int CompareColors(const void* p1, const void* p2) { const uint32_t a = *(const uint32_t*)p1; const uint32_t b = *(const uint32_t*)p2; - if (a < b) { - return -1; - } - if (a == b) { - return 0; - } - return 1; + return (a < b) ? -1 : (a > b) ? 1 : 0; } // If number of colors in the image is less than or equal to MAX_PALETTE_SIZE, @@ -511,14 +505,6 @@ static int GetHuffBitLengthsAndCodes( return 0; } -static void ShiftHistogramImage(uint32_t* image , int image_size) { - int i; - for (i = 0; i < image_size; ++i) { - image[i] <<= 8; - image[i] |= 0xff000000; - } -} - static void ClearHuffmanTreeIfOnlyOneSymbol(const int num_symbols, uint8_t* lengths, uint16_t* symbols) { @@ -758,8 +744,8 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, VP8LSubSampleSize(height, histogram_bits); VP8LHistogram** histogram_image; PixOrCopy* backward_refs; - uint32_t* histogram_symbols = (uint32_t*) - calloc(histogram_image_xysize, sizeof(*histogram_symbols)); + const size_t histo_size = histogram_image_xysize * sizeof(uint32_t); + uint32_t* const histogram_symbols = (uint32_t*)calloc(1, histo_size); if (histogram_symbols == NULL) goto Error; @@ -790,38 +776,37 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, goto Error; } - // Huffman image + meta huffman. - write_histogram_image = (histogram_image_size > 1); - VP8LWriteBits(bw, 1, write_histogram_image); - if (write_histogram_image) { - int image_size_bits; - uint32_t* histogram_argb = (uint32_t*) - malloc(histogram_image_xysize * sizeof(*histogram_argb)); - if (histogram_argb == NULL) goto Error; - memcpy(histogram_argb, histogram_symbols, - histogram_image_xysize * sizeof(*histogram_argb)); - - ShiftHistogramImage(histogram_argb, histogram_image_xysize); - VP8LWriteBits(bw, 4, histogram_bits); - if (!EncodeImageInternal(bw, histogram_argb, - VP8LSubSampleSize(width, histogram_bits), - VP8LSubSampleSize(height, histogram_bits), - quality, 0, 0)) { - free(histogram_argb); - goto Error; - } - image_size_bits = VP8LBitsLog2Ceiling(histogram_image_size - 1); - VP8LWriteBits(bw, 4, image_size_bits); - VP8LWriteBits(bw, image_size_bits, histogram_image_size - 2); - free(histogram_argb); - } - // Color Cache parameters. VP8LWriteBits(bw, 1, use_color_cache); if (use_color_cache) { VP8LWriteBits(bw, 4, cache_bits); } + // Huffman image + meta huffman. + write_histogram_image = (histogram_image_size > 1); + VP8LWriteBits(bw, 1, write_histogram_image); + if (write_histogram_image) { + uint32_t* const histogram_argb = (uint32_t*)malloc(histo_size); + int max_index = 0; + if (histogram_argb == NULL) goto Error; + for (i = 0; i < histogram_image_xysize; ++i) { + const int index = histogram_symbols[i] & 0xffff; + histogram_argb[i] = 0xff000000 | (index << 8); + if (index >= max_index) { + max_index = index + 1; + } + } + histogram_image_size = max_index; + + VP8LWriteBits(bw, 4, histogram_bits); + ok = EncodeImageInternal(bw, histogram_argb, + VP8LSubSampleSize(width, histogram_bits), + VP8LSubSampleSize(height, histogram_bits), + quality, 0, 0); + free(histogram_argb); + if (!ok) goto Error; + } + // Store Huffman codes. for (i = 0; i < histogram_image_size; ++i) { int k; From fbb501b8ee9e679b76f16c0c71aae245653ec785 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Wed, 25 Apr 2012 11:14:11 +0000 Subject: [PATCH 32/46] Restrict histo_bits to ensure histo_image size is under 32MB Change-Id: I75ccb65d56ee060b649de714287e71611a92c8e9 --- src/enc/vp8l.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index d7825671..c9675e7e 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -27,6 +27,8 @@ extern "C" { #endif +#define MAX_HUFF_IMAGE_SIZE (32 * 1024 * 1024) + static const int kImageSizeBits = 14; static int CompareColors(const void* p1, const void* p2) { @@ -985,10 +987,25 @@ static WebPEncodingError WriteImage(VP8LEncoder* const enc, return err; } +static int GetHistoBits(const WebPConfig* const config, + const WebPPicture* const pic) { + const int width = pic->width; + const int height = pic->height; + const int hist_size = sizeof(VP8LHistogram); + int histo_bits = 9 - (int)(config->quality / 16.f + .5f); + while (1) { + const size_t huff_image_size = VP8LSubSampleSize(width, histo_bits) * + VP8LSubSampleSize(height, histo_bits) * + hist_size; + if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break; + ++histo_bits; + } + return (histo_bits < 3) ? 3 : (histo_bits > 10) ? 10 : histo_bits; +} + static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, WebPPicture* const picture) { const int method = config->method; - const int histo_bits = 9 - (int)(config->quality / 16.f + .5f); VP8LEncoder* enc = (VP8LEncoder*)malloc(sizeof(*enc)); if (enc == NULL) { WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); @@ -999,10 +1016,8 @@ static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, enc->config_ = config; enc->pic_ = picture; enc->use_lz77_ = 1; - - enc->histo_bits_ = - (histo_bits < 3) ? 3 : (histo_bits > 8) ? 8 : histo_bits; enc->transform_bits_ = (method < 4) ? 5 : (method > 4) ? 3 : 4; + enc->histo_bits_ = GetHistoBits(config, picture); return enc; } From 31035f3b49d050fb23d9a90c94862cf163d0a0f9 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Wed, 25 Apr 2012 13:37:36 +0000 Subject: [PATCH 33/46] reduce memory usage by allocating only one histo instead of lz77+rle * introduce VP8LBackwardRefs structure and simplify the code by not passing around {PixOrCopy/int} pairs. More functions should be turned into using this struct (TODO(later)). Change-Id: I69c5c9fa61dddd61a2abc2824d70b8606a1c55b6 --- src/enc/vp8l.c | 139 +++++++++++++++++++++++++------------------------ 1 file changed, 71 insertions(+), 68 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index c9675e7e..5b95fdc1 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -165,102 +165,107 @@ static void BundleColorMap(const uint32_t* const argb, } } +// TODO(urvang): should be moved to backward_reference.h and used more +// as function parameters. +typedef struct { + PixOrCopy* refs; + int size; +} VP8LBackwardRefs; + +static void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs) { + if (refs != NULL) { + refs->refs = NULL; + refs->size = 0; + } +} + +static void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs) { + if (refs != NULL) { + free(refs->refs); + VP8LInitBackwardRefs(refs); + } +} + static int GetBackwardReferences(int width, int height, const uint32_t* argb, int quality, int use_color_cache, int cache_bits, int use_2d_locality, - PixOrCopy** backward_refs, - int* backward_refs_size) { + VP8LBackwardRefs* const best) { int ok = 0; - // Backward Reference using LZ77. int lz77_is_useful; - int backward_refs_rle_size; - int backward_refs_lz77_size; + VP8LBackwardRefs refs_rle, refs_lz77; const int num_pix = width * height; - VP8LHistogram* histo_rle; - PixOrCopy* backward_refs_lz77 = (PixOrCopy*) - malloc(num_pix * sizeof(*backward_refs_lz77)); - PixOrCopy* backward_refs_rle = (PixOrCopy*) - malloc(num_pix * sizeof(*backward_refs_lz77)); - VP8LHistogram* histo_lz77 = (VP8LHistogram*)malloc(2 * sizeof(*histo_lz77)); - if (backward_refs_lz77 == NULL || backward_refs_rle == NULL || - histo_lz77 == NULL) { - free(backward_refs_lz77); - free(backward_refs_rle); + refs_rle.refs = (PixOrCopy*)malloc(num_pix * sizeof(*refs_rle.refs)); + refs_lz77.refs = (PixOrCopy*)malloc(num_pix * sizeof(*refs_lz77.refs)); + + VP8LInitBackwardRefs(best); + if (refs_rle.refs == NULL || refs_lz77.refs == NULL) { + Error1: + VP8LClearBackwardRefs(&refs_rle); + VP8LClearBackwardRefs(&refs_lz77); goto End; } - *backward_refs = NULL; - histo_rle = histo_lz77 + 1; if (!VP8LBackwardReferencesHashChain(width, height, use_color_cache, argb, cache_bits, quality, - backward_refs_lz77, - &backward_refs_lz77_size)) { + refs_lz77.refs, &refs_lz77.size)) { goto End; } - VP8LHistogramInit(histo_lz77, cache_bits); - VP8LHistogramCreate(histo_lz77, backward_refs_lz77, backward_refs_lz77_size); - // Backward Reference using RLE only. - VP8LBackwardReferencesRle(width, height, argb, backward_refs_rle, - &backward_refs_rle_size); + VP8LBackwardReferencesRle(width, height, argb, refs_rle.refs, &refs_rle.size); - VP8LHistogramInit(histo_rle, cache_bits); - VP8LHistogramCreate(histo_rle, backward_refs_rle, backward_refs_rle_size); - - // Check if LZ77 is useful. - lz77_is_useful = (VP8LHistogramEstimateBits(histo_rle) > - VP8LHistogramEstimateBits(histo_lz77)); + { + int bit_cost_lz77, bit_cost_rle; + VP8LHistogram* const histo = (VP8LHistogram*)malloc(sizeof(*histo)); + if (histo == NULL) goto Error1; + // Evaluate lz77 coding + VP8LHistogramInit(histo, cache_bits); + VP8LHistogramCreate(histo, refs_lz77.refs, refs_lz77.size); + bit_cost_lz77 = (int)VP8LHistogramEstimateBits(histo); + // Evaluate RLE coding + VP8LHistogramInit(histo, cache_bits); + VP8LHistogramCreate(histo, refs_rle.refs, refs_rle.size); + bit_cost_rle = (int)VP8LHistogramEstimateBits(histo); + // Decide if LZ77 is useful. + lz77_is_useful = (bit_cost_lz77 < bit_cost_rle); + free(histo); + } // Choose appropriate backward reference. if (lz77_is_useful) { // TraceBackwards is costly. Run it for higher qualities. const int try_lz77_trace_backwards = (quality >= 75); - free(backward_refs_rle); + *best = refs_lz77; // default guess: lz77 is better + VP8LClearBackwardRefs(&refs_rle); if (try_lz77_trace_backwards) { const int recursion_level = (num_pix < 320 * 200) ? 1 : 0; - int backward_refs_trace_size; - PixOrCopy* backward_refs_trace; - backward_refs_trace = - (PixOrCopy*)malloc(num_pix * sizeof(*backward_refs_trace)); - if (backward_refs_trace == NULL) { - free(backward_refs_lz77); + VP8LBackwardRefs refs_trace; + refs_trace.refs = (PixOrCopy*)malloc(num_pix * sizeof(*refs_trace.refs)); + if (refs_trace.refs == NULL) { goto End; } if (VP8LBackwardReferencesTraceBackwards(width, height, recursion_level, - use_color_cache, argb, - cache_bits, backward_refs_trace, - &backward_refs_trace_size)) { - free(backward_refs_lz77); - *backward_refs = backward_refs_trace; - *backward_refs_size = backward_refs_trace_size; - } else { - free(backward_refs_trace); - *backward_refs = backward_refs_lz77; - *backward_refs_size = backward_refs_lz77_size; + use_color_cache, + argb, cache_bits, + refs_trace.refs, + &refs_trace.size)) { + VP8LClearBackwardRefs(&refs_lz77); + *best = refs_trace; } - } else { - *backward_refs = backward_refs_lz77; - *backward_refs_size = backward_refs_lz77_size; } } else { - free(backward_refs_lz77); - *backward_refs = backward_refs_rle; - *backward_refs_size = backward_refs_rle_size; + VP8LClearBackwardRefs(&refs_lz77); + *best = refs_rle; } - if (use_2d_locality) { - // Use backward reference with 2D locality. - VP8LBackwardReferences2DLocality(width, *backward_refs_size, - *backward_refs); + if (use_2d_locality) { // Use backward reference with 2D locality. + VP8LBackwardReferences2DLocality(width, best->size, best->refs); } ok = 1; End: - free(histo_lz77); if (!ok) { - free(*backward_refs); - *backward_refs = NULL; + VP8LClearBackwardRefs(best); } return ok; } @@ -739,13 +744,12 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, uint8_t** bit_lengths = NULL; uint16_t** bit_codes = NULL; const int use_2d_locality = 1; - int backward_refs_size; const int use_color_cache = (cache_bits > 0); const int color_cache_size = use_color_cache ? (1 << cache_bits) : 0; const int histogram_image_xysize = VP8LSubSampleSize(width, histogram_bits) * VP8LSubSampleSize(height, histogram_bits); VP8LHistogram** histogram_image; - PixOrCopy* backward_refs; + VP8LBackwardRefs refs; const size_t histo_size = histogram_image_xysize * sizeof(uint32_t); uint32_t* const histogram_symbols = (uint32_t*)calloc(1, histo_size); @@ -754,11 +758,11 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, // Calculate backward references from ARGB image. if (!GetBackwardReferences(width, height, argb, quality, use_color_cache, cache_bits, use_2d_locality, - &backward_refs, &backward_refs_size)) { + &refs)) { goto Error; } // Build histogram image & symbols from backward references. - if (!GetHistImageSymbols(width, height, backward_refs, backward_refs_size, + if (!GetHistImageSymbols(width, height, refs.refs, refs.size, quality, histogram_bits, cache_bits, &histogram_image, &histogram_image_size, histogram_symbols)) { @@ -834,16 +838,15 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, bit_codes[i]); } // Store actual literals. - StoreImageToBitMask(bw, width, histogram_bits, backward_refs, - backward_refs_size, histogram_symbols, - bit_lengths, bit_codes); + StoreImageToBitMask(bw, width, histogram_bits, refs.refs, refs.size, + histogram_symbols, bit_lengths, bit_codes); ok = 1; Error: if (!ok) { DeleteHistograms(histogram_image, histogram_image_size); } - free(backward_refs); + VP8LClearBackwardRefs(&refs); for (i = 0; i < 5 * histogram_image_size; ++i) { free(bit_lengths[i]); free(bit_codes[i]); From 01f50663dcf0e8ef3b5b7b468f76c948e85344fc Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Thu, 26 Apr 2012 07:19:24 +0000 Subject: [PATCH 34/46] code-moving and clean-up * removed use_lz77_ field, and added cache_bits_ one. * use more BackwardRefs params * move code around to organize more logically * reduce memory use on histo ... Change-Id: I833217a1b950189cf486704049e3fe28382ce335 --- src/enc/vp8l.c | 339 ++++++++++++++++++++++++++---------------------- src/enc/vp8li.h | 4 +- 2 files changed, 187 insertions(+), 156 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 5b95fdc1..cc8099d1 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -29,7 +29,17 @@ extern "C" { #define MAX_HUFF_IMAGE_SIZE (32 * 1024 * 1024) -static const int kImageSizeBits = 14; +// TODO(vikas): find a common place between enc and dec for these: +#define PREDICTOR_TRANSFORM 0 +#define CROSS_COLOR_TRANSFORM 1 +#define SUBTRACT_GREEN 2 +#define COLOR_INDEXING_TRANSFORM 3 +#define TRANSFORM_PRESENT 1 + +#define IMAGE_SIZE_BITS 14 + +// ----------------------------------------------------------------------------- +// Palette static int CompareColors(const void* p1, const void* p2) { const uint32_t a = *(const uint32_t*)p1; @@ -103,14 +113,14 @@ static int AnalyzeEntropy(const uint32_t const *argb, int xsize, int ysize, VP8LHistogramInit(predicted, 0); VP8LHistogramInit(nonpredicted, 0); for (i = 1; i < xsize * ysize; ++i) { - uint32_t pix_diff; - if ((argb[i] == argb[i - 1]) || - (i >= xsize && argb[i] == argb[i - xsize])) { + const uint32_t pix = argb[i]; + const uint32_t pix_diff = VP8LSubPixels(pix, argb[i - 1]); + if (pix_diff == 0) continue; + if (i >= xsize && pix == argb[i - xsize]) { continue; } VP8LHistogramAddSinglePixOrCopy(nonpredicted, - PixOrCopyCreateLiteral(argb[i])); - pix_diff = VP8LSubPixels(argb[i], argb[i - 1]); + PixOrCopyCreateLiteral(pix)); VP8LHistogramAddSinglePixOrCopy(predicted, PixOrCopyCreateLiteral(pix_diff)); } @@ -122,7 +132,7 @@ static int AnalyzeEntropy(const uint32_t const *argb, int xsize, int ysize, static int VP8LEncAnalyze(VP8LEncoder* const enc) { const WebPPicture* const pic = enc->pic_; - assert(pic && pic->argb); + assert(pic != NULL && pic->argb != NULL); enc->use_palette_ = AnalyzeAndCreatePalette(pic->argb, pic->width * pic->height, @@ -142,28 +152,7 @@ static int VP8LEncAnalyze(VP8LEncoder* const enc) { return 1; } -// Bundles multiple (2, 4 or 8) pixels into a single pixel. -// Returns the new xsize. -static void BundleColorMap(const uint32_t* const argb, - int width, int height, int xbits, - uint32_t* bundled_argb, int xs) { - int x, y; - const int bit_depth = 1 << (3 - xbits); - uint32_t code = 0; - - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - const int mask = (1 << xbits) - 1; - const int xsub = x & mask; - if (xsub == 0) { - code = 0; - } - // TODO(vikasa): simplify the bundling logic. - code |= (argb[y * width + x] & 0xff00) << (bit_depth * xsub); - bundled_argb[y * xs + (x >> xbits)] = 0xff000000 | code; - } - } -} +// ----------------------------------------------------------------------------- // TODO(urvang): should be moved to backward_reference.h and used more // as function parameters. @@ -281,8 +270,7 @@ static void DeleteHistograms(VP8LHistogram** histograms, int size) { } static int GetHistImageSymbols(int xsize, int ysize, - PixOrCopy* backward_refs, - int backward_refs_size, + const VP8LBackwardRefs* const refs, int quality, int histogram_bits, int cache_bits, VP8LHistogram*** histogram_image, @@ -296,7 +284,7 @@ static int GetHistImageSymbols(int xsize, int ysize, *histogram_image = NULL; if (!VP8LHistogramBuildImage(xsize, ysize, histogram_bits, cache_bits, - backward_refs, backward_refs_size, + refs->refs, refs->size, &histogram_image_raw, &histogram_image_raw_size)) { goto Error; @@ -682,7 +670,7 @@ static int StoreHuffmanCode(VP8LBitWriter* const bw, static void StoreImageToBitMask( VP8LBitWriter* const bw, int width, int histo_bits, - const PixOrCopy* literals, int literals_size, + const VP8LBackwardRefs* const refs, const uint32_t* histogram_symbols, uint8_t** const bitdepths, uint16_t** const bit_symbols) { // x and y trace the position in the image. @@ -690,21 +678,21 @@ static void StoreImageToBitMask( int y = 0; const int histo_xsize = histo_bits ? VP8LSubSampleSize(width, histo_bits) : 1; int i; - for (i = 0; i < literals_size; ++i) { - const PixOrCopy v = literals[i]; + for (i = 0; i < refs->size; ++i) { + const PixOrCopy* const v = &refs->refs[i]; const int histogram_ix = histogram_symbols[histo_bits ? (y >> histo_bits) * histo_xsize + (x >> histo_bits) : 0]; - if (PixOrCopyIsCacheIdx(&v)) { - const int code = PixOrCopyCacheIdx(&v); - int literal_ix = 256 + kLengthCodes + code; + if (PixOrCopyIsCacheIdx(v)) { + const int code = PixOrCopyCacheIdx(v); + const int literal_ix = 256 + kLengthCodes + code; VP8LWriteBits(bw, bitdepths[5 * histogram_ix][literal_ix], bit_symbols[5 * histogram_ix][literal_ix]); - } else if (PixOrCopyIsLiteral(&v)) { + } else if (PixOrCopyIsLiteral(v)) { static const int order[] = {1, 2, 0, 3}; int k; for (k = 0; k < 4; ++k) { - const int code = PixOrCopyLiteral(&v, order[k]); + const int code = PixOrCopyLiteral(v, order[k]); VP8LWriteBits(bw, bitdepths[5 * histogram_ix + k][code], bit_symbols[5 * histogram_ix + k][code]); } @@ -712,19 +700,19 @@ static void StoreImageToBitMask( int bits, n_bits; int code, distance; int len_ix; - PrefixEncode(v.len, &code, &n_bits, &bits); + PrefixEncode(v->len, &code, &n_bits, &bits); len_ix = 256 + code; VP8LWriteBits(bw, bitdepths[5 * histogram_ix][len_ix], bit_symbols[5 * histogram_ix][len_ix]); VP8LWriteBits(bw, n_bits, bits); - distance = PixOrCopyDistance(&v); + distance = PixOrCopyDistance(v); PrefixEncode(distance, &code, &n_bits, &bits); VP8LWriteBits(bw, bitdepths[5 * histogram_ix + 4][code], bit_symbols[5 * histogram_ix + 4][code]); VP8LWriteBits(bw, n_bits, bits); } - x += PixOrCopyLength(&v); + x += PixOrCopyLength(v); while (x >= width) { x -= width; ++y; @@ -762,7 +750,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, goto Error; } // Build histogram image & symbols from backward references. - if (!GetHistImageSymbols(width, height, refs.refs, refs.size, + if (!GetHistImageSymbols(width, height, &refs, quality, histogram_bits, cache_bits, &histogram_image, &histogram_image_size, histogram_symbols)) { @@ -838,7 +826,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, bit_codes[i]); } // Store actual literals. - StoreImageToBitMask(bw, width, histogram_bits, refs.refs, refs.size, + StoreImageToBitMask(bw, width, histogram_bits, &refs, histogram_symbols, bit_lengths, bit_codes); ok = 1; @@ -858,51 +846,60 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, return ok; } -static int EvalAndApplySubtractGreen(VP8LBitWriter* const bw, - VP8LEncoder* const enc, - int width, int height) { +// ----------------------------------------------------------------------------- +// Transforms + +// Check if it would be a good idea to subtract green from red and blue. We +// only impact entropy in red/blue components, don't bother to look at others. +static int EvalAndApplySubtractGreen(const VP8LEncoder* const enc, + int width, int height, + VP8LBitWriter* const bw) { if (!enc->use_palette_) { int i; - VP8LHistogram* before = NULL; - // Check if it would be a good idea to subtract green from red and blue. - VP8LHistogram* after = (VP8LHistogram*)malloc(2 * sizeof(*after)); - if (after == NULL) return 0; - before = after + 1; + const uint32_t* const argb = enc->argb_; + int bit_cost_before, bit_cost_after; + VP8LHistogram* const histo = (VP8LHistogram*)malloc(sizeof(*histo)); + if (histo == NULL) return 0; - VP8LHistogramInit(before, 1); - VP8LHistogramInit(after, 1); + VP8LHistogramInit(histo, 1); for (i = 0; i < width * height; ++i) { - // We only impact entropy in red and blue components, don't bother - // to look at others. - const uint32_t c = enc->argb_[i]; - const int green = (c >> 8) & 0xff; - ++(before->red_[(c >> 16) & 0xff]); - ++(before->blue_[c & 0xff]); - ++(after->red_[((c >> 16) - green) & 0xff]); - ++(after->blue_[(c - green) & 0xff]); + const uint32_t c = argb[i]; + ++histo->red_[(c >> 16) & 0xff]; + ++histo->blue_[(c >> 0) & 0xff]; } + bit_cost_before = VP8LHistogramEstimateBits(histo); + + VP8LHistogramInit(histo, 1); + for (i = 0; i < width * height; ++i) { + const uint32_t c = argb[i]; + const int green = (c >> 8) & 0xff; + ++histo->red_[((c >> 16) - green) & 0xff]; + ++histo->blue_[((c >> 0) - green) & 0xff]; + } + bit_cost_after = VP8LHistogramEstimateBits(histo); + free(histo); + // Check if subtracting green yields low entropy. - if (VP8LHistogramEstimateBits(after) < VP8LHistogramEstimateBits(before)) { - VP8LWriteBits(bw, 1, 1); - VP8LWriteBits(bw, 2, 2); + if (bit_cost_after < bit_cost_before) { + VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); + VP8LWriteBits(bw, 2, SUBTRACT_GREEN); VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height); } - free(after); } return 1; } -static int ApplyPredictFilter(VP8LBitWriter* const bw, - VP8LEncoder* const enc, - int width, int height, int quality) { +static int ApplyPredictFilter(const VP8LEncoder* const enc, + int width, int height, int quality, + VP8LBitWriter* const bw) { const int pred_bits = enc->transform_bits_; const int transform_width = VP8LSubSampleSize(width, pred_bits); const int transform_height = VP8LSubSampleSize(height, pred_bits); VP8LResidualImage(width, height, pred_bits, enc->argb_, enc->argb_scratch_, enc->transform_data_); - VP8LWriteBits(bw, 1, 1); - VP8LWriteBits(bw, 2, 0); + VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); + VP8LWriteBits(bw, 2, PREDICTOR_TRANSFORM); VP8LWriteBits(bw, 4, pred_bits); if (!EncodeImageInternal(bw, enc->transform_data_, transform_width, transform_height, quality, 0, 0)) { @@ -911,9 +908,9 @@ static int ApplyPredictFilter(VP8LBitWriter* const bw, return 1; } -static int ApplyCrossColorFilter(VP8LBitWriter* const bw, - VP8LEncoder* const enc, - int width, int height, int quality) { +static int ApplyCrossColorFilter(const VP8LEncoder* const enc, + int width, int height, int quality, + VP8LBitWriter* const bw) { const int ccolor_transform_bits = enc->transform_bits_; const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits); const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits); @@ -921,8 +918,8 @@ static int ApplyCrossColorFilter(VP8LBitWriter* const bw, VP8LColorSpaceTransform(width, height, ccolor_transform_bits, step, enc->argb_, enc->transform_data_); - VP8LWriteBits(bw, 1, 1); - VP8LWriteBits(bw, 2, 1); + VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); + VP8LWriteBits(bw, 2, CROSS_COLOR_TRANSFORM); VP8LWriteBits(bw, 4, ccolor_transform_bits); if (!EncodeImageInternal(bw, enc->transform_data_, transform_width, transform_height, quality, 0, 0)) { @@ -931,6 +928,8 @@ static int ApplyCrossColorFilter(VP8LBitWriter* const bw, return 1; } +// ----------------------------------------------------------------------------- + static void PutLE32(uint8_t* const data, uint32_t val) { data[0] = (val >> 0) & 0xff; data[1] = (val >> 8) & 0xff; @@ -938,7 +937,7 @@ static void PutLE32(uint8_t* const data, uint32_t val) { data[3] = (val >> 24) & 0xff; } -static WebPEncodingError WriteRiffHeader(VP8LEncoder* const enc, +static WebPEncodingError WriteRiffHeader(const VP8LEncoder* const enc, size_t riff_size, size_t vp8l_size) { const WebPPicture* const pic = enc->pic_; uint8_t riff[HEADER_SIZE + SIGNATURE_SIZE] = { @@ -956,7 +955,17 @@ static WebPEncodingError WriteRiffHeader(VP8LEncoder* const enc, return VP8_ENC_OK; } -static WebPEncodingError WriteImage(VP8LEncoder* const enc, +static void WriteImageSize(VP8LEncoder* const enc, VP8LBitWriter* const bw) { + WebPPicture* const pic = enc->pic_; + const int width = pic->width - 1; + const int height = pic->height -1; + assert(width < WEBP_MAX_DIMENSION && height < WEBP_MAX_DIMENSION); + + VP8LWriteBits(bw, IMAGE_SIZE_BITS, width); + VP8LWriteBits(bw, IMAGE_SIZE_BITS, height); +} + +static WebPEncodingError WriteImage(const VP8LEncoder* const enc, VP8LBitWriter* const bw) { size_t riff_size, vp8l_size, webpll_size, pad; const WebPPicture* const pic = enc->pic_; @@ -990,60 +999,12 @@ static WebPEncodingError WriteImage(VP8LEncoder* const enc, return err; } -static int GetHistoBits(const WebPConfig* const config, - const WebPPicture* const pic) { - const int width = pic->width; - const int height = pic->height; - const int hist_size = sizeof(VP8LHistogram); - int histo_bits = 9 - (int)(config->quality / 16.f + .5f); - while (1) { - const size_t huff_image_size = VP8LSubSampleSize(width, histo_bits) * - VP8LSubSampleSize(height, histo_bits) * - hist_size; - if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break; - ++histo_bits; - } - return (histo_bits < 3) ? 3 : (histo_bits > 10) ? 10 : histo_bits; -} - -static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, - WebPPicture* const picture) { - const int method = config->method; - VP8LEncoder* enc = (VP8LEncoder*)malloc(sizeof(*enc)); - if (enc == NULL) { - WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); - return NULL; - } - memset(enc, 0, sizeof(*enc)); - - enc->config_ = config; - enc->pic_ = picture; - enc->use_lz77_ = 1; - enc->transform_bits_ = (method < 4) ? 5 : (method > 4) ? 3 : 4; - enc->histo_bits_ = GetHistoBits(config, picture); - - return enc; -} - -static void WriteImageSize(VP8LEncoder* const enc, VP8LBitWriter* const bw) { - WebPPicture* const pic = enc->pic_; - const int width = pic->width - 1; - const int height = pic->height -1; - assert(width < WEBP_MAX_DIMENSION && height < WEBP_MAX_DIMENSION); - - VP8LWriteBits(bw, kImageSizeBits, width); - VP8LWriteBits(bw, kImageSizeBits, height); -} - -static void DeleteVP8LEncoder(VP8LEncoder* enc) { - free(enc->argb_); - free(enc); -} +// ----------------------------------------------------------------------------- // Allocates the memory for argb (W x H) buffer, 2 rows of context for // prediction and transform data. static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, - int height, int width) { + int width, int height) { WebPEncodingError err = VP8_ENC_OK; const size_t tile_size = 1 << enc->transform_bits_; const size_t image_size = height * width; @@ -1069,6 +1030,29 @@ static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, return err; } +// Bundles multiple (2, 4 or 8) pixels into a single pixel. +// Returns the new xsize. +static void BundleColorMap(const uint32_t* const argb, + int width, int height, int xbits, + uint32_t* bundled_argb, int xs) { + int x, y; + const int bit_depth = 1 << (3 - xbits); + uint32_t code = 0; + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + const int mask = (1 << xbits) - 1; + const int xsub = x & mask; + if (xsub == 0) { + code = 0; + } + // TODO(vikasa): simplify the bundling logic. + code |= (argb[y * width + x] & 0xff00) << (bit_depth * xsub); + bundled_argb[y * xs + (x >> xbits)] = 0xff000000 | code; + } + } +} + // Note: Expects "enc->palette_" to be set properly. // Also, "enc->palette_" will be modified after this call and should not be used // later. @@ -1077,7 +1061,7 @@ static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, int width, int height, int quality) { WebPEncodingError err = VP8_ENC_OK; int i; - uint32_t* argb = enc->pic_->argb; + uint32_t* const argb = enc->pic_->argb; uint32_t* const palette = enc->palette_; const int palette_size = enc->palette_size_; @@ -1094,8 +1078,8 @@ static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, } // Save palette to bitstream. - VP8LWriteBits(bw, 1, 1); - VP8LWriteBits(bw, 2, 3); + VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); + VP8LWriteBits(bw, 2, COLOR_INDEXING_TRANSFORM); VP8LWriteBits(bw, 8, palette_size - 1); for (i = palette_size - 1; i >= 1; --i) { palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); @@ -1113,7 +1097,7 @@ static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, } else if (palette_size <= 4) { xbits = 2; } - err = AllocateTransformBuffer(enc, height, VP8LSubSampleSize(width, xbits)); + err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height); if (err != VP8_ENC_OK) goto Error; BundleColorMap(argb, width, height, xbits, enc->argb_, enc->current_width_); } @@ -1122,10 +1106,60 @@ static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, return err; } +// ----------------------------------------------------------------------------- + +static int GetHistoBits(const WebPConfig* const config, + const WebPPicture* const pic) { + const int width = pic->width; + const int height = pic->height; + const size_t hist_size = sizeof(VP8LHistogram); + int histo_bits = 9 - (int)(config->quality / 16.f + .5f); + while (1) { + const size_t huff_image_size = VP8LSubSampleSize(width, histo_bits) * + VP8LSubSampleSize(height, histo_bits) * + hist_size; + if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break; + ++histo_bits; + } + return (histo_bits < 3) ? 3 : (histo_bits > 10) ? 10 : histo_bits; +} + +static void InitEncParams(VP8LEncoder* const enc) { + const WebPConfig* const config = enc->config_; + const WebPPicture* const picture = enc->pic_; + const int method = config->method; + const float quality = config->quality; + enc->transform_bits_ = (method < 4) ? 5 : (method > 4) ? 3 : 4; + enc->histo_bits_ = GetHistoBits(config, picture); + enc->cache_bits_ = (quality <= 25.f) ? 0 : 7; +} + +// ----------------------------------------------------------------------------- +// VP8LEncoder + +static VP8LEncoder* NewVP8LEncoder(const WebPConfig* const config, + WebPPicture* const picture) { + VP8LEncoder* const enc = (VP8LEncoder*)calloc(1, sizeof(*enc)); + if (enc == NULL) { + WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); + return NULL; + } + enc->config_ = config; + enc->pic_ = picture; + return enc; +} + +static void DeleteVP8LEncoder(VP8LEncoder* enc) { + free(enc->argb_); + free(enc); +} + +// ----------------------------------------------------------------------------- +// Main call + int VP8LEncodeImage(const WebPConfig* const config, WebPPicture* const picture) { int ok = 0; - int cache_bits = 7; // If equal to 0, don't use color cache. int width, height, quality; VP8LEncoder* enc = NULL; WebPEncodingError err = VP8_ENC_OK; @@ -1138,16 +1172,16 @@ int VP8LEncodeImage(const WebPConfig* const config, goto Error; } - enc = InitVP8LEncoder(config, picture); + enc = NewVP8LEncoder(config, picture); if (enc == NULL) { - err = VP8_ENC_ERROR_NULL_PARAMETER; + err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; } width = picture->width; height = picture->height; quality = config->quality; - VP8LBitWriterInit(&bw, (width * height) >> 1); + InitEncParams(enc); // --------------------------------------------------------------------------- // Analyze image (entropy, num_palettes etc) @@ -1158,18 +1192,19 @@ int VP8LEncodeImage(const WebPConfig* const config, } // Write image size. + VP8LBitWriterInit(&bw, (width * height) >> 1); WriteImageSize(enc, &bw); if (enc->use_palette_) { err = ApplyPalette(&bw, enc, width, height, quality); if (err != VP8_ENC_OK) goto Error; - cache_bits = 0; // Don't use color cache. + enc->cache_bits_ = 0; } // In case image is not packed. if (enc->argb_ == NULL) { const size_t image_size = height * width; - err = AllocateTransformBuffer(enc, height, width); + err = AllocateTransformBuffer(enc, width, height); if (err != VP8_ENC_OK) goto Error; memcpy(enc->argb_, picture->argb, image_size * sizeof(*enc->argb_)); enc->current_width_ = width; @@ -1178,40 +1213,36 @@ int VP8LEncodeImage(const WebPConfig* const config, // --------------------------------------------------------------------------- // Apply transforms and write transform data. - if (!EvalAndApplySubtractGreen(&bw, enc, enc->current_width_, height)) { + if (!EvalAndApplySubtractGreen(enc, enc->current_width_, height, &bw)) { err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; } if (enc->use_predict_) { - if (!ApplyPredictFilter(&bw, enc, enc->current_width_, height, quality)) { + if (!ApplyPredictFilter(enc, enc->current_width_, height, quality, &bw)) { err = VP8_ENC_ERROR_INVALID_CONFIGURATION; goto Error; } } if (enc->use_cross_color_) { - if (!ApplyCrossColorFilter(&bw, enc, enc->current_width_, height, - quality)) { + if (!ApplyCrossColorFilter(enc, enc->current_width_, height, quality, + &bw)) { err = VP8_ENC_ERROR_INVALID_CONFIGURATION; goto Error; } } - VP8LWriteBits(&bw, 1, 0); // No more transforms. + VP8LWriteBits(&bw, 1, !TRANSFORM_PRESENT); // No more transforms. // --------------------------------------------------------------------------- // Estimate the color cache size. - if (cache_bits > 0) { - if (quality > 25) { - if (!VP8LCalculateEstimateForCacheSize(enc->argb_, enc->current_width_, - height, &cache_bits)) { - err = VP8_ENC_ERROR_INVALID_CONFIGURATION; - goto Error; - } - } else { - cache_bits = 0; // Don't use color cache. + if (enc->cache_bits_ > 0) { + if (!VP8LCalculateEstimateForCacheSize(enc->argb_, enc->current_width_, + height, &enc->cache_bits_)) { + err = VP8_ENC_ERROR_INVALID_CONFIGURATION; + goto Error; } } @@ -1219,7 +1250,7 @@ int VP8LEncodeImage(const WebPConfig* const config, // Encode and write the transformed image. ok = EncodeImageInternal(&bw, enc->argb_, enc->current_width_, height, - quality, cache_bits, enc->histo_bits_); + quality, enc->cache_bits_, enc->histo_bits_); if (!ok) goto Error; err = WriteImage(enc, &bw); diff --git a/src/enc/vp8li.h b/src/enc/vp8li.h index 305c3451..6ddf6978 100644 --- a/src/enc/vp8li.h +++ b/src/enc/vp8li.h @@ -7,7 +7,7 @@ // // Lossless encoder: internal header. // -// Author: Vikas Arora(vikaas.arora@gmail.com) +// Author: Vikas Arora (vikaas.arora@gmail.com) #ifndef WEBP_ENC_VP8LI_H_ #define WEBP_ENC_VP8LI_H_ @@ -44,9 +44,9 @@ typedef struct { int current_width_; // Corresponds to packed image width. // Encoding parameters derived from quality parameter. - int use_lz77_; int histo_bits_; int transform_bits_; + int cache_bits_; // If equal to 0, don't use color cache. // Encoding parameters derived from image characteristics. int use_cross_color_; From 889a5786810cd64429d366415267ebb771a7b6df Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Thu, 26 Apr 2012 09:41:45 +0000 Subject: [PATCH 35/46] Improve predict vs no-predict heuristic. This improves compression density. For example, at quality 95 on 1000 PNGs: bpp(before) = 2.447 and bpp(after) = 2.412 Change-Id: I19c343ba05cca48a6940293721066502a5c3d693 --- src/enc/vp8l.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index cc8099d1..63ce1d4d 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -144,7 +144,7 @@ static int VP8LEncAnalyze(VP8LEncoder* const enc) { return 0; } - if (8 * pred_entropy < 7 * non_pred_entropy) { + if (20 * pred_entropy < 19 * non_pred_entropy) { enc->use_predict_ = 1; enc->use_cross_color_ = 1; } From bfc73db4a851250bd746c07129badecc782110cc Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Thu, 26 Apr 2012 09:55:14 +0000 Subject: [PATCH 36/46] Move GetHistImageSymbols to histogram.c Planning to revisit memory allocation scheme at several instanaces (next CL). Change-Id: Id7b9f4854e9577e10b53d5b1d8595b7d862e6e01 --- src/enc/backward_references.h | 5 +++ src/enc/histogram.c | 71 ++++++++++++++++++++++++++------- src/enc/histogram.h | 45 ++++++++++----------- src/enc/vp8l.c | 75 ++++------------------------------- 4 files changed, 90 insertions(+), 106 deletions(-) diff --git a/src/enc/backward_references.h b/src/enc/backward_references.h index 22e53a4a..7bcc3313 100644 --- a/src/enc/backward_references.h +++ b/src/enc/backward_references.h @@ -100,6 +100,11 @@ typedef struct { uint32_t argb_or_distance; } PixOrCopy; +typedef struct { + PixOrCopy* refs; + int size; +} VP8LBackwardRefs; + static WEBP_INLINE PixOrCopy PixOrCopyCreateCopy(uint32_t distance, uint16_t len) { PixOrCopy retval; diff --git a/src/enc/histogram.c b/src/enc/histogram.c index 26244849..ba232fb2 100644 --- a/src/enc/histogram.c +++ b/src/enc/histogram.c @@ -68,12 +68,11 @@ void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const p, } void VP8LHistogramCreate(VP8LHistogram* const p, - const PixOrCopy* const literal_and_length, - int n_literal_and_length) { + const VP8LBackwardRefs* const refs) { int i; VP8LHistogramClear(p); - for (i = 0; i < n_literal_and_length; ++i) { - VP8LHistogramAddSinglePixOrCopy(p, literal_and_length[i]); + for (i = 0; i < refs->size; ++i) { + VP8LHistogramAddSinglePixOrCopy(p, refs->refs[i]); } } @@ -196,11 +195,12 @@ double VP8LHistogramEstimateBitsHeader(const VP8LHistogram* const p) { HuffmanCost(&p->distance_[0], DISTANCE_CODES_MAX); } -int VP8LHistogramBuildImage(int xsize, int ysize, - int histobits, int palettebits, - const PixOrCopy* backward_refs, - int backward_refs_size, - VP8LHistogram*** image_arg, int* image_size) { +static int HistogramBuildImage(int xsize, int ysize, + int histobits, int palettebits, + const PixOrCopy* const backward_refs, + int backward_refs_size, + VP8LHistogram*** const image_arg, + int* const image_size) { int histo_xsize = histobits ? (xsize + (1 << histobits) - 1) >> histobits : 1; int histo_ysize = histobits ? (ysize + (1 << histobits) - 1) >> histobits : 1; int i; @@ -241,8 +241,9 @@ int VP8LHistogramBuildImage(int xsize, int ysize, return 1; } -int VP8LHistogramCombine(VP8LHistogram** in, int in_size, int quality, - VP8LHistogram*** out_arg, int* out_size) { +static int HistogramCombine(VP8LHistogram** const in, int in_size, int quality, + VP8LHistogram*** const out_arg, + int* const out_size) { int ok = 0; int i; unsigned int seed = 0; @@ -339,7 +340,7 @@ Error: static double HistogramDistance(const VP8LHistogram* const square_histogram, int cur_symbol, int candidate_symbol, - VP8LHistogram** candidate_histograms) { + VP8LHistogram** const candidate_histograms) { double new_bit_cost; double previous_bit_cost; VP8LHistogram modified; @@ -367,8 +368,9 @@ static double HistogramDistance(const VP8LHistogram* const square_histogram, return new_bit_cost - previous_bit_cost; } -void VP8LHistogramRefine(VP8LHistogram** raw, int raw_size, - uint32_t* symbols, int out_size, VP8LHistogram** out) { +static void HistogramRefine(VP8LHistogram** const raw, int raw_size, + uint32_t* const symbols, int out_size, + VP8LHistogram** const out) { int i; // Find the best 'out' histogram for each of the raw histograms for (i = 0; i < raw_size; ++i) { @@ -394,4 +396,45 @@ void VP8LHistogramRefine(VP8LHistogram** raw, int raw_size, } } +int VP8LGetHistImageSymbols(int xsize, int ysize, + const VP8LBackwardRefs* const refs, + int quality, int histogram_bits, + int cache_bits, + VP8LHistogram*** const histogram_image, + int* const histogram_image_size, + uint32_t* const histogram_symbols) { + // Build histogram image. + int ok = 0; + int i; + int histogram_image_raw_size; + VP8LHistogram** histogram_image_raw = NULL; + + *histogram_image = NULL; + if (!HistogramBuildImage(xsize, ysize, histogram_bits, cache_bits, + refs->refs, refs->size, + &histogram_image_raw, + &histogram_image_raw_size)) { + goto Error; + } + // Collapse similar histograms. + if (!HistogramCombine(histogram_image_raw, histogram_image_raw_size, + quality, histogram_image, histogram_image_size)) { + goto Error; + } + // Refine histogram image. + for (i = 0; i < histogram_image_raw_size; ++i) { + histogram_symbols[i] = -1; + } + HistogramRefine(histogram_image_raw, histogram_image_raw_size, + histogram_symbols, *histogram_image_size, *histogram_image); + ok = 1; + +Error: + if (!ok) { + VP8LDeleteHistograms(*histogram_image, *histogram_image_size); + } + VP8LDeleteHistograms(histogram_image_raw, histogram_image_raw_size); + return ok; +} + #endif diff --git a/src/enc/histogram.h b/src/enc/histogram.h index 7ce29f7b..400fb262 100644 --- a/src/enc/histogram.h +++ b/src/enc/histogram.h @@ -59,8 +59,7 @@ static WEBP_INLINE void VP8LHistogramInit(VP8LHistogram* const p, // The input data is the PixOrCopy data, which models the // literals, stop codes and backward references (both distances and lengths) void VP8LHistogramCreate(VP8LHistogram* const p, - const PixOrCopy* const literal_and_length, - int n_literal_and_length); + const VP8LBackwardRefs* const refs); void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const p, const PixOrCopy v); @@ -117,32 +116,28 @@ static WEBP_INLINE int VP8LHistogramNumCodes(const VP8LHistogram* const p) { return 256 + kLengthCodes + (1 << p->palette_code_bits_); } +static WEBP_INLINE void VP8LDeleteHistograms(VP8LHistogram** histograms, + int size) { + if (histograms != NULL) { + int i; + for (i = 0; i < size; ++i) { + free(histograms[i]); + } + free(histograms); + } +} + void VP8LConvertPopulationCountTableToBitEstimates( int n, const int* const population_counts, double* const output); -// Build a 2d image of histograms, subresolutioned by (1 << histobits) to -// the original image. -int VP8LHistogramBuildImage(int xsize, int ysize, - int histobits, int palette_bits, - const PixOrCopy* backward_refs, - int backward_refs_size, - VP8LHistogram*** image, - int* histogram_size); - -// Combines several histograms into fewer histograms. -int VP8LHistogramCombine(VP8LHistogram** in, - int in_size, - int quality, - VP8LHistogram*** out, - int* out_size); - -// Moves histograms from one cluster to another if smaller entropy can -// be achieved by doing that. -void VP8LHistogramRefine(VP8LHistogram** raw, - int raw_size, - uint32_t* symbols, - int out_size, - VP8LHistogram** out); +// Builds the histogram image. +int VP8LGetHistImageSymbols(int xsize, int ysize, + const VP8LBackwardRefs* const refs, + int quality, int histogram_bits, + int cache_bits, + VP8LHistogram*** histogram_image, + int* const histogram_image_size, + uint32_t* const histogram_symbols); #if defined(__cplusplus) || defined(c_plusplus) } diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 63ce1d4d..50b20f36 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -154,13 +154,6 @@ static int VP8LEncAnalyze(VP8LEncoder* const enc) { // ----------------------------------------------------------------------------- -// TODO(urvang): should be moved to backward_reference.h and used more -// as function parameters. -typedef struct { - PixOrCopy* refs; - int size; -} VP8LBackwardRefs; - static void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs) { if (refs != NULL) { refs->refs = NULL; @@ -209,11 +202,11 @@ static int GetBackwardReferences(int width, int height, if (histo == NULL) goto Error1; // Evaluate lz77 coding VP8LHistogramInit(histo, cache_bits); - VP8LHistogramCreate(histo, refs_lz77.refs, refs_lz77.size); + VP8LHistogramCreate(histo, &refs_lz77); bit_cost_lz77 = (int)VP8LHistogramEstimateBits(histo); // Evaluate RLE coding VP8LHistogramInit(histo, cache_bits); - VP8LHistogramCreate(histo, refs_rle.refs, refs_rle.size); + VP8LHistogramCreate(histo, &refs_rle); bit_cost_rle = (int)VP8LHistogramEstimateBits(histo); // Decide if LZ77 is useful. lz77_is_useful = (bit_cost_lz77 < bit_cost_rle); @@ -259,58 +252,6 @@ End: return ok; } -static void DeleteHistograms(VP8LHistogram** histograms, int size) { - if (histograms != NULL) { - int i; - for (i = 0; i < size; ++i) { - free(histograms[i]); - } - free(histograms); - } -} - -static int GetHistImageSymbols(int xsize, int ysize, - const VP8LBackwardRefs* const refs, - int quality, int histogram_bits, - int cache_bits, - VP8LHistogram*** histogram_image, - int* histogram_image_size, - uint32_t* histogram_symbols) { - // Build histogram image. - int ok = 0; - int i; - int histogram_image_raw_size; - VP8LHistogram** histogram_image_raw = NULL; - - *histogram_image = NULL; - if (!VP8LHistogramBuildImage(xsize, ysize, histogram_bits, cache_bits, - refs->refs, refs->size, - &histogram_image_raw, - &histogram_image_raw_size)) { - goto Error; - } - // Collapse similar histograms. - if (!VP8LHistogramCombine(histogram_image_raw, histogram_image_raw_size, - quality, histogram_image, histogram_image_size)) { - goto Error; - } - // Refine histogram image. - for (i = 0; i < histogram_image_raw_size; ++i) { - histogram_symbols[i] = -1; - } - VP8LHistogramRefine(histogram_image_raw, histogram_image_raw_size, - histogram_symbols, *histogram_image_size, - *histogram_image); - ok = 1; - -Error: - if (!ok) { - DeleteHistograms(*histogram_image, *histogram_image_size); - } - DeleteHistograms(histogram_image_raw, histogram_image_raw_size); - return ok; -} - // Heuristics for selecting the stride ranges to collapse. static int ValuesShouldBeCollapsedToStrideAverage(int a, int b) { return abs(a - b) < 4; @@ -750,10 +691,10 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, goto Error; } // Build histogram image & symbols from backward references. - if (!GetHistImageSymbols(width, height, &refs, - quality, histogram_bits, cache_bits, - &histogram_image, &histogram_image_size, - histogram_symbols)) { + if (!VP8LGetHistImageSymbols(width, height, &refs, + quality, histogram_bits, cache_bits, + &histogram_image, &histogram_image_size, + histogram_symbols)) { goto Error; } // Create Huffman bit lengths & codes for each histogram image. @@ -816,7 +757,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, } // Free combined histograms. - DeleteHistograms(histogram_image, histogram_image_size); + VP8LDeleteHistograms(histogram_image, histogram_image_size); histogram_image = NULL; // Emit no bits if there is only one symbol in the histogram. @@ -832,7 +773,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, Error: if (!ok) { - DeleteHistograms(histogram_image, histogram_image_size); + VP8LDeleteHistograms(histogram_image, histogram_image_size); } VP8LClearBackwardRefs(&refs); for (i = 0; i < 5 * histogram_image_size; ++i) { From b39e7487a7c1596532549bea8ae92b1905cec411 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Thu, 26 Apr 2012 10:25:49 +0000 Subject: [PATCH 37/46] Reducing emerging palette size from 11 to 9 bits. This is required to reduce memory used to construct histo_image. Change-Id: I491a06e10a3e3f3d8a00ecec286394378283ffea --- src/enc/backward_references.c | 2 +- src/enc/backward_references.h | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/enc/backward_references.c b/src/enc/backward_references.c index 6b948871..d6f6183f 100644 --- a/src/enc/backward_references.c +++ b/src/enc/backward_references.c @@ -751,7 +751,7 @@ int VP8LCalculateEstimateForCacheSize( stream, &stream_size)) { goto Error; } - for (cache_bits = 0; cache_bits < 12; ++cache_bits) { + for (cache_bits = 0; cache_bits <= kColorCacheBitsMax; ++cache_bits) { double cur_entropy; VP8LHistogram histo; VP8LHistogramInit(&histo, cache_bits); diff --git a/src/enc/backward_references.h b/src/enc/backward_references.h index 7bcc3313..d5e1de8b 100644 --- a/src/enc/backward_references.h +++ b/src/enc/backward_references.h @@ -27,8 +27,10 @@ extern "C" { // Compression constants #define CODE_LENGTH_CODES 19 static const int kLengthCodes = 24; -static const int kColorCacheBitsMax = 11; -#define PIX_OR_COPY_CODES_MAX (256 + 24 + (1 << 11)) +// The spec allows 11, we use 9 bits to reduce memory consumption in encoding. +// Having 9 instead of 11 removes about 0.25 % of compression density. +static const int kColorCacheBitsMax = 9; +#define PIX_OR_COPY_CODES_MAX (256 + 24 + (1 << 9)) static const int kMaxLength = 4096; // use GNU builtins where available. From a2849bc50218da4513bbe56ee9819171017d8593 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Thu, 26 Apr 2012 11:59:37 +0000 Subject: [PATCH 38/46] Lossless decoder: remove an unneeded param in ReadHuffmanCodeLengths(). Change-Id: I279452fdf38b680737d5ba6e868a219281bc8962 --- src/dec/vp8l.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dec/vp8l.c b/src/dec/vp8l.c index d7f21ca0..5be21965 100644 --- a/src/dec/vp8l.c +++ b/src/dec/vp8l.c @@ -172,7 +172,7 @@ static WEBP_INLINE int ReadSymbol(const HuffmanTree* tree, static int ReadHuffmanCodeLengths( VP8LDecoder* const dec, const int* const code_length_code_lengths, - int num_codes, int num_symbols, int* const code_lengths) { + int num_symbols, int* const code_lengths) { int ok = 0; VP8LBitReader* const br = &dec->br_; int symbol; @@ -180,7 +180,8 @@ static int ReadHuffmanCodeLengths( int prev_code_len = DEFAULT_CODE_LENGTH; HuffmanTree tree; - if (!HuffmanTreeBuildImplicit(&tree, code_length_code_lengths, num_codes)) { + if (!HuffmanTreeBuildImplicit(&tree, code_length_code_lengths, + NUM_CODE_LENGTH_CODES)) { dec->status_ = VP8_STATUS_BITSTREAM_ERROR; return 0; } @@ -278,9 +279,8 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, for (i = 0; i < num_codes; ++i) { code_length_code_lengths[kCodeLengthCodeOrder[i]] = VP8LReadBits(br, 3); } - ok = ReadHuffmanCodeLengths(dec, code_length_code_lengths, - NUM_CODE_LENGTH_CODES, - alphabet_size, code_lengths); + ok = ReadHuffmanCodeLengths(dec, code_length_code_lengths, alphabet_size, + code_lengths); if (ok) { ok = HuffmanTreeBuildImplicit(tree, code_lengths, alphabet_size); } From 781c01f42188093af1bd887805fd9b48b2e5dd87 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Fri, 27 Apr 2012 05:51:13 +0000 Subject: [PATCH 39/46] Simple Huffman code changes. No empty trees are codified with the simple Huffman code. The simple Huffman code is simplified to be either a 1-bit code or 8-bit code for symbols. Change-Id: I3e2813027b5a643862729339303d80197c497aff --- src/dec/vp8l.c | 30 +++++++++++------------------- src/enc/vp8l.c | 25 ++++++++++++------------- 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/src/dec/vp8l.c b/src/dec/vp8l.c index 5be21965..d73ffe27 100644 --- a/src/dec/vp8l.c +++ b/src/dec/vp8l.c @@ -238,25 +238,17 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, int symbols[2]; int codes[2]; int code_lengths[2]; - const int nbits = VP8LReadBits(br, 3); - const int num_symbols = 1 + ((nbits == 0) ? 0 : VP8LReadBits(br, 1)); - - if (nbits == 0) { - symbols[0] = 0; - codes[0] = 0; - code_lengths[0] = 0; - } else { - const int num_bits = (nbits - 1) * 2 + 4; - int i; - for (i = 0; i < num_symbols; ++i) { - symbols[i] = VP8LReadBits(br, num_bits); - if (symbols[i] >= alphabet_size) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - return 0; - } - codes[i] = i; - code_lengths[i] = num_symbols - 1; - } + const int num_symbols = VP8LReadBits(br, 1) + 1; + const int first_symbol_len_code = VP8LReadBits(br, 1); + // The first code is either 1 bit or 8 bit code. + symbols[0] = VP8LReadBits(br, (first_symbol_len_code == 0) ? 1 : 8); + codes[0] = 0; + code_lengths[0] = num_symbols - 1; + // The second code (if present), is always 8 bit long. + if (num_symbols == 2) { + symbols[1] = VP8LReadBits(br, 8); + codes[1] = 1; + code_lengths[1] = num_symbols - 1; } ok = HuffmanTreeBuildExplicit(tree, code_lengths, codes, symbols, num_symbols); diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 50b20f36..c666d7ea 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -531,20 +531,19 @@ static int StoreHuffmanCode(VP8LBitWriter* const bw, ++count; } } - if (count <= 2) { - int num_bits = 4; - // 0, 1 or 2 symbols to encode. - VP8LWriteBits(bw, 1, 1); - if (count == 0) { - VP8LWriteBits(bw, 3, 0); - ok = 1; - goto End; - } - while (symbols[count - 1] >= (1 << num_bits)) num_bits += 2; - VP8LWriteBits(bw, 3, (num_bits - 4) / 2 + 1); + if (count == 0) count = 1; + if (count <= 2 && symbols[0] < 256 && symbols[1] < 256) { + VP8LWriteBits(bw, 1, 1); // Small tree marker to encode 1 or 2 symbols. VP8LWriteBits(bw, 1, count - 1); - for (i = 0; i < count; ++i) { - VP8LWriteBits(bw, num_bits, symbols[i]); + if (symbols[0] <= 1) { + VP8LWriteBits(bw, 1, 0); // Code bit for small (1 bit) symbol value. + VP8LWriteBits(bw, 1, symbols[0]); + } else { + VP8LWriteBits(bw, 1, 1); + VP8LWriteBits(bw, 8, symbols[0]); + } + if (count == 2) { + VP8LWriteBits(bw, 8, symbols[1]); } ok = 1; goto End; From cf33ccd1604b485dc23921911027676eee20216c Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Fri, 27 Apr 2012 08:04:57 +0000 Subject: [PATCH 40/46] Evaluate output cluster's bit_costs once in HistogramRefine. Avoid bit_costs evaluated every time in function HistogramDistance. Also moved VP8LInitBackwardRefs and VP8LClearBackwardRefs to backward_references.h Change-Id: Id507f164d0fc64480aebc4a3ea3e6950ed377a60 --- src/enc/backward_references.h | 15 +++++++++ src/enc/histogram.c | 63 ++++++++++++++++++++--------------- src/enc/vp8l.c | 14 -------- 3 files changed, 52 insertions(+), 40 deletions(-) diff --git a/src/enc/backward_references.h b/src/enc/backward_references.h index d5e1de8b..2ba55d15 100644 --- a/src/enc/backward_references.h +++ b/src/enc/backward_references.h @@ -15,6 +15,7 @@ #include #include +#include #include "../webp/types.h" #if defined(__cplusplus) || defined(c_plusplus) @@ -172,6 +173,20 @@ static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy* const p) { return p->argb_or_distance; } +static WEBP_INLINE void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs) { + if (refs != NULL) { + refs->refs = NULL; + refs->size = 0; + } +} + +static WEBP_INLINE void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs) { + if (refs != NULL) { + free(refs->refs); + VP8LInitBackwardRefs(refs); + } +} + // Ridiculously simple backward references for images where it is unlikely // that there are large backward references (photos). void VP8LBackwardReferencesRle( diff --git a/src/enc/histogram.c b/src/enc/histogram.c index ba232fb2..0a4aa314 100644 --- a/src/enc/histogram.c +++ b/src/enc/histogram.c @@ -197,8 +197,7 @@ double VP8LHistogramEstimateBitsHeader(const VP8LHistogram* const p) { static int HistogramBuildImage(int xsize, int ysize, int histobits, int palettebits, - const PixOrCopy* const backward_refs, - int backward_refs_size, + const VP8LBackwardRefs* const backward_refs, VP8LHistogram*** const image_arg, int* const image_size) { int histo_xsize = histobits ? (xsize + (1 << histobits) - 1) >> histobits : 1; @@ -226,8 +225,8 @@ static int HistogramBuildImage(int xsize, int ysize, VP8LHistogramInit(image[i], palettebits); } // x and y trace the position in the image. - for (i = 0; i < backward_refs_size; ++i) { - const PixOrCopy v = backward_refs[i]; + for (i = 0; i < backward_refs->size; ++i) { + const PixOrCopy v = backward_refs->refs[i]; const int ix = histobits ? (y >> histobits) * histo_xsize + (x >> histobits) : 0; VP8LHistogramAddSinglePixOrCopy(image[ix], v); @@ -338,47 +337,54 @@ Error: // What is the bit cost of moving square_histogram from // cur_symbol to candidate_symbol. static double HistogramDistance(const VP8LHistogram* const square_histogram, - int cur_symbol, - int candidate_symbol, + int cur_symbol, int candidate_symbol, + const double* const symbol_bit_costs, VP8LHistogram** const candidate_histograms) { double new_bit_cost; double previous_bit_cost; - VP8LHistogram modified; + VP8LHistogram modified_histo; if (cur_symbol == candidate_symbol) { return 0; // Going nowhere. No savings. } - previous_bit_cost = - VP8LHistogramEstimateBits(candidate_histograms[candidate_symbol]); + previous_bit_cost = symbol_bit_costs[candidate_symbol]; if (cur_symbol != -1) { - previous_bit_cost += - VP8LHistogramEstimateBits(candidate_histograms[cur_symbol]); + previous_bit_cost += symbol_bit_costs[cur_symbol]; } // Compute the bit cost of the histogram where the data moves to. - modified = *candidate_histograms[candidate_symbol]; - VP8LHistogramAdd(&modified, square_histogram); - new_bit_cost = VP8LHistogramEstimateBits(&modified); + modified_histo = *candidate_histograms[candidate_symbol]; + VP8LHistogramAdd(&modified_histo, square_histogram); + new_bit_cost = VP8LHistogramEstimateBits(&modified_histo); // Compute the bit cost of the histogram where the data moves away. if (cur_symbol != -1) { - modified = *candidate_histograms[cur_symbol]; - VP8LHistogramRemove(&modified, square_histogram); - new_bit_cost += VP8LHistogramEstimateBits(&modified); + modified_histo = *candidate_histograms[cur_symbol]; + VP8LHistogramRemove(&modified_histo, square_histogram); + new_bit_cost += VP8LHistogramEstimateBits(&modified_histo); } return new_bit_cost - previous_bit_cost; } -static void HistogramRefine(VP8LHistogram** const raw, int raw_size, - uint32_t* const symbols, int out_size, - VP8LHistogram** const out) { +static int HistogramRefine(VP8LHistogram** const raw, int raw_size, + uint32_t* const symbols, + VP8LHistogram** const out, int out_size) { int i; + double* const symbol_bit_costs = + (double*)malloc(out_size * sizeof(*symbol_bit_costs)); + if (symbol_bit_costs == NULL) return 0; + for (i = 0; i < out_size; ++i) { + symbol_bit_costs[i] = VP8LHistogramEstimateBits(out[i]); + } + // Find the best 'out' histogram for each of the raw histograms for (i = 0; i < raw_size; ++i) { int best_out = 0; - double best_bits = HistogramDistance(raw[i], symbols[i], 0, out); + double best_bits = HistogramDistance(raw[i], symbols[i], 0, + symbol_bit_costs, out); int k; for (k = 1; k < out_size; ++k) { - double cur_bits = HistogramDistance(raw[i], symbols[i], k, out); + double cur_bits = HistogramDistance(raw[i], symbols[i], k, + symbol_bit_costs, out); if (cur_bits < best_bits) { best_bits = cur_bits; best_out = k; @@ -386,6 +392,7 @@ static void HistogramRefine(VP8LHistogram** const raw, int raw_size, } symbols[i] = best_out; } + free(out_bit_costs); // Recompute each out based on raw and symbols. for (i = 0; i < out_size; ++i) { @@ -394,6 +401,8 @@ static void HistogramRefine(VP8LHistogram** const raw, int raw_size, for (i = 0; i < raw_size; ++i) { VP8LHistogramAdd(out[symbols[i]], raw[i]); } + + return 1; } int VP8LGetHistImageSymbols(int xsize, int ysize, @@ -410,8 +419,7 @@ int VP8LGetHistImageSymbols(int xsize, int ysize, VP8LHistogram** histogram_image_raw = NULL; *histogram_image = NULL; - if (!HistogramBuildImage(xsize, ysize, histogram_bits, cache_bits, - refs->refs, refs->size, + if (!HistogramBuildImage(xsize, ysize, histogram_bits, cache_bits, refs, &histogram_image_raw, &histogram_image_raw_size)) { goto Error; @@ -425,8 +433,11 @@ int VP8LGetHistImageSymbols(int xsize, int ysize, for (i = 0; i < histogram_image_raw_size; ++i) { histogram_symbols[i] = -1; } - HistogramRefine(histogram_image_raw, histogram_image_raw_size, - histogram_symbols, *histogram_image_size, *histogram_image); + if (!HistogramRefine(histogram_image_raw, histogram_image_raw_size, + histogram_symbols, + *histogram_image, *histogram_image_size)) { + goto Error; + } ok = 1; Error: diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index c666d7ea..5c9d19be 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -154,20 +154,6 @@ static int VP8LEncAnalyze(VP8LEncoder* const enc) { // ----------------------------------------------------------------------------- -static void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs) { - if (refs != NULL) { - refs->refs = NULL; - refs->size = 0; - } -} - -static void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs) { - if (refs != NULL) { - free(refs->refs); - VP8LInitBackwardRefs(refs); - } -} - static int GetBackwardReferences(int width, int height, const uint32_t* argb, int quality, int use_color_cache, From ec123ca3f6bb89d964f4e26ced792fb692368fca Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Fri, 27 Apr 2012 08:17:00 +0000 Subject: [PATCH 41/46] Forgot to update out_bit_costs to symbol_bit_costs at one instance. Change-Id: Iaf952c0cb6e3fe35257d2503a16a437c6f2eb3aa --- src/enc/histogram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/enc/histogram.c b/src/enc/histogram.c index 0a4aa314..7e9b80bd 100644 --- a/src/enc/histogram.c +++ b/src/enc/histogram.c @@ -392,7 +392,7 @@ static int HistogramRefine(VP8LHistogram** const raw, int raw_size, } symbols[i] = best_out; } - free(out_bit_costs); + free(symbol_bit_costs); // Recompute each out based on raw and symbols. for (i = 0; i < out_size; ++i) { From ada6ff77df00f97852a48671080803f30bc7cd41 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Fri, 27 Apr 2012 11:06:24 +0000 Subject: [PATCH 42/46] Approximate FastLog between value range [256, 8192] Profiled data: Profiled few images and found that in the function VP8LFastLog, 90% of time table lookup is performed, while rest of time (10%) call to log function is made. Typical lookup accounts for 10 CPU instructions and call to log 200 instruction counts. The weighted average comes out to be 30 instructions per call. For mid qualities (25-75), this function (VP8LFastLog) accounts for 30-50% of total CPU cycles (via call path: VP8LCOlorSpaceTransform -> PredictionCostCrossColor -> ShannonEntropy). After this change, the log is called less that 1% of time, with average instructions being 15 per call. Measured the performance over 1000 files for various qualities and found overall compression speedup between 10-15% (in quality range [0, 75]). The compression density loss is around 0.5% (though at some qualities, compression is little better as well). Change-Id: I247bc6a8d4351819c871f19d65455dc23aea8650 --- src/dsp/lossless.c | 17 +++++++++++++---- src/dsp/lossless.h | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/dsp/lossless.c b/src/dsp/lossless.c index 8fbb9a95..b8b5ca6e 100644 --- a/src/dsp/lossless.c +++ b/src/dsp/lossless.c @@ -28,7 +28,8 @@ extern "C" { // computation. // // ", ".join(["%.16ff" % x for x in [0.0]+[log(x) for x in range(1, 256)]]) -static const float kLogTable[] = { +#define LOG_LOOKUP_IDX_MAX 256 +static const float kLogTable[LOG_LOOKUP_IDX_MAX] = { 0.0000000000000000f, 0.0000000000000000f, 0.6931471805599453f, 1.0986122886681098f, 1.3862943611198906f, 1.6094379124341003f, 1.7917594692280550f, 1.9459101490553132f, 2.0794415416798357f, @@ -117,9 +118,17 @@ static const float kLogTable[] = { 5.5412635451584258f }; -double VP8LFastLog(int v) { - if (v < (int)(sizeof(kLogTable) / sizeof(kLogTable[0]))) { - return kLogTable[v]; +#define APPROX_LOG_MAX 4096 +#define LOG_2_BASE_E 0.6931471805599453f + +float VP8LFastLog(int v) { + if (v < APPROX_LOG_MAX) { + int log_cnt = 0; + while (v >= LOG_LOOKUP_IDX_MAX) { + ++log_cnt; + v = v >> 1; + } + return kLogTable[v] + (log_cnt * LOG_2_BASE_E); } return log(v); } diff --git a/src/dsp/lossless.h b/src/dsp/lossless.h index 26c8ab02..60a6a7fe 100644 --- a/src/dsp/lossless.h +++ b/src/dsp/lossless.h @@ -64,7 +64,7 @@ static WEBP_INLINE uint32_t VP8LSubSampleSize(uint32_t size, #ifdef USE_LOSSLESS_ENCODER // Faster logarithm for small integers, with the property of log(0) == 0. -double VP8LFastLog(int v); +float VP8LFastLog(int v); // In-place difference of each component with mod 256. static WEBP_INLINE uint32_t VP8LSubPixels(uint32_t a, uint32_t b) { From 41b5c8ff7133d911b85510099e64b0b0720463da Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Fri, 27 Apr 2012 19:37:50 +0000 Subject: [PATCH 43/46] Some cosmetics in histogram.c Change-Id: I5d5872a793759fad593dba88c3f593f72b328b0c --- src/enc/histogram.c | 195 ++++++++++++++++++++------------------------ src/enc/histogram.h | 2 +- src/enc/vp8l.c | 9 +- 3 files changed, 94 insertions(+), 112 deletions(-) diff --git a/src/enc/histogram.c b/src/enc/histogram.c index 7e9b80bd..662ba19c 100644 --- a/src/enc/histogram.c +++ b/src/enc/histogram.c @@ -195,40 +195,29 @@ double VP8LHistogramEstimateBitsHeader(const VP8LHistogram* const p) { HuffmanCost(&p->distance_[0], DISTANCE_CODES_MAX); } -static int HistogramBuildImage(int xsize, int ysize, - int histobits, int palettebits, +static int HistogramBuildImage(int xsize, int histo_bits, int cache_bits, const VP8LBackwardRefs* const backward_refs, - VP8LHistogram*** const image_arg, - int* const image_size) { - int histo_xsize = histobits ? (xsize + (1 << histobits) - 1) >> histobits : 1; - int histo_ysize = histobits ? (ysize + (1 << histobits) - 1) >> histobits : 1; + VP8LHistogram** const image, + int image_size) { int i; + const int histo_xsize = + histo_bits ? (xsize + (1 << histo_bits) - 1) >> histo_bits : 1; int x = 0; int y = 0; - VP8LHistogram** image; - *image_arg = NULL; - *image_size = histo_xsize * histo_ysize; - image = (VP8LHistogram**)calloc(*image_size, sizeof(*image)); - if (image == NULL) { - return 0; - } - for (i = 0; i < *image_size; ++i) { + for (i = 0; i < image_size; ++i) { image[i] = (VP8LHistogram*)malloc(sizeof(*image[i])); - if (!image[i]) { + if (image[i] == NULL) { int k; - for (k = 0; k < *image_size; ++k) { - free(image[k]); - } - free(image); + for (k = 0; k < i; ++k) free(image[k]); return 0; } - VP8LHistogramInit(image[i], palettebits); + VP8LHistogramInit(image[i], cache_bits); } // x and y trace the position in the image. for (i = 0; i < backward_refs->size; ++i) { const PixOrCopy v = backward_refs->refs[i]; const int ix = - histobits ? (y >> histobits) * histo_xsize + (x >> histobits) : 0; + histo_bits ? (y >> histo_bits) * histo_xsize + (x >> histo_bits) : 0; VP8LHistogramAddSinglePixOrCopy(image[ix], v); x += PixOrCopyLength(&v); while (x >= xsize) { @@ -236,83 +225,72 @@ static int HistogramBuildImage(int xsize, int ysize, ++y; } } - *image_arg = image; return 1; } -static int HistogramCombine(VP8LHistogram** const in, int in_size, int quality, - VP8LHistogram*** const out_arg, - int* const out_size) { +static int HistogramCombine(VP8LHistogram** const in, int in_size, + int num_pairs, VP8LHistogram** const out, + int* const out_size_arg) { int ok = 0; int i; unsigned int seed = 0; int tries_with_no_success = 0; - int inner_iters = 10 + quality / 2; - int iter; - double* bit_costs = (double*)malloc(in_size * sizeof(*bit_costs)); - VP8LHistogram** out = (VP8LHistogram**)calloc(in_size, sizeof(*out)); - *out_arg = out; - *out_size = in_size; - if (bit_costs == NULL || out == NULL) { - goto Error; - } - // Copy + const int outer_iters = in_size * 3; + double* const bit_costs = (double*)malloc(in_size * sizeof(*bit_costs)); + // TODO(urvang): 'bit_cost' should be part of VP8LHistogram. + VP8LHistogram* const combo = (VP8LHistogram*)malloc(sizeof(*combo)); + int out_size = in_size; + if (bit_costs == NULL || out == NULL || combo == NULL) goto End; + + // Copy histogram 'in' to 'out'. for (i = 0; i < in_size; ++i) { - VP8LHistogram* new_histo = (VP8LHistogram*)malloc(sizeof(*new_histo)); - if (new_histo == NULL) { - goto Error; - } - *new_histo = *(in[i]); - out[i] = new_histo; + VP8LHistogram* const temp = (VP8LHistogram*)malloc(sizeof(*temp)); + if (temp == NULL) goto End; + *temp = *(in[i]); + out[i] = temp; bit_costs[i] = VP8LHistogramEstimateBits(out[i]); } - // Collapse similar histograms. - for (iter = 0; iter < in_size * 3 && *out_size >= 2; ++iter) { - double best_val = 0; - int best_ix0 = 0; - int best_ix1 = 0; - // Try a few times. - int k; - for (k = 0; k < inner_iters; ++k) { - // Choose two, build a combo out of them. - double cost_val; - VP8LHistogram* combo; - int ix0 = rand_r(&seed) % *out_size; - int ix1; - int diff = ((k & 7) + 1) % (*out_size - 1); - if (diff >= 3) { - diff = rand_r(&seed) % (*out_size - 1); - } - ix1 = (ix0 + diff + 1) % *out_size; - if (ix0 == ix1) { + + // Collapse similar histograms in 'out'. + for (i = 0; i < outer_iters && out_size >= 2; ++i) { + // We pick the best pair to be combined out of 'inner_iters' pairs. + double best_cost_diff = 0; + int best_idx1 = 0; + int best_idx2 = 0; + int j; + for (j = 0; j < num_pairs; ++j) { + double curr_cost_diff; + // Choose two histograms at random and try to combine them. + const int idx1 = rand_r(&seed) % out_size; + const int tmp = ((j & 7) + 1) % (out_size - 1); + const int diff = (tmp < 3) ? tmp : rand_r(&seed) % (out_size - 1); + const int idx2 = (idx1 + diff + 1) % out_size; + if (idx1 == idx2) { continue; } - combo = (VP8LHistogram*)malloc(sizeof(*combo)); - if (combo == NULL) { - goto Error; + *combo = *out[idx1]; + VP8LHistogramAdd(combo, out[idx2]); + // Calculate cost reduction on combining. + curr_cost_diff = + VP8LHistogramEstimateBits(combo) - bit_costs[idx1] - bit_costs[idx2]; + if (best_cost_diff > curr_cost_diff) { + best_cost_diff = curr_cost_diff; + best_idx1 = idx1; + best_idx2 = idx2; } - *combo = *out[ix0]; - VP8LHistogramAdd(combo, out[ix1]); - cost_val = - VP8LHistogramEstimateBits(combo) - bit_costs[ix0] - bit_costs[ix1]; - if (best_val > cost_val) { - best_val = cost_val; - best_ix0 = ix0; - best_ix1 = ix1; - } - free(combo); } - if (best_val < 0.0) { - VP8LHistogramAdd(out[best_ix0], out[best_ix1]); - bit_costs[best_ix0] = - best_val + bit_costs[best_ix0] + bit_costs[best_ix1]; - // Erase (*out)[best_ix1] - free(out[best_ix1]); - memmove(&out[best_ix1], &out[best_ix1 + 1], - (*out_size - best_ix1 - 1) * sizeof(out[0])); - memmove(&bit_costs[best_ix1], &bit_costs[best_ix1 + 1], - (*out_size - best_ix1 - 1) * sizeof(bit_costs[0])); - --(*out_size); + + if (best_cost_diff < 0.0) { + // Combine this pair and store the combined histogram at 'best_idx1'. + VP8LHistogramAdd(out[best_idx1], out[best_idx2]); + bit_costs[best_idx1] = + best_cost_diff + bit_costs[best_idx1] + bit_costs[best_idx2]; + free(out[best_idx2]); + memmove(&out[best_idx2], &out[best_idx2 + 1], + (out_size - best_idx2 - 1) * sizeof(*out)); + memmove(&bit_costs[best_idx2], &bit_costs[best_idx2 + 1], + (out_size - best_idx2 - 1) * sizeof(*bit_costs)); + --out_size; tries_with_no_success = 0; } if (++tries_with_no_success >= 50) { @@ -320,16 +298,17 @@ static int HistogramCombine(VP8LHistogram** const in, int in_size, int quality, } } ok = 1; -Error: + End: free(bit_costs); + free(combo); if (!ok) { - if (out) { - int i; - for (i = 0; i < *out_size; ++i) { - free(out[i]); - } + if (out != NULL) { + for (i = 0; i < out_size; ++i) free(out[i]); free(out); } + *out_size_arg = 0; + } else { + *out_size_arg = out_size; } return ok; } @@ -407,44 +386,46 @@ static int HistogramRefine(VP8LHistogram** const raw, int raw_size, int VP8LGetHistImageSymbols(int xsize, int ysize, const VP8LBackwardRefs* const refs, - int quality, int histogram_bits, + int quality, int histo_bits, int cache_bits, - VP8LHistogram*** const histogram_image, + VP8LHistogram** const histogram_image, int* const histogram_image_size, uint32_t* const histogram_symbols) { - // Build histogram image. int ok = 0; int i; - int histogram_image_raw_size; - VP8LHistogram** histogram_image_raw = NULL; + const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1; + const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1; + const int num_histo_pairs = 10 + quality / 2; // For HistogramCombine(). + const int histo_image_raw_size = histo_xsize * histo_ysize; + VP8LHistogram** const histo_image_raw = + (VP8LHistogram**)calloc(histo_image_raw_size, sizeof(*histo_image_raw)); + if (histo_image_raw == NULL) return 0; - *histogram_image = NULL; - if (!HistogramBuildImage(xsize, ysize, histogram_bits, cache_bits, refs, - &histogram_image_raw, - &histogram_image_raw_size)) { + // Build histogram image. + if (!HistogramBuildImage(xsize, histo_bits, cache_bits, refs, + histo_image_raw, histo_image_raw_size)) { goto Error; } // Collapse similar histograms. - if (!HistogramCombine(histogram_image_raw, histogram_image_raw_size, - quality, histogram_image, histogram_image_size)) { + if (!HistogramCombine(histo_image_raw, histo_image_raw_size, num_histo_pairs, + histogram_image, histogram_image_size)) { goto Error; } // Refine histogram image. - for (i = 0; i < histogram_image_raw_size; ++i) { + for (i = 0; i < histo_image_raw_size; ++i) { histogram_symbols[i] = -1; } - if (!HistogramRefine(histogram_image_raw, histogram_image_raw_size, - histogram_symbols, - *histogram_image, *histogram_image_size)) { + if (!HistogramRefine(histo_image_raw, histo_image_raw_size, histogram_symbols, + histogram_image, *histogram_image_size)) { goto Error; } ok = 1; Error: if (!ok) { - VP8LDeleteHistograms(*histogram_image, *histogram_image_size); + VP8LDeleteHistograms(histogram_image, *histogram_image_size); } - VP8LDeleteHistograms(histogram_image_raw, histogram_image_raw_size); + VP8LDeleteHistograms(histo_image_raw, histo_image_raw_size); return ok; } diff --git a/src/enc/histogram.h b/src/enc/histogram.h index 400fb262..5b8ceee6 100644 --- a/src/enc/histogram.h +++ b/src/enc/histogram.h @@ -135,7 +135,7 @@ int VP8LGetHistImageSymbols(int xsize, int ysize, const VP8LBackwardRefs* const refs, int quality, int histogram_bits, int cache_bits, - VP8LHistogram*** histogram_image, + VP8LHistogram** const histogram_image, int* const histogram_image_size, uint32_t* const histogram_symbols); diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 5c9d19be..8fbc2793 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -652,7 +652,6 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, int cache_bits, int histogram_bits) { int i; int ok = 0; - int histogram_image_size; int write_histogram_image; int* bit_lengths_sizes = NULL; uint8_t** bit_lengths = NULL; @@ -662,12 +661,14 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, const int color_cache_size = use_color_cache ? (1 << cache_bits) : 0; const int histogram_image_xysize = VP8LSubSampleSize(width, histogram_bits) * VP8LSubSampleSize(height, histogram_bits); - VP8LHistogram** histogram_image; + VP8LHistogram** histogram_image = + (VP8LHistogram**)calloc(histogram_image_xysize, sizeof(*histogram_image)); + int histogram_image_size; VP8LBackwardRefs refs; const size_t histo_size = histogram_image_xysize * sizeof(uint32_t); uint32_t* const histogram_symbols = (uint32_t*)calloc(1, histo_size); - if (histogram_symbols == NULL) goto Error; + if (histogram_image == NULL || histogram_symbols == NULL) goto Error; // Calculate backward references from ARGB image. if (!GetBackwardReferences(width, height, argb, quality, @@ -678,7 +679,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, // Build histogram image & symbols from backward references. if (!VP8LGetHistImageSymbols(width, height, &refs, quality, histogram_bits, cache_bits, - &histogram_image, &histogram_image_size, + histogram_image, &histogram_image_size, histogram_symbols)) { goto Error; } From 1a210ef1a9e307218500c5c7088e5b4e1a1436f0 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Mon, 30 Apr 2012 12:18:50 +0000 Subject: [PATCH 44/46] big code clean-up and refactoring and optimization * de-inline some function * make VP8LBackwardRefs be more like a vectorwith max capacity * add bit_cost_ field to VP8LHistogram * general code simplifications * remove some memmov() from HistogramRefine * simplify HistogramDistance() ... Change-Id: I16904d9fa2380e1cf4a3fdddf56ed1fcadfa25dc --- src/enc/backward_references.c | 175 +++++++++----------- src/enc/backward_references.h | 48 ++++-- src/enc/histogram.c | 289 +++++++++++++++++----------------- src/enc/histogram.h | 64 ++++---- src/enc/vp8l.c | 74 +++++---- 5 files changed, 322 insertions(+), 328 deletions(-) diff --git a/src/enc/backward_references.c b/src/enc/backward_references.c index d6f6183f..2d89b8e7 100644 --- a/src/enc/backward_references.c +++ b/src/enc/backward_references.c @@ -83,7 +83,7 @@ typedef struct { static int VP8LHashChainInit(VP8LHashChain* const p, int size) { int i; p->chain_ = (int*)malloc(size * sizeof(*p->chain_)); - if (!p->chain_) { + if (p->chain_ == NULL) { return 0; } for (i = 0; i < size; ++i) { @@ -177,55 +177,50 @@ static int VP8LHashChainFindCopy( return best_length >= kMinLength; } -static WEBP_INLINE void PushBackCopy(int length, PixOrCopy* const stream, - int* const stream_size) { +static WEBP_INLINE void PushBackCopy(VP8LBackwardRefs* const refs, int length) { while (length >= kMaxLength) { - stream[*stream_size] = PixOrCopyCreateCopy(1, kMaxLength); - ++(*stream_size); + refs->refs[refs->size++] = PixOrCopyCreateCopy(1, kMaxLength); length -= kMaxLength; } if (length > 0) { - stream[*stream_size] = PixOrCopyCreateCopy(1, length); - ++(*stream_size); + refs->refs[refs->size++] = PixOrCopyCreateCopy(1, length); } } void VP8LBackwardReferencesRle( - int xsize, int ysize, const uint32_t* const argb, PixOrCopy* const stream, - int* const stream_size) { + int xsize, int ysize, const uint32_t* const argb, + VP8LBackwardRefs* const refs) { const int pix_count = xsize * ysize; int match_len = 0; int i; - *stream_size = 0; + refs->size = 0; for (i = 0; i < pix_count; ++i) { if (i >= 1 && argb[i] == argb[i - 1]) { ++match_len; } else { - PushBackCopy(match_len, stream, stream_size); + PushBackCopy(refs, match_len); match_len = 0; - stream[*stream_size] = PixOrCopyCreateLiteral(argb[i]); - ++(*stream_size); + refs->refs[refs->size++] = PixOrCopyCreateLiteral(argb[i]); } } - PushBackCopy(match_len, stream, stream_size); + PushBackCopy(refs, match_len); } // Returns 1 when successful. int VP8LBackwardReferencesHashChain( int xsize, int ysize, int use_color_cache, const uint32_t* const argb, - int cache_bits, int quality, PixOrCopy* const stream, - int* const stream_size) { + int cache_bits, int quality, VP8LBackwardRefs* const refs) { int i; int ok = 0; const int pix_count = xsize * ysize; VP8LHashChain* hash_chain = (VP8LHashChain*)malloc(sizeof(*hash_chain)); VP8LColorCache hashers; - if (!hash_chain || + if (hash_chain == NULL || !VP8LColorCacheInit(&hashers, cache_bits) || !VP8LHashChainInit(hash_chain, pix_count)) { goto Error; } - *stream_size = 0; + refs->size = 0; for (i = 0; i < pix_count; ) { // Alternative#1: Code the pixels starting at 'i' using backward reference. int offset = 0; @@ -256,11 +251,11 @@ int VP8LBackwardReferencesHashChain( // Alternative#2 is a better match. So push pixel at 'i' as literal. if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) { const int ix = VP8LColorCacheGetIndex(&hashers, argb[i]); - stream[*stream_size] = PixOrCopyCreateCacheIdx(ix); + refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix); } else { - stream[*stream_size] = PixOrCopyCreateLiteral(argb[i]); + refs->refs[refs->size] = PixOrCopyCreateLiteral(argb[i]); } - ++(*stream_size); + ++refs->size; VP8LColorCacheInsert(&hashers, argb[i]); i++; // Backward reference to be done for next pixel. len = len2; @@ -270,8 +265,7 @@ int VP8LBackwardReferencesHashChain( if (len >= kMaxLength) { len = kMaxLength - 1; } - stream[*stream_size] = PixOrCopyCreateCopy(offset, len); - ++(*stream_size); + refs->refs[refs->size++] = PixOrCopyCreateCopy(offset, len); for (k = 0; k < len; ++k) { VP8LColorCacheInsert(&hashers, argb[i + k]); if (k != 0 && i + k + 1 < pix_count) { @@ -284,11 +278,11 @@ int VP8LBackwardReferencesHashChain( if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) { // push pixel as a PixOrCopyCreateCacheIdx pixel int ix = VP8LColorCacheGetIndex(&hashers, argb[i]); - stream[*stream_size] = PixOrCopyCreateCacheIdx(ix); + refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix); } else { - stream[*stream_size] = PixOrCopyCreateLiteral(argb[i]); + refs->refs[refs->size] = PixOrCopyCreateLiteral(argb[i]); } - ++(*stream_size); + ++refs->size; VP8LColorCacheInsert(&hashers, argb[i]); if (i + 1 < pix_count) { VP8LHashChainInsert(hash_chain, &argb[i], i); @@ -304,6 +298,8 @@ Error: return ok; } +// ----------------------------------------------------------------------------- + typedef struct { double alpha_[VALUES_IN_BYTE]; double red_[VALUES_IN_BYTE]; @@ -317,32 +313,26 @@ static int CostModelBuild(CostModel* const p, int xsize, int ysize, int recursion_level, int use_color_cache, const uint32_t* const argb, int cache_bits) { int ok = 0; - int stream_size; VP8LHistogram histo; - int i; - PixOrCopy* stream = (PixOrCopy*)malloc(xsize * ysize * sizeof(*stream)); - if (stream == NULL) { - goto Error; - } + VP8LBackwardRefs refs; + + if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize)) goto Error; + p->cache_bits_ = cache_bits; if (recursion_level > 0) { if (!VP8LBackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1, use_color_cache, argb, cache_bits, - &stream[0], &stream_size)) { + &refs)) { goto Error; } } else { const int quality = 100; if (!VP8LBackwardReferencesHashChain(xsize, ysize, use_color_cache, argb, - cache_bits, quality, - &stream[0], &stream_size)) { + cache_bits, quality, &refs)) { goto Error; } } - VP8LHistogramInit(&histo, cache_bits); - for (i = 0; i < stream_size; ++i) { - VP8LHistogramAddSinglePixOrCopy(&histo, stream[i]); - } + VP8LHistogramCreate(&histo, &refs, cache_bits); VP8LConvertPopulationCountTableToBitEstimates( VP8LHistogramNumCodes(&histo), &histo.literal_[0], &p->literal_[0]); @@ -355,8 +345,9 @@ static int CostModelBuild(CostModel* const p, int xsize, int ysize, VP8LConvertPopulationCountTableToBitEstimates( DISTANCE_CODES_MAX, &histo.distance_[0], &p->distance_[0]); ok = 1; -Error: - free(stream); + + Error: + VP8LClearBackwardRefs(&refs); return ok; } @@ -527,9 +518,10 @@ static void TraceBackwards( static int BackwardReferencesHashChainFollowChosenPath( int xsize, int ysize, int use_color_cache, const uint32_t* const argb, int cache_bits, const uint32_t* const chosen_path, int chosen_path_size, - PixOrCopy* const stream, int* const stream_size) { + VP8LBackwardRefs* const refs) { const int quality = 100; const int pix_count = xsize * ysize; + int size = 0; int i = 0; int k; int ix; @@ -541,8 +533,8 @@ static int BackwardReferencesHashChainFollowChosenPath( !VP8LColorCacheInit(&hashers, cache_bits)) { goto Error; } - *stream_size = 0; - for (ix = 0; ix < chosen_path_size; ++ix) { + refs->size = 0; + for (ix = 0; ix < chosen_path_size; ++ix, ++size) { int offset = 0; int len = 0; int maxlen = chosen_path[ix]; @@ -550,8 +542,7 @@ static int BackwardReferencesHashChainFollowChosenPath( VP8LHashChainFindCopy(hash_chain, quality, i, xsize, argb, maxlen, &offset, &len); assert(len == maxlen); - stream[*stream_size] = PixOrCopyCreateCopy(offset, len); - ++(*stream_size); + refs->refs[size] = PixOrCopyCreateCopy(offset, len); for (k = 0; k < len; ++k) { VP8LColorCacheInsert(&hashers, argb[i + k]); if (i + k + 1 < pix_count) { @@ -564,11 +555,10 @@ static int BackwardReferencesHashChainFollowChosenPath( if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) { // push pixel as a color cache index int ix = VP8LColorCacheGetIndex(&hashers, argb[i]); - stream[*stream_size] = PixOrCopyCreateCacheIdx(ix); + refs->refs[size] = PixOrCopyCreateCacheIdx(ix); } else { - stream[*stream_size] = PixOrCopyCreateLiteral(argb[i]); + refs->refs[size] = PixOrCopyCreateLiteral(argb[i]); } - ++(*stream_size); VP8LColorCacheInsert(&hashers, argb[i]); if (i + 1 < pix_count) { VP8LHashChainInsert(hash_chain, &argb[i], i); @@ -576,6 +566,8 @@ static int BackwardReferencesHashChainFollowChosenPath( ++i; } } + assert(size < refs->max_size); + refs->size = size; ok = 1; Error: VP8LHashChainClear(hash_chain); @@ -589,8 +581,7 @@ Error: // Returns 1 on success. int VP8LBackwardReferencesTraceBackwards( int xsize, int ysize, int recursive_cost_model, int use_color_cache, - const uint32_t* const argb, int cache_bits, PixOrCopy* const stream, - int* const stream_size) { + const uint32_t* const argb, int cache_bits, VP8LBackwardRefs* const refs) { int ok = 0; const int dist_array_size = xsize * ysize; uint32_t* chosen_path = NULL; @@ -600,7 +591,6 @@ int VP8LBackwardReferencesTraceBackwards( if (dist_array == NULL) { goto Error; } - *stream_size = 0; if (!BackwardReferencesHashChainDistanceOnly( xsize, ysize, recursive_cost_model, use_color_cache, argb, cache_bits, dist_array)) { @@ -611,72 +601,59 @@ int VP8LBackwardReferencesTraceBackwards( free(dist_array); if (!BackwardReferencesHashChainFollowChosenPath( xsize, ysize, use_color_cache, argb, cache_bits, - chosen_path, chosen_path_size, - stream, stream_size)) { + chosen_path, chosen_path_size, refs)) { goto Error; } ok = 1; -Error: + Error: free(chosen_path); return ok; } -void VP8LBackwardReferences2DLocality(int xsize, int data_size, - PixOrCopy* const data) { +void VP8LBackwardReferences2DLocality(int xsize, VP8LBackwardRefs* const refs) { int i; - for (i = 0; i < data_size; ++i) { - if (PixOrCopyIsCopy(&data[i])) { - int dist = data[i].argb_or_distance; - int transformed_dist = VP8LDistanceToPlaneCode(xsize, dist); - data[i].argb_or_distance = transformed_dist; + for (i = 0; i < refs->size; ++i) { + if (PixOrCopyIsCopy(&refs->refs[i])) { + const int dist = refs->refs[i].argb_or_distance; + const int transformed_dist = VP8LDistanceToPlaneCode(xsize, dist); + refs->refs[i].argb_or_distance = transformed_dist; } } } int VP8LVerifyBackwardReferences( const uint32_t* const argb, int xsize, int ysize, int cache_bits, - const PixOrCopy* const lit, int lit_size) { + const VP8LBackwardRefs* const refs) { int num_pixels = 0; int i; VP8LColorCache hashers; VP8LColorCacheInit(&hashers, cache_bits); - for (i = 0; i < lit_size; ++i) { - if (PixOrCopyIsLiteral(&lit[i])) { - if (argb[num_pixels] != PixOrCopyArgb(&lit[i])) { - printf("i %d, pixel %d, original: 0x%08x, literal: 0x%08x\n", - i, num_pixels, argb[num_pixels], PixOrCopyArgb(&lit[i])); + for (i = 0; i < refs->size; ++i) { + const PixOrCopy token = refs->refs[i]; + if (PixOrCopyIsLiteral(&token)) { + if (argb[num_pixels] != PixOrCopyArgb(&token)) { VP8LColorCacheClear(&hashers); return 0; } VP8LColorCacheInsert(&hashers, argb[num_pixels]); ++num_pixels; - } else if (PixOrCopyIsCacheIdx(&lit[i])) { - uint32_t cache_entry = - VP8LColorCacheLookup(&hashers, PixOrCopyCacheIdx(&lit[i])); + } else if (PixOrCopyIsCacheIdx(&token)) { + const uint32_t cache_entry = + VP8LColorCacheLookup(&hashers, PixOrCopyCacheIdx(&token)); if (argb[num_pixels] != cache_entry) { - printf("i %d, pixel %d, original: 0x%08x, cache_idx: %d, " - "cache_entry: 0x%08x\n", - i, num_pixels, argb[num_pixels], PixOrCopyCacheIdx(&lit[i]), - cache_entry); VP8LColorCacheClear(&hashers); return 0; } VP8LColorCacheInsert(&hashers, argb[num_pixels]); ++num_pixels; - } else if (PixOrCopyIsCopy(&lit[i])) { + } else if (PixOrCopyIsCopy(&token)) { int k; - if (PixOrCopyDistance(&lit[i]) == 0) { - printf("Bw reference with zero distance.\n"); + if (PixOrCopyDistance(&token) == 0) { VP8LColorCacheClear(&hashers); return 0; } - for (k = 0; k < lit[i].len; ++k) { - if (argb[num_pixels] != - argb[num_pixels - PixOrCopyDistance(&lit[i])]) { - printf("i %d, pixel %d, original: 0x%08x, copied: 0x%08x, dist: %d\n", - i, num_pixels, argb[num_pixels], - argb[num_pixels - PixOrCopyDistance(&lit[i])], - PixOrCopyDistance(&lit[i])); + for (k = 0; k < token.len; ++k) { + if (argb[num_pixels] != argb[num_pixels - PixOrCopyDistance(&token)]) { VP8LColorCacheClear(&hashers); return 0; } @@ -688,7 +665,6 @@ int VP8LVerifyBackwardReferences( { const int pix_count = xsize * ysize; if (num_pixels != pix_count) { - printf("verify failure: %d != %d\n", num_pixels, pix_count); VP8LColorCacheClear(&hashers); return 0; } @@ -700,7 +676,7 @@ int VP8LVerifyBackwardReferences( // Returns 1 on success. static int ComputeCacheHistogram( const uint32_t* const argb, int xsize, int ysize, - const PixOrCopy* const stream, int stream_size, int cache_bits, + const VP8LBackwardRefs* const refs, int cache_bits, VP8LHistogram* const histo) { int pixel_index = 0; int i; @@ -709,21 +685,22 @@ static int ComputeCacheHistogram( if (!VP8LColorCacheInit(&hashers, cache_bits)) { return 0; } - for (i = 0; i < stream_size; ++i) { - const PixOrCopy v = stream[i]; - if (PixOrCopyIsLiteral(&v)) { + for (i = 0; i < refs->size; ++i) { + const PixOrCopy* const v = &refs->refs[i]; + if (PixOrCopyIsLiteral(v)) { if (cache_bits != 0 && VP8LColorCacheContains(&hashers, argb[pixel_index])) { // push pixel as a cache index const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]); - VP8LHistogramAddSinglePixOrCopy(histo, PixOrCopyCreateCacheIdx(ix)); + const PixOrCopy token = PixOrCopyCreateCacheIdx(ix); + VP8LHistogramAddSinglePixOrCopy(histo, &token); } else { VP8LHistogramAddSinglePixOrCopy(histo, v); } } else { VP8LHistogramAddSinglePixOrCopy(histo, v); } - for (k = 0; k < PixOrCopyLength(&v); ++k) { + for (k = 0; k < PixOrCopyLength(v); ++k) { VP8LColorCacheInsert(&hashers, argb[pixel_index]); ++pixel_index; } @@ -742,21 +719,19 @@ int VP8LCalculateEstimateForCacheSize( int ok = 0; int cache_bits; double lowest_entropy = 1e99; - PixOrCopy* stream = (PixOrCopy*)malloc(xsize * ysize * sizeof(*stream)); - int stream_size; + VP8LBackwardRefs refs; static const double kSmallPenaltyForLargeCache = 4.0; static const int quality = 30; - if (stream == NULL || + if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize) || !VP8LBackwardReferencesHashChain(xsize, ysize, 0, argb, 0, quality, - stream, &stream_size)) { + &refs)) { goto Error; } for (cache_bits = 0; cache_bits <= kColorCacheBitsMax; ++cache_bits) { double cur_entropy; VP8LHistogram histo; VP8LHistogramInit(&histo, cache_bits); - ComputeCacheHistogram(argb, xsize, ysize, &stream[0], stream_size, - cache_bits, &histo); + ComputeCacheHistogram(argb, xsize, ysize, &refs, cache_bits, &histo); cur_entropy = VP8LHistogramEstimateBits(&histo) + kSmallPenaltyForLargeCache * cache_bits; if (cache_bits == 0 || cur_entropy < lowest_entropy) { @@ -765,8 +740,8 @@ int VP8LCalculateEstimateForCacheSize( } } ok = 1; -Error: - free(stream); + Error: + VP8LClearBackwardRefs(&refs); return ok; } diff --git a/src/enc/backward_references.h b/src/enc/backward_references.h index 2ba55d15..8d647010 100644 --- a/src/enc/backward_references.h +++ b/src/enc/backward_references.h @@ -103,10 +103,6 @@ typedef struct { uint32_t argb_or_distance; } PixOrCopy; -typedef struct { - PixOrCopy* refs; - int size; -} VP8LBackwardRefs; static WEBP_INLINE PixOrCopy PixOrCopyCreateCopy(uint32_t distance, uint16_t len) { @@ -136,15 +132,15 @@ static WEBP_INLINE PixOrCopy PixOrCopyCreateLiteral(uint32_t argb) { } static WEBP_INLINE int PixOrCopyIsLiteral(const PixOrCopy* const p) { - return p->mode == kLiteral; + return (p->mode == kLiteral); } static WEBP_INLINE int PixOrCopyIsCacheIdx(const PixOrCopy* const p) { - return p->mode == kCacheIdx; + return (p->mode == kCacheIdx); } static WEBP_INLINE int PixOrCopyIsCopy(const PixOrCopy* const p) { - return p->mode == kCopy; + return (p->mode == kCopy); } static WEBP_INLINE uint32_t PixOrCopyLiteral(const PixOrCopy* const p, @@ -173,10 +169,21 @@ static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy* const p) { return p->argb_or_distance; } +// ----------------------------------------------------------------------------- +// VP8LBackwardRefs + +typedef struct { + PixOrCopy* refs; + int size; // currently used + int max_size; // maximum capacity +} VP8LBackwardRefs; + + static WEBP_INLINE void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs) { if (refs != NULL) { refs->refs = NULL; refs->size = 0; + refs->max_size = 0; } } @@ -187,32 +194,41 @@ static WEBP_INLINE void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs) { } } +// Allocate 'max_size' references. Returns false in case of memory error. +static WEBP_INLINE int VP8LBackwardRefsAlloc(VP8LBackwardRefs* const refs, + int max_size) { + assert(refs != NULL); + refs->size = 0; + refs->max_size = 0; + refs->refs = (PixOrCopy*)malloc(max_size * sizeof(*refs->refs)); + if (refs->refs == NULL) return 0; + refs->max_size = max_size; + return 1; +} + // Ridiculously simple backward references for images where it is unlikely // that there are large backward references (photos). void VP8LBackwardReferencesRle( - int xsize, int ysize, const uint32_t* const argb, PixOrCopy* const stream, - int* const stream_size); + int xsize, int ysize, const uint32_t* const argb, + VP8LBackwardRefs* const refs); // This is a simple fast function for obtaining backward references // based on simple heuristics. Returns 1 on success. int VP8LBackwardReferencesHashChain( int xsize, int ysize, int use_color_cache, const uint32_t* const argb, - int cache_bits, int quality, PixOrCopy* const stream, - int* const stream_size); + int cache_bits, int quality, VP8LBackwardRefs* const refs); // This method looks for a shortest path through the backward reference // network based on a cost model generated by a first round of compression. // Returns 1 on success. int VP8LBackwardReferencesTraceBackwards( int xsize, int ysize, int recursive_cost_model, int use_color_cache, - const uint32_t* const argb, int cache_bits, PixOrCopy* const stream, - int* const stream_size); + const uint32_t* const argb, int cache_bits, VP8LBackwardRefs* const refs); // Convert backward references that are of linear distance along // the image scan lines to have a 2d locality indexing where // smaller values are used for backward references that are close by. -void VP8LBackwardReferences2DLocality(int xsize, int data_size, - PixOrCopy* const data); +void VP8LBackwardReferences2DLocality(int xsize, VP8LBackwardRefs* const refs); // Internals of locality transform exposed for testing use. int VP8LDistanceToPlaneCode(int xsize, int distance); @@ -221,7 +237,7 @@ int VP8LDistanceToPlaneCode(int xsize, int distance); // the image given in tuple (argb, xsize, ysize). int VP8LVerifyBackwardReferences( const uint32_t* const argb, int xsize, int ysize, int cache_bits, - const PixOrCopy* const lit, int lit_size); + const VP8LBackwardRefs* const refs); // Produce an estimate for a good color cache size for the image. int VP8LCalculateEstimateForCacheSize( diff --git a/src/enc/histogram.c b/src/enc/histogram.c index 662ba19c..551bfec2 100644 --- a/src/enc/histogram.c +++ b/src/enc/histogram.c @@ -17,6 +17,57 @@ #include "./histogram.h" #include "../dsp/lossless.h" +void VP8LHistogramCreate(VP8LHistogram* const p, + const VP8LBackwardRefs* const refs, + int palette_code_bits) { + int i; + if (palette_code_bits >= 0) { + p->palette_code_bits_ = palette_code_bits; + } + VP8LHistogramClear(p); + for (i = 0; i < refs->size; ++i) { + VP8LHistogramAddSinglePixOrCopy(p, &refs->refs[i]); + } +} + +void VP8LHistogramClear(VP8LHistogram* const p) { + memset(p->literal_, 0, sizeof(p->literal_)); + memset(p->red_, 0, sizeof(p->red_)); + memset(p->blue_, 0, sizeof(p->blue_)); + memset(p->alpha_, 0, sizeof(p->alpha_)); + memset(p->distance_, 0, sizeof(p->distance_)); + p->bit_cost_ = 0; +} + +void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits) { + p->palette_code_bits_ = palette_code_bits; + VP8LHistogramClear(p); +} + +VP8LHistogram** VP8LAllocateHistograms(int size, int cache_bits) { + int i; + VP8LHistogram** const histos = + (VP8LHistogram**)calloc(size, sizeof(*histos)); + if (histos == NULL) return NULL; + for (i = 0; i < size; ++i) { + histos[i] = (VP8LHistogram*)malloc(sizeof(**histos)); + if (histos[i] == NULL) { + VP8LDeleteHistograms(histos, i); + return NULL; + } + VP8LHistogramInit(histos[i], cache_bits); + } + return histos; +} + +void VP8LDeleteHistograms(VP8LHistogram** const histograms, int size) { + if (histograms != NULL) { + int i; + for (i = 0; i < size; ++i) free(histograms[i]); + free(histograms); + } +} + void VP8LConvertPopulationCountTableToBitEstimates( int num_symbols, const int* const population_counts, @@ -47,34 +98,27 @@ void VP8LConvertPopulationCountTableToBitEstimates( } void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const p, - const PixOrCopy v) { - if (PixOrCopyIsLiteral(&v)) { - ++p->alpha_[PixOrCopyLiteral(&v, 3)]; - ++p->red_[PixOrCopyLiteral(&v, 2)]; - ++p->literal_[PixOrCopyLiteral(&v, 1)]; - ++p->blue_[PixOrCopyLiteral(&v, 0)]; - } else if (PixOrCopyIsCacheIdx(&v)) { - int literal_ix = 256 + kLengthCodes + PixOrCopyCacheIdx(&v); + const PixOrCopy* const v) { + if (PixOrCopyIsLiteral(v)) { + ++p->alpha_[PixOrCopyLiteral(v, 3)]; + ++p->red_[PixOrCopyLiteral(v, 2)]; + ++p->literal_[PixOrCopyLiteral(v, 1)]; + ++p->blue_[PixOrCopyLiteral(v, 0)]; + } else if (PixOrCopyIsCacheIdx(v)) { + int literal_ix = 256 + kLengthCodes + PixOrCopyCacheIdx(v); ++p->literal_[literal_ix]; } else { int code, extra_bits_count, extra_bits_value; - PrefixEncode(PixOrCopyLength(&v), + PrefixEncode(PixOrCopyLength(v), &code, &extra_bits_count, &extra_bits_value); ++p->literal_[256 + code]; - PrefixEncode(PixOrCopyDistance(&v), + PrefixEncode(PixOrCopyDistance(v), &code, &extra_bits_count, &extra_bits_value); ++p->distance_[code]; } } -void VP8LHistogramCreate(VP8LHistogram* const p, - const VP8LBackwardRefs* const refs) { - int i; - VP8LHistogramClear(p); - for (i = 0; i < refs->size; ++i) { - VP8LHistogramAddSinglePixOrCopy(p, refs->refs[i]); - } -} + static double BitsEntropy(const int* const array, int n) { double retval = 0; @@ -133,7 +177,7 @@ double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) { BitsEntropy(&p->alpha_[0], 256) + BitsEntropy(&p->distance_[0], DISTANCE_CODES_MAX); // Compute the extra bits cost. - size_t i; + int i; for (i = 2; i < kLengthCodes - 2; ++i) { retval += (i >> 1) * p->literal_[256 + i + 2]; @@ -189,74 +233,61 @@ static double HuffmanCost(const int* const population, int length) { double VP8LHistogramEstimateBitsHeader(const VP8LHistogram* const p) { return HuffmanCost(&p->alpha_[0], 256) + - HuffmanCost(&p->red_[0], 256) + - HuffmanCost(&p->literal_[0], VP8LHistogramNumCodes(p)) + - HuffmanCost(&p->blue_[0], 256) + - HuffmanCost(&p->distance_[0], DISTANCE_CODES_MAX); + HuffmanCost(&p->red_[0], 256) + + HuffmanCost(&p->literal_[0], VP8LHistogramNumCodes(p)) + + HuffmanCost(&p->blue_[0], 256) + + HuffmanCost(&p->distance_[0], DISTANCE_CODES_MAX); } -static int HistogramBuildImage(int xsize, int histo_bits, int cache_bits, - const VP8LBackwardRefs* const backward_refs, - VP8LHistogram** const image, - int image_size) { +static void HistogramBuildImage(int xsize, int histo_bits, + const VP8LBackwardRefs* const backward_refs, + VP8LHistogram** const image) { int i; + int x = 0, y = 0; const int histo_xsize = - histo_bits ? (xsize + (1 << histo_bits) - 1) >> histo_bits : 1; - int x = 0; - int y = 0; - for (i = 0; i < image_size; ++i) { - image[i] = (VP8LHistogram*)malloc(sizeof(*image[i])); - if (image[i] == NULL) { - int k; - for (k = 0; k < i; ++k) free(image[k]); - return 0; - } - VP8LHistogramInit(image[i], cache_bits); - } - // x and y trace the position in the image. + (histo_bits > 0) ? VP8LSubSampleSize(xsize, histo_bits) : 1; for (i = 0; i < backward_refs->size; ++i) { - const PixOrCopy v = backward_refs->refs[i]; + const PixOrCopy* const v = &backward_refs->refs[i]; const int ix = - histo_bits ? (y >> histo_bits) * histo_xsize + (x >> histo_bits) : 0; + (histo_bits > 0) ? (y >> histo_bits) * histo_xsize + (x >> histo_bits) + : 0; VP8LHistogramAddSinglePixOrCopy(image[ix], v); - x += PixOrCopyLength(&v); + x += PixOrCopyLength(v); while (x >= xsize) { x -= xsize; ++y; } } - return 1; } -static int HistogramCombine(VP8LHistogram** const in, int in_size, +static int HistogramCombine(VP8LHistogram* const * const in, int in_size, int num_pairs, VP8LHistogram** const out, - int* const out_size_arg) { + int* const final_out_size) { int ok = 0; - int i; + int i, iter; unsigned int seed = 0; int tries_with_no_success = 0; - const int outer_iters = in_size * 3; - double* const bit_costs = (double*)malloc(in_size * sizeof(*bit_costs)); - // TODO(urvang): 'bit_cost' should be part of VP8LHistogram. - VP8LHistogram* const combo = (VP8LHistogram*)malloc(sizeof(*combo)); + const int min_cluster_size = 2; int out_size = in_size; - if (bit_costs == NULL || out == NULL || combo == NULL) goto End; + const int outer_iters = in_size * 3; + VP8LHistogram* const histos = (VP8LHistogram*)malloc(2 * sizeof(*histos)); + VP8LHistogram* cur_combo = histos + 0; // trial merged histogram + VP8LHistogram* best_combo = histos + 1; // best merged histogram so far + if (histos == NULL) goto End; - // Copy histogram 'in' to 'out'. + // Copy histograms from in[] to out[]. for (i = 0; i < in_size; ++i) { - VP8LHistogram* const temp = (VP8LHistogram*)malloc(sizeof(*temp)); - if (temp == NULL) goto End; - *temp = *(in[i]); - out[i] = temp; - bit_costs[i] = VP8LHistogramEstimateBits(out[i]); + out[i] = (VP8LHistogram*)malloc(sizeof(*(out[i]))); + if (out[i] == NULL) goto End; + *(out[i]) = *(in[i]); + out[i]->bit_cost_ = in[i]->bit_cost_ = VP8LHistogramEstimateBits(out[i]); } // Collapse similar histograms in 'out'. - for (i = 0; i < outer_iters && out_size >= 2; ++i) { + for (iter = 0; iter < outer_iters && out_size >= min_cluster_size; ++iter) { // We pick the best pair to be combined out of 'inner_iters' pairs. - double best_cost_diff = 0; - int best_idx1 = 0; - int best_idx2 = 0; + double best_cost_diff = 0.; + int best_idx1 = 0, best_idx2 = 1; int j; for (j = 0; j < num_pairs; ++j) { double curr_cost_diff; @@ -268,12 +299,18 @@ static int HistogramCombine(VP8LHistogram** const in, int in_size, if (idx1 == idx2) { continue; } - *combo = *out[idx1]; - VP8LHistogramAdd(combo, out[idx2]); + *cur_combo = *out[idx1]; + VP8LHistogramAdd(cur_combo, out[idx2]); + cur_combo->bit_cost_ = VP8LHistogramEstimateBits(cur_combo); // Calculate cost reduction on combining. - curr_cost_diff = - VP8LHistogramEstimateBits(combo) - bit_costs[idx1] - bit_costs[idx2]; - if (best_cost_diff > curr_cost_diff) { + curr_cost_diff = cur_combo->bit_cost_ + - out[idx1]->bit_cost_ - out[idx2]->bit_cost_; + if (best_cost_diff > curr_cost_diff) { // found a better pair? + { // swap cur/best combo histograms + VP8LHistogram* const tmp = cur_combo; + cur_combo = best_combo; + best_combo = tmp; + } best_cost_diff = curr_cost_diff; best_idx1 = idx1; best_idx2 = idx2; @@ -281,89 +318,59 @@ static int HistogramCombine(VP8LHistogram** const in, int in_size, } if (best_cost_diff < 0.0) { - // Combine this pair and store the combined histogram at 'best_idx1'. - VP8LHistogramAdd(out[best_idx1], out[best_idx2]); - bit_costs[best_idx1] = - best_cost_diff + bit_costs[best_idx1] + bit_costs[best_idx2]; + *out[best_idx1] = *best_combo; + // swap best_idx2 slot with last one (which is now unused) free(out[best_idx2]); - memmove(&out[best_idx2], &out[best_idx2 + 1], - (out_size - best_idx2 - 1) * sizeof(*out)); - memmove(&bit_costs[best_idx2], &bit_costs[best_idx2 + 1], - (out_size - best_idx2 - 1) * sizeof(*bit_costs)); --out_size; + if (best_idx2 != out_size) { + out[best_idx2] = out[out_size]; + out[out_size] = NULL; // just for sanity check. + } tries_with_no_success = 0; } if (++tries_with_no_success >= 50) { break; } } + *final_out_size = out_size; ok = 1; + End: - free(bit_costs); - free(combo); - if (!ok) { - if (out != NULL) { - for (i = 0; i < out_size; ++i) free(out[i]); - free(out); - } - *out_size_arg = 0; - } else { - *out_size_arg = out_size; - } + free(histos); return ok; } +// ----------------------------------------------------------------------------- +// Histogram refinement + // What is the bit cost of moving square_histogram from // cur_symbol to candidate_symbol. +// TODO(skal): we don't really need to copy the histogram and Add(). Instead +// we just need VP8LDualHistogramEstimateBits(A, B) estimation function. static double HistogramDistance(const VP8LHistogram* const square_histogram, - int cur_symbol, int candidate_symbol, - const double* const symbol_bit_costs, - VP8LHistogram** const candidate_histograms) { + const VP8LHistogram* const candidate) { + const double previous_bit_cost = candidate->bit_cost_; double new_bit_cost; - double previous_bit_cost; VP8LHistogram modified_histo; - if (cur_symbol == candidate_symbol) { - return 0; // Going nowhere. No savings. - } - previous_bit_cost = symbol_bit_costs[candidate_symbol]; - if (cur_symbol != -1) { - previous_bit_cost += symbol_bit_costs[cur_symbol]; - } - - // Compute the bit cost of the histogram where the data moves to. - modified_histo = *candidate_histograms[candidate_symbol]; + modified_histo = *candidate; VP8LHistogramAdd(&modified_histo, square_histogram); new_bit_cost = VP8LHistogramEstimateBits(&modified_histo); - // Compute the bit cost of the histogram where the data moves away. - if (cur_symbol != -1) { - modified_histo = *candidate_histograms[cur_symbol]; - VP8LHistogramRemove(&modified_histo, square_histogram); - new_bit_cost += VP8LHistogramEstimateBits(&modified_histo); - } return new_bit_cost - previous_bit_cost; } -static int HistogramRefine(VP8LHistogram** const raw, int raw_size, - uint32_t* const symbols, - VP8LHistogram** const out, int out_size) { +// Find the best 'out' histogram for each of the raw histograms. +// Note: we assume that out[]->bit_cost_ is already up-to-date. +static void HistogramRefine(VP8LHistogram* const * const raw, int raw_size, + uint16_t* const symbols, + VP8LHistogram** const out, int out_size) { int i; - double* const symbol_bit_costs = - (double*)malloc(out_size * sizeof(*symbol_bit_costs)); - if (symbol_bit_costs == NULL) return 0; - for (i = 0; i < out_size; ++i) { - symbol_bit_costs[i] = VP8LHistogramEstimateBits(out[i]); - } - - // Find the best 'out' histogram for each of the raw histograms for (i = 0; i < raw_size; ++i) { int best_out = 0; - double best_bits = HistogramDistance(raw[i], symbols[i], 0, - symbol_bit_costs, out); + double best_bits = HistogramDistance(raw[i], out[0]); int k; for (k = 1; k < out_size; ++k) { - double cur_bits = HistogramDistance(raw[i], symbols[i], k, - symbol_bit_costs, out); + const double cur_bits = HistogramDistance(raw[i], out[k]); if (cur_bits < best_bits) { best_bits = cur_bits; best_out = k; @@ -371,7 +378,6 @@ static int HistogramRefine(VP8LHistogram** const raw, int raw_size, } symbols[i] = best_out; } - free(symbol_bit_costs); // Recompute each out based on raw and symbols. for (i = 0; i < out_size; ++i) { @@ -380,50 +386,39 @@ static int HistogramRefine(VP8LHistogram** const raw, int raw_size, for (i = 0; i < raw_size; ++i) { VP8LHistogramAdd(out[symbols[i]], raw[i]); } - - return 1; } -int VP8LGetHistImageSymbols(int xsize, int ysize, - const VP8LBackwardRefs* const refs, - int quality, int histo_bits, - int cache_bits, - VP8LHistogram** const histogram_image, - int* const histogram_image_size, - uint32_t* const histogram_symbols) { +int VP8LGetHistoImageSymbols(int xsize, int ysize, + const VP8LBackwardRefs* const refs, + int quality, int histo_bits, int cache_bits, + VP8LHistogram** const histogram_image, + int* const histo_image_raw_size_ptr, + uint16_t* const histogram_symbols) { int ok = 0; - int i; const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1; const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1; const int num_histo_pairs = 10 + quality / 2; // For HistogramCombine(). const int histo_image_raw_size = histo_xsize * histo_ysize; - VP8LHistogram** const histo_image_raw = - (VP8LHistogram**)calloc(histo_image_raw_size, sizeof(*histo_image_raw)); + VP8LHistogram** const histo_image_raw = + VP8LAllocateHistograms(histo_image_raw_size, cache_bits); if (histo_image_raw == NULL) return 0; + *histo_image_raw_size_ptr = histo_image_raw_size; // initial guess. // Build histogram image. - if (!HistogramBuildImage(xsize, histo_bits, cache_bits, refs, - histo_image_raw, histo_image_raw_size)) { - goto Error; - } + HistogramBuildImage(xsize, histo_bits, refs, histo_image_raw); // Collapse similar histograms. if (!HistogramCombine(histo_image_raw, histo_image_raw_size, num_histo_pairs, - histogram_image, histogram_image_size)) { + histogram_image, histo_image_raw_size_ptr)) { goto Error; } // Refine histogram image. - for (i = 0; i < histo_image_raw_size; ++i) { - histogram_symbols[i] = -1; - } - if (!HistogramRefine(histo_image_raw, histo_image_raw_size, histogram_symbols, - histogram_image, *histogram_image_size)) { - goto Error; - } + HistogramRefine(histo_image_raw, histo_image_raw_size, histogram_symbols, + histogram_image, *histo_image_raw_size_ptr); ok = 1; Error: if (!ok) { - VP8LDeleteHistograms(histogram_image, *histogram_image_size); + VP8LDeleteHistograms(histogram_image, *histo_image_raw_size_ptr); } VP8LDeleteHistograms(histo_image_raw, histo_image_raw_size); return ok; diff --git a/src/enc/histogram.h b/src/enc/histogram.h index 5b8ceee6..7cf69e95 100644 --- a/src/enc/histogram.h +++ b/src/enc/histogram.h @@ -38,30 +38,33 @@ typedef struct { // Backward reference prefix-code histogram. int distance_[DISTANCE_CODES_MAX]; int palette_code_bits_; + double bit_cost_; // cached value of VP8LHistogramEstimateBits(this) } VP8LHistogram; -static WEBP_INLINE void VP8LHistogramClear(VP8LHistogram* const p) { - memset(&p->literal_[0], 0, sizeof(p->literal_)); - memset(&p->red_[0], 0, sizeof(p->red_)); - memset(&p->blue_[0], 0, sizeof(p->blue_)); - memset(&p->alpha_[0], 0, sizeof(p->alpha_)); - memset(&p->distance_[0], 0, sizeof(p->distance_)); -} - -static WEBP_INLINE void VP8LHistogramInit(VP8LHistogram* const p, - int palette_code_bits) { - p->palette_code_bits_ = palette_code_bits; - VP8LHistogramClear(p); -} - // Create the histogram. // -// The input data is the PixOrCopy data, which models the -// literals, stop codes and backward references (both distances and lengths) +// The input data is the PixOrCopy data, which models the literals, stop +// codes and backward references (both distances and lengths). Also: if +// palette_code_bits is >= 0, initialize the histogram with this value. void VP8LHistogramCreate(VP8LHistogram* const p, - const VP8LBackwardRefs* const refs); + const VP8LBackwardRefs* const refs, + int palette_code_bits); -void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const p, const PixOrCopy v); +// Reset the histogram's stats. +void VP8LHistogramClear(VP8LHistogram* const p); + +// Set the palette_code_bits and reset the stats. +void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits); + +// Allocate an array of pointer to histograms, allocated and initialized +// using 'cache_bits'. Return NULL in case of memory error. +VP8LHistogram** VP8LAllocateHistograms(int size, int cache_bits); + +// Destroy an array of histograms (and the array itself). +void VP8LDeleteHistograms(VP8LHistogram** const histograms, int size); + +void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const p, + const PixOrCopy* const v); // Estimate how many bits the combined entropy of literals and distance // approximately maps to. @@ -116,28 +119,17 @@ static WEBP_INLINE int VP8LHistogramNumCodes(const VP8LHistogram* const p) { return 256 + kLengthCodes + (1 << p->palette_code_bits_); } -static WEBP_INLINE void VP8LDeleteHistograms(VP8LHistogram** histograms, - int size) { - if (histograms != NULL) { - int i; - for (i = 0; i < size; ++i) { - free(histograms[i]); - } - free(histograms); - } -} - void VP8LConvertPopulationCountTableToBitEstimates( int n, const int* const population_counts, double* const output); // Builds the histogram image. -int VP8LGetHistImageSymbols(int xsize, int ysize, - const VP8LBackwardRefs* const refs, - int quality, int histogram_bits, - int cache_bits, - VP8LHistogram** const histogram_image, - int* const histogram_image_size, - uint32_t* const histogram_symbols); +int VP8LGetHistoImageSymbols(int xsize, int ysize, + const VP8LBackwardRefs* const refs, + int quality, int histogram_bits, + int cache_bits, + VP8LHistogram** const histogram_image, + int* const histogram_image_size, + uint16_t* const histogram_symbols); #if defined(__cplusplus) || defined(c_plusplus) } diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 8fbc2793..709d48f7 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -119,10 +119,12 @@ static int AnalyzeEntropy(const uint32_t const *argb, int xsize, int ysize, if (i >= xsize && pix == argb[i - xsize]) { continue; } - VP8LHistogramAddSinglePixOrCopy(nonpredicted, - PixOrCopyCreateLiteral(pix)); - VP8LHistogramAddSinglePixOrCopy(predicted, - PixOrCopyCreateLiteral(pix_diff)); + { + const PixOrCopy pix_token = PixOrCopyCreateLiteral(pix); + const PixOrCopy pix_diff_token = PixOrCopyCreateLiteral(pix_diff); + VP8LHistogramAddSinglePixOrCopy(nonpredicted, &pix_token); + VP8LHistogramAddSinglePixOrCopy(predicted, &pix_diff_token); + } } *nonpredicted_bits = (int)VP8LHistogramEstimateBitsBulk(nonpredicted); *predicted_bits = (int)VP8LHistogramEstimateBitsBulk(predicted); @@ -163,9 +165,9 @@ static int GetBackwardReferences(int width, int height, int lz77_is_useful; VP8LBackwardRefs refs_rle, refs_lz77; const int num_pix = width * height; - refs_rle.refs = (PixOrCopy*)malloc(num_pix * sizeof(*refs_rle.refs)); - refs_lz77.refs = (PixOrCopy*)malloc(num_pix * sizeof(*refs_lz77.refs)); + VP8LBackwardRefsAlloc(&refs_rle, num_pix); + VP8LBackwardRefsAlloc(&refs_lz77, num_pix); VP8LInitBackwardRefs(best); if (refs_rle.refs == NULL || refs_lz77.refs == NULL) { Error1: @@ -176,24 +178,22 @@ static int GetBackwardReferences(int width, int height, if (!VP8LBackwardReferencesHashChain(width, height, use_color_cache, argb, cache_bits, quality, - refs_lz77.refs, &refs_lz77.size)) { + &refs_lz77)) { goto End; } // Backward Reference using RLE only. - VP8LBackwardReferencesRle(width, height, argb, refs_rle.refs, &refs_rle.size); + VP8LBackwardReferencesRle(width, height, argb, &refs_rle); { - int bit_cost_lz77, bit_cost_rle; + double bit_cost_lz77, bit_cost_rle; VP8LHistogram* const histo = (VP8LHistogram*)malloc(sizeof(*histo)); if (histo == NULL) goto Error1; // Evaluate lz77 coding - VP8LHistogramInit(histo, cache_bits); - VP8LHistogramCreate(histo, &refs_lz77); - bit_cost_lz77 = (int)VP8LHistogramEstimateBits(histo); + VP8LHistogramCreate(histo, &refs_lz77, cache_bits); + bit_cost_lz77 = VP8LHistogramEstimateBits(histo); // Evaluate RLE coding - VP8LHistogramInit(histo, cache_bits); - VP8LHistogramCreate(histo, &refs_rle); - bit_cost_rle = (int)VP8LHistogramEstimateBits(histo); + VP8LHistogramCreate(histo, &refs_rle, cache_bits); + bit_cost_rle = VP8LHistogramEstimateBits(histo); // Decide if LZ77 is useful. lz77_is_useful = (bit_cost_lz77 < bit_cost_rle); free(histo); @@ -208,15 +208,13 @@ static int GetBackwardReferences(int width, int height, if (try_lz77_trace_backwards) { const int recursion_level = (num_pix < 320 * 200) ? 1 : 0; VP8LBackwardRefs refs_trace; - refs_trace.refs = (PixOrCopy*)malloc(num_pix * sizeof(*refs_trace.refs)); - if (refs_trace.refs == NULL) { + if (!VP8LBackwardRefsAlloc(&refs_trace, num_pix)) { goto End; } if (VP8LBackwardReferencesTraceBackwards(width, height, recursion_level, use_color_cache, argb, cache_bits, - refs_trace.refs, - &refs_trace.size)) { + &refs_trace)) { VP8LClearBackwardRefs(&refs_lz77); *best = refs_trace; } @@ -227,7 +225,7 @@ static int GetBackwardReferences(int width, int height, } if (use_2d_locality) { // Use backward reference with 2D locality. - VP8LBackwardReferences2DLocality(width, best->size, best->refs); + VP8LBackwardReferences2DLocality(width, best); } ok = 1; @@ -597,7 +595,7 @@ static int StoreHuffmanCode(VP8LBitWriter* const bw, static void StoreImageToBitMask( VP8LBitWriter* const bw, int width, int histo_bits, const VP8LBackwardRefs* const refs, - const uint32_t* histogram_symbols, + const uint16_t* histogram_symbols, uint8_t** const bitdepths, uint16_t** const bit_symbols) { // x and y trace the position in the image. int x = 0; @@ -665,8 +663,8 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, (VP8LHistogram**)calloc(histogram_image_xysize, sizeof(*histogram_image)); int histogram_image_size; VP8LBackwardRefs refs; - const size_t histo_size = histogram_image_xysize * sizeof(uint32_t); - uint32_t* const histogram_symbols = (uint32_t*)calloc(1, histo_size); + uint16_t* const histogram_symbols = + (uint16_t*)malloc(histogram_image_xysize * sizeof(*histogram_symbols)); if (histogram_image == NULL || histogram_symbols == NULL) goto Error; @@ -677,10 +675,10 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, goto Error; } // Build histogram image & symbols from backward references. - if (!VP8LGetHistImageSymbols(width, height, &refs, - quality, histogram_bits, cache_bits, - histogram_image, &histogram_image_size, - histogram_symbols)) { + if (!VP8LGetHistoImageSymbols(width, height, &refs, + quality, histogram_bits, cache_bits, + histogram_image, &histogram_image_size, + histogram_symbols)) { goto Error; } // Create Huffman bit lengths & codes for each histogram image. @@ -707,7 +705,8 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, write_histogram_image = (histogram_image_size > 1); VP8LWriteBits(bw, 1, write_histogram_image); if (write_histogram_image) { - uint32_t* const histogram_argb = (uint32_t*)malloc(histo_size); + uint32_t* const histogram_argb = + (uint32_t*)malloc(histogram_image_xysize * sizeof(*histogram_argb)); int max_index = 0; if (histogram_argb == NULL) goto Error; for (i = 0; i < histogram_image_xysize; ++i) { @@ -784,7 +783,7 @@ static int EvalAndApplySubtractGreen(const VP8LEncoder* const enc, if (!enc->use_palette_) { int i; const uint32_t* const argb = enc->argb_; - int bit_cost_before, bit_cost_after; + double bit_cost_before, bit_cost_after; VP8LHistogram* const histo = (VP8LHistogram*)malloc(sizeof(*histo)); if (histo == NULL) return 0; @@ -1186,6 +1185,23 @@ int VP8LEncodeImage(const WebPConfig* const config, goto Error; } + if (picture->stats != NULL) { + WebPAuxStats* const stats = picture->stats; + memset(stats, 0, sizeof(*stats)); + stats->PSNR[0] = 99.; + stats->PSNR[1] = 99.; + stats->PSNR[2] = 99.; + stats->PSNR[3] = 99.; + // note: padding byte may be missing. Not a big deal. + stats->coded_size = VP8LBitWriterNumBytes(&bw) + HEADER_SIZE; + } + + if (picture->extra_info != NULL) { + const int mb_w = (width + 15) >> 4; + const int mb_h = (height + 15) >> 4; + memset(picture->extra_info, 0, mb_w * mb_h * sizeof(*picture->extra_info)); + } + Error: VP8LBitWriterDestroy(&bw); DeleteVP8LEncoder(enc); From d0d88990d80396fac1a2c1dc49dacbf63420dc93 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Mon, 30 Apr 2012 19:46:22 +0000 Subject: [PATCH 45/46] more consolidation: introduce VP8LHistogramSet VP8LHistogramSet is container for pointers to histograms that we can shuffle around. Allocation is one big chunk of memory. Downside is that we don't de-allocate memory on-the-go during HistogramRefine(). + renamed HistogramRefine() into HistogramRemap(), so we don't confuse with "HistogramCombine" + made VP8LHistogramClear() static. Change-Id: Idf1a748a871c3b942cca5c8050072ccd82c7511d --- src/enc/histogram.c | 159 +++++++++++++++++++++----------------------- src/enc/histogram.h | 22 +++--- src/enc/vp8l.c | 66 +++++++++--------- 3 files changed, 121 insertions(+), 126 deletions(-) diff --git a/src/enc/histogram.c b/src/enc/histogram.c index 551bfec2..0958b6b8 100644 --- a/src/enc/histogram.c +++ b/src/enc/histogram.c @@ -17,20 +17,7 @@ #include "./histogram.h" #include "../dsp/lossless.h" -void VP8LHistogramCreate(VP8LHistogram* const p, - const VP8LBackwardRefs* const refs, - int palette_code_bits) { - int i; - if (palette_code_bits >= 0) { - p->palette_code_bits_ = palette_code_bits; - } - VP8LHistogramClear(p); - for (i = 0; i < refs->size; ++i) { - VP8LHistogramAddSinglePixOrCopy(p, &refs->refs[i]); - } -} - -void VP8LHistogramClear(VP8LHistogram* const p) { +static void HistogramClear(VP8LHistogram* const p) { memset(p->literal_, 0, sizeof(p->literal_)); memset(p->red_, 0, sizeof(p->red_)); memset(p->blue_, 0, sizeof(p->blue_)); @@ -39,38 +26,52 @@ void VP8LHistogramClear(VP8LHistogram* const p) { p->bit_cost_ = 0; } +void VP8LHistogramCreate(VP8LHistogram* const p, + const VP8LBackwardRefs* const refs, + int palette_code_bits) { + int i; + if (palette_code_bits >= 0) { + p->palette_code_bits_ = palette_code_bits; + } + HistogramClear(p); + for (i = 0; i < refs->size; ++i) { + VP8LHistogramAddSinglePixOrCopy(p, &refs->refs[i]); + } +} + void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits) { p->palette_code_bits_ = palette_code_bits; - VP8LHistogramClear(p); + HistogramClear(p); } -VP8LHistogram** VP8LAllocateHistograms(int size, int cache_bits) { +VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) { int i; - VP8LHistogram** const histos = - (VP8LHistogram**)calloc(size, sizeof(*histos)); - if (histos == NULL) return NULL; + VP8LHistogramSet* set; + VP8LHistogram* bulk; + const size_t total_size = sizeof(*set) + + size * sizeof(*set->histograms) + + size * sizeof(**set->histograms); + uint8_t* memory = (uint8_t*)malloc(total_size); + if (memory == NULL) return NULL; + + set = (VP8LHistogramSet*)memory; + memory += sizeof(*set); + set->histograms = (VP8LHistogram**)memory; + memory += size * sizeof(*set->histograms); + bulk = (VP8LHistogram*)memory; + set->max_size = size; + set->size = size; for (i = 0; i < size; ++i) { - histos[i] = (VP8LHistogram*)malloc(sizeof(**histos)); - if (histos[i] == NULL) { - VP8LDeleteHistograms(histos, i); - return NULL; - } - VP8LHistogramInit(histos[i], cache_bits); + set->histograms[i] = bulk + i; + VP8LHistogramInit(set->histograms[i], cache_bits); } - return histos; + return set; } -void VP8LDeleteHistograms(VP8LHistogram** const histograms, int size) { - if (histograms != NULL) { - int i; - for (i = 0; i < size; ++i) free(histograms[i]); - free(histograms); - } -} +// ----------------------------------------------------------------------------- void VP8LConvertPopulationCountTableToBitEstimates( - int num_symbols, - const int* const population_counts, + int num_symbols, const int* const population_counts, double* const output) { int sum = 0; int nonzeros = 0; @@ -241,7 +242,7 @@ double VP8LHistogramEstimateBitsHeader(const VP8LHistogram* const p) { static void HistogramBuildImage(int xsize, int histo_bits, const VP8LBackwardRefs* const backward_refs, - VP8LHistogram** const image) { + VP8LHistogramSet* const image) { int i; int x = 0, y = 0; const int histo_xsize = @@ -251,7 +252,7 @@ static void HistogramBuildImage(int xsize, int histo_bits, const int ix = (histo_bits > 0) ? (y >> histo_bits) * histo_xsize + (x >> histo_bits) : 0; - VP8LHistogramAddSinglePixOrCopy(image[ix], v); + VP8LHistogramAddSinglePixOrCopy(image->histograms[ix], v); x += PixOrCopyLength(v); while (x >= xsize) { x -= xsize; @@ -260,27 +261,25 @@ static void HistogramBuildImage(int xsize, int histo_bits, } } -static int HistogramCombine(VP8LHistogram* const * const in, int in_size, - int num_pairs, VP8LHistogram** const out, - int* const final_out_size) { +static int HistogramCombine(const VP8LHistogramSet* const in, + VP8LHistogramSet* const out, int num_pairs) { int ok = 0; int i, iter; unsigned int seed = 0; int tries_with_no_success = 0; const int min_cluster_size = 2; - int out_size = in_size; - const int outer_iters = in_size * 3; + int out_size = in->size; + const int outer_iters = in->size * 3; VP8LHistogram* const histos = (VP8LHistogram*)malloc(2 * sizeof(*histos)); VP8LHistogram* cur_combo = histos + 0; // trial merged histogram VP8LHistogram* best_combo = histos + 1; // best merged histogram so far if (histos == NULL) goto End; // Copy histograms from in[] to out[]. - for (i = 0; i < in_size; ++i) { - out[i] = (VP8LHistogram*)malloc(sizeof(*(out[i]))); - if (out[i] == NULL) goto End; - *(out[i]) = *(in[i]); - out[i]->bit_cost_ = in[i]->bit_cost_ = VP8LHistogramEstimateBits(out[i]); + assert(in->size <= out->size); + for (i = 0; i < in->size; ++i) { + in->histograms[i]->bit_cost_ = VP8LHistogramEstimateBits(in->histograms[i]); + *out->histograms[i] = *in->histograms[i]; } // Collapse similar histograms in 'out'. @@ -299,12 +298,13 @@ static int HistogramCombine(VP8LHistogram* const * const in, int in_size, if (idx1 == idx2) { continue; } - *cur_combo = *out[idx1]; - VP8LHistogramAdd(cur_combo, out[idx2]); + *cur_combo = *out->histograms[idx1]; + VP8LHistogramAdd(cur_combo, out->histograms[idx2]); cur_combo->bit_cost_ = VP8LHistogramEstimateBits(cur_combo); // Calculate cost reduction on combining. curr_cost_diff = cur_combo->bit_cost_ - - out[idx1]->bit_cost_ - out[idx2]->bit_cost_; + - out->histograms[idx1]->bit_cost_ + - out->histograms[idx2]->bit_cost_; if (best_cost_diff > curr_cost_diff) { // found a better pair? { // swap cur/best combo histograms VP8LHistogram* const tmp = cur_combo; @@ -318,13 +318,12 @@ static int HistogramCombine(VP8LHistogram* const * const in, int in_size, } if (best_cost_diff < 0.0) { - *out[best_idx1] = *best_combo; + *out->histograms[best_idx1] = *best_combo; // swap best_idx2 slot with last one (which is now unused) - free(out[best_idx2]); --out_size; if (best_idx2 != out_size) { - out[best_idx2] = out[out_size]; - out[out_size] = NULL; // just for sanity check. + out->histograms[best_idx2] = out->histograms[out_size]; + out->histograms[out_size] = NULL; // just for sanity check. } tries_with_no_success = 0; } @@ -332,7 +331,7 @@ static int HistogramCombine(VP8LHistogram* const * const in, int in_size, break; } } - *final_out_size = out_size; + out->size = out_size; ok = 1; End: @@ -359,18 +358,19 @@ static double HistogramDistance(const VP8LHistogram* const square_histogram, return new_bit_cost - previous_bit_cost; } -// Find the best 'out' histogram for each of the raw histograms. +// Find the best 'out' histogram for each of the 'in' histograms. // Note: we assume that out[]->bit_cost_ is already up-to-date. -static void HistogramRefine(VP8LHistogram* const * const raw, int raw_size, - uint16_t* const symbols, - VP8LHistogram** const out, int out_size) { +static void HistogramRemap(const VP8LHistogramSet* const in, + const VP8LHistogramSet* const out, + uint16_t* const symbols) { int i; - for (i = 0; i < raw_size; ++i) { + for (i = 0; i < in->size; ++i) { int best_out = 0; - double best_bits = HistogramDistance(raw[i], out[0]); + double best_bits = HistogramDistance(in->histograms[i], out->histograms[0]); int k; - for (k = 1; k < out_size; ++k) { - const double cur_bits = HistogramDistance(raw[i], out[k]); + for (k = 1; k < out->size; ++k) { + const double cur_bits = + HistogramDistance(in->histograms[i], out->histograms[k]); if (cur_bits < best_bits) { best_bits = cur_bits; best_out = k; @@ -380,47 +380,40 @@ static void HistogramRefine(VP8LHistogram* const * const raw, int raw_size, } // Recompute each out based on raw and symbols. - for (i = 0; i < out_size; ++i) { - VP8LHistogramClear(out[i]); + for (i = 0; i < out->size; ++i) { + HistogramClear(out->histograms[i]); } - for (i = 0; i < raw_size; ++i) { - VP8LHistogramAdd(out[symbols[i]], raw[i]); + for (i = 0; i < in->size; ++i) { + VP8LHistogramAdd(out->histograms[symbols[i]], in->histograms[i]); } } int VP8LGetHistoImageSymbols(int xsize, int ysize, const VP8LBackwardRefs* const refs, int quality, int histo_bits, int cache_bits, - VP8LHistogram** const histogram_image, - int* const histo_image_raw_size_ptr, + VP8LHistogramSet* const image_in, uint16_t* const histogram_symbols) { int ok = 0; const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1; const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1; const int num_histo_pairs = 10 + quality / 2; // For HistogramCombine(). const int histo_image_raw_size = histo_xsize * histo_ysize; - VP8LHistogram** const histo_image_raw = - VP8LAllocateHistograms(histo_image_raw_size, cache_bits); - if (histo_image_raw == NULL) return 0; - *histo_image_raw_size_ptr = histo_image_raw_size; // initial guess. + VP8LHistogramSet* const image_out = + VP8LAllocateHistogramSet(histo_image_raw_size, cache_bits); + if (image_out == NULL) return 0; // Build histogram image. - HistogramBuildImage(xsize, histo_bits, refs, histo_image_raw); + HistogramBuildImage(xsize, histo_bits, refs, image_out); // Collapse similar histograms. - if (!HistogramCombine(histo_image_raw, histo_image_raw_size, num_histo_pairs, - histogram_image, histo_image_raw_size_ptr)) { + if (!HistogramCombine(image_out, image_in, num_histo_pairs)) { goto Error; } - // Refine histogram image. - HistogramRefine(histo_image_raw, histo_image_raw_size, histogram_symbols, - histogram_image, *histo_image_raw_size_ptr); + // Find the optimal map from original histograms to the final ones. + HistogramRemap(image_out, image_in, histogram_symbols); ok = 1; Error: - if (!ok) { - VP8LDeleteHistograms(histogram_image, *histo_image_raw_size_ptr); - } - VP8LDeleteHistograms(histo_image_raw, histo_image_raw_size); + free(image_out); return ok; } diff --git a/src/enc/histogram.h b/src/enc/histogram.h index 7cf69e95..99b17659 100644 --- a/src/enc/histogram.h +++ b/src/enc/histogram.h @@ -41,6 +41,14 @@ typedef struct { double bit_cost_; // cached value of VP8LHistogramEstimateBits(this) } VP8LHistogram; +// Collection of histograms with fixed capacity, allocated as one +// big memory chunk. Can be destroyed by simply calling 'free()'. +typedef struct { + int size; // number of slots currently in use + int max_size; // maximum capacity + VP8LHistogram** histograms; +} VP8LHistogramSet; + // Create the histogram. // // The input data is the PixOrCopy data, which models the literals, stop @@ -50,18 +58,12 @@ void VP8LHistogramCreate(VP8LHistogram* const p, const VP8LBackwardRefs* const refs, int palette_code_bits); -// Reset the histogram's stats. -void VP8LHistogramClear(VP8LHistogram* const p); - // Set the palette_code_bits and reset the stats. void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits); // Allocate an array of pointer to histograms, allocated and initialized // using 'cache_bits'. Return NULL in case of memory error. -VP8LHistogram** VP8LAllocateHistograms(int size, int cache_bits); - -// Destroy an array of histograms (and the array itself). -void VP8LDeleteHistograms(VP8LHistogram** const histograms, int size); +VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits); void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const p, const PixOrCopy* const v); @@ -125,10 +127,8 @@ void VP8LConvertPopulationCountTableToBitEstimates( // Builds the histogram image. int VP8LGetHistoImageSymbols(int xsize, int ysize, const VP8LBackwardRefs* const refs, - int quality, int histogram_bits, - int cache_bits, - VP8LHistogram** const histogram_image, - int* const histogram_image_size, + int quality, int histogram_bits, int cache_bits, + VP8LHistogramSet* const image_in, uint16_t* const histogram_symbols); #if defined(__cplusplus) || defined(c_plusplus) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 709d48f7..e2fbdba3 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -103,7 +103,8 @@ static int AnalyzeAndCreatePalette(const uint32_t* const argb, int num_pix, } static int AnalyzeEntropy(const uint32_t const *argb, int xsize, int ysize, - int* nonpredicted_bits, int* predicted_bits) { + double* const nonpredicted_bits, + double* const predicted_bits) { int i; VP8LHistogram* nonpredicted = NULL; VP8LHistogram* predicted = (VP8LHistogram*)malloc(2 * sizeof(*predicted)); @@ -126,8 +127,8 @@ static int AnalyzeEntropy(const uint32_t const *argb, int xsize, int ysize, VP8LHistogramAddSinglePixOrCopy(predicted, &pix_diff_token); } } - *nonpredicted_bits = (int)VP8LHistogramEstimateBitsBulk(nonpredicted); - *predicted_bits = (int)VP8LHistogramEstimateBitsBulk(predicted); + *nonpredicted_bits = VP8LHistogramEstimateBitsBulk(nonpredicted); + *predicted_bits = VP8LHistogramEstimateBitsBulk(predicted); free(predicted); return 1; } @@ -140,13 +141,13 @@ static int VP8LEncAnalyze(VP8LEncoder* const enc) { AnalyzeAndCreatePalette(pic->argb, pic->width * pic->height, enc->palette_, &enc->palette_size_); if (!enc->use_palette_) { - int non_pred_entropy, pred_entropy; + double non_pred_entropy, pred_entropy; if (!AnalyzeEntropy(pic->argb, pic->width, pic->height, &non_pred_entropy, &pred_entropy)) { return 0; } - if (20 * pred_entropy < 19 * non_pred_entropy) { + if (pred_entropy < 0.95 * non_pred_entropy) { enc->use_predict_ = 1; enc->use_cross_color_ = 1; } @@ -157,7 +158,7 @@ static int VP8LEncAnalyze(VP8LEncoder* const enc) { // ----------------------------------------------------------------------------- static int GetBackwardReferences(int width, int height, - const uint32_t* argb, + const uint32_t* const argb, int quality, int use_color_cache, int cache_bits, int use_2d_locality, VP8LBackwardRefs* const best) { @@ -345,13 +346,15 @@ static int OptimizeHuffmanForRle(int length, int* counts) { // TODO(vikasa): Wrap bit_codes and bit_lengths in a Struct. static int GetHuffBitLengthsAndCodes( - int histogram_image_size, VP8LHistogram** histogram_image, + const VP8LHistogramSet* const histogram_image, int use_color_cache, int** bit_length_sizes, uint16_t*** bit_codes, uint8_t*** bit_lengths) { int i, k; int ok = 1; + const int histogram_image_size = histogram_image->size; for (i = 0; i < histogram_image_size; ++i) { - const int num_literals = VP8LHistogramNumCodes(histogram_image[i]); + VP8LHistogram* const histo = histogram_image->histograms[i]; + const int num_literals = VP8LHistogramNumCodes(histo); k = 0; // TODO(vikasa): Alloc one big buffer instead of allocating in the loop. (*bit_length_sizes)[5 * i] = num_literals; @@ -364,23 +367,21 @@ static int GetHuffBitLengthsAndCodes( } // For each component, optimize histogram for Huffman with RLE compression. - ok = ok && OptimizeHuffmanForRle(num_literals, - histogram_image[i]->literal_); + ok = ok && OptimizeHuffmanForRle(num_literals, histo->literal_); if (!use_color_cache) { // Implies that palette_bits == 0, // and so number of palette entries = (1 << 0) = 1. // Optimization might have smeared population count in this single // palette entry, so zero it out. - histogram_image[i]->literal_[256 + kLengthCodes] = 0; + histo->literal_[256 + kLengthCodes] = 0; } - ok = ok && OptimizeHuffmanForRle(256, histogram_image[i]->red_); - ok = ok && OptimizeHuffmanForRle(256, histogram_image[i]->blue_); - ok = ok && OptimizeHuffmanForRle(256, histogram_image[i]->alpha_); - ok = ok && OptimizeHuffmanForRle(DISTANCE_CODES_MAX, - histogram_image[i]->distance_); + ok = ok && OptimizeHuffmanForRle(256, histo->red_); + ok = ok && OptimizeHuffmanForRle(256, histo->blue_); + ok = ok && OptimizeHuffmanForRle(256, histo->alpha_); + ok = ok && OptimizeHuffmanForRle(DISTANCE_CODES_MAX, histo->distance_); // Create a Huffman tree (in the form of bit lengths) for each component. - ok = ok && VP8LCreateHuffmanTree(histogram_image[i]->literal_, num_literals, + ok = ok && VP8LCreateHuffmanTree(histo->literal_, num_literals, 15, (*bit_lengths)[5 * i]); for (k = 1; k < 5; ++k) { int val = 256; @@ -396,14 +397,14 @@ static int GetHuffBitLengthsAndCodes( goto Error; } } - ok = ok && VP8LCreateHuffmanTree(histogram_image[i]->red_, 256, 15, + ok = ok && + VP8LCreateHuffmanTree(histo->red_, 256, 15, (*bit_lengths)[5 * i + 1]) && - VP8LCreateHuffmanTree(histogram_image[i]->blue_, 256, 15, + VP8LCreateHuffmanTree(histo->blue_, 256, 15, (*bit_lengths)[5 * i + 2]) && - VP8LCreateHuffmanTree(histogram_image[i]->alpha_, 256, 15, + VP8LCreateHuffmanTree(histo->alpha_, 256, 15, (*bit_lengths)[5 * i + 3]) && - VP8LCreateHuffmanTree(histogram_image[i]->distance_, - DISTANCE_CODES_MAX, 15, + VP8LCreateHuffmanTree(histo->distance_, DISTANCE_CODES_MAX, 15, (*bit_lengths)[5 * i + 4]); // Create the actual bit codes for the bit lengths. for (k = 0; k < 5; ++k) { @@ -657,10 +658,11 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, const int use_2d_locality = 1; const int use_color_cache = (cache_bits > 0); const int color_cache_size = use_color_cache ? (1 << cache_bits) : 0; - const int histogram_image_xysize = VP8LSubSampleSize(width, histogram_bits) * + const int histogram_image_xysize = + VP8LSubSampleSize(width, histogram_bits) * VP8LSubSampleSize(height, histogram_bits); - VP8LHistogram** histogram_image = - (VP8LHistogram**)calloc(histogram_image_xysize, sizeof(*histogram_image)); + VP8LHistogramSet* histogram_image = + VP8LAllocateHistogramSet(histogram_image_xysize, 0); int histogram_image_size; VP8LBackwardRefs refs; uint16_t* const histogram_symbols = @@ -677,11 +679,12 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, // Build histogram image & symbols from backward references. if (!VP8LGetHistoImageSymbols(width, height, &refs, quality, histogram_bits, cache_bits, - histogram_image, &histogram_image_size, + histogram_image, histogram_symbols)) { goto Error; } // Create Huffman bit lengths & codes for each histogram image. + histogram_image_size = histogram_image->size; bit_lengths_sizes = (int*)calloc(5 * histogram_image_size, sizeof(*bit_lengths_sizes)); bit_lengths = (uint8_t**)calloc(5 * histogram_image_size, @@ -689,8 +692,8 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, bit_codes = (uint16_t**)calloc(5 * histogram_image_size, sizeof(*bit_codes)); if (bit_lengths_sizes == NULL || bit_lengths == NULL || bit_codes == NULL || - !GetHuffBitLengthsAndCodes(histogram_image_size, histogram_image, - use_color_cache, &bit_lengths_sizes, + !GetHuffBitLengthsAndCodes(histogram_image, use_color_cache, + &bit_lengths_sizes, &bit_codes, &bit_lengths)) { goto Error; } @@ -742,7 +745,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, } // Free combined histograms. - VP8LDeleteHistograms(histogram_image, histogram_image_size); + free(histogram_image); histogram_image = NULL; // Emit no bits if there is only one symbol in the histogram. @@ -757,9 +760,8 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, ok = 1; Error: - if (!ok) { - VP8LDeleteHistograms(histogram_image, histogram_image_size); - } + if (!ok) free(histogram_image); + VP8LClearBackwardRefs(&refs); for (i = 0; i < 5 * histogram_image_size; ++i) { free(bit_lengths[i]); From e8d3d6a01858fe54202afb43f56e19b3ff311763 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Thu, 3 May 2012 17:04:32 +0000 Subject: [PATCH 46/46] split StoreHuffmanCode() into smaller functions Change-Id: Iaa715f4997505eebabee1e92e964a5d7ee6f3e7d --- src/enc/vp8l.c | 172 +++++++++++++++++++++++++++---------------------- 1 file changed, 94 insertions(+), 78 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index e2fbdba3..7d5e70fa 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -432,8 +432,10 @@ static void ClearHuffmanTreeIfOnlyOneSymbol(const int num_symbols, int k; int count = 0; for (k = 0; k < num_symbols; ++k) { - if (lengths[k] != 0) ++count; - if (count > 1) return; + if (lengths[k] != 0) { + ++count; + if (count > 1) return; + } } for (k = 0; k < num_symbols; ++k) { lengths[k] = 0; @@ -490,34 +492,105 @@ static void StoreHuffmanTreeToBitMask( } } +static int StoreFullHuffmanCode(VP8LBitWriter* const bw, + const uint8_t* const bit_lengths, + int bit_lengths_size) { + int ok = 0; + int huffman_tree_size = 0; + uint8_t code_length_bitdepth[CODE_LENGTH_CODES] = { 0 }; + uint16_t code_length_bitdepth_symbols[CODE_LENGTH_CODES] = { 0 }; + uint8_t* huffman_tree_extra_bits; + uint8_t* const huffman_tree = + (uint8_t*)malloc(bit_lengths_size * sizeof(*huffman_tree) + + bit_lengths_size * sizeof(*huffman_tree_extra_bits)); + + if (huffman_tree == NULL) return 0; + huffman_tree_extra_bits = + huffman_tree + bit_lengths_size * sizeof(*huffman_tree); + + VP8LWriteBits(bw, 1, 0); + VP8LCreateCompressedHuffmanTree(bit_lengths, bit_lengths_size, + &huffman_tree_size, huffman_tree, + huffman_tree_extra_bits); + { + int histogram[CODE_LENGTH_CODES] = { 0 }; + int i; + for (i = 0; i < huffman_tree_size; ++i) { + ++histogram[huffman_tree[i]]; + } + + if (!VP8LCreateHuffmanTree(histogram, CODE_LENGTH_CODES, + 7, code_length_bitdepth)) { + goto End; + } + } + VP8LConvertBitDepthsToSymbols(code_length_bitdepth, CODE_LENGTH_CODES, + code_length_bitdepth_symbols); + StoreHuffmanTreeOfHuffmanTreeToBitMask(bw, code_length_bitdepth); + ClearHuffmanTreeIfOnlyOneSymbol(CODE_LENGTH_CODES, + code_length_bitdepth, + code_length_bitdepth_symbols); + { + int trailing_zero_bits = 0; + int trimmed_length = huffman_tree_size; + int write_trimmed_length; + int length; + int i = huffman_tree_size; + while (i-- > 0) { + const int ix = huffman_tree[i]; + if (ix == 0 || ix == 17 || ix == 18) { + --trimmed_length; // discount trailing zeros + trailing_zero_bits += code_length_bitdepth[ix]; + if (ix == 17) { + trailing_zero_bits += 3; + } else if (ix == 18) { + trailing_zero_bits += 7; + } + } else { + break; + } + } + write_trimmed_length = (trimmed_length > 1 && trailing_zero_bits > 12); + length = write_trimmed_length ? trimmed_length : huffman_tree_size; + VP8LWriteBits(bw, 1, write_trimmed_length); + if (write_trimmed_length) { + const int nbits = VP8LBitsLog2Ceiling(trimmed_length - 1); + const int nbitpairs = (nbits == 0) ? 1 : (nbits + 1) / 2; + VP8LWriteBits(bw, 3, nbitpairs - 1); + VP8LWriteBits(bw, nbitpairs * 2, trimmed_length - 2); + } + StoreHuffmanTreeToBitMask(bw, huffman_tree, huffman_tree_extra_bits, + length, code_length_bitdepth, + code_length_bitdepth_symbols); + } + ok = 1; + End: + free(huffman_tree); + return ok; +} + static int StoreHuffmanCode(VP8LBitWriter* const bw, const uint8_t* const bit_lengths, int bit_lengths_size) { int i; - int ok = 0; int count = 0; int symbols[2] = { 0, 0 }; - int huffman_tree_size = 0; - uint8_t code_length_bitdepth[CODE_LENGTH_CODES]; - uint16_t code_length_bitdepth_symbols[CODE_LENGTH_CODES]; - int huffman_tree_histogram[CODE_LENGTH_CODES]; - uint8_t* huffman_tree_extra_bits; - uint8_t* huffman_tree = (uint8_t*)malloc(bit_lengths_size * - (sizeof(*huffman_tree) + - sizeof(*huffman_tree_extra_bits))); + const int kMaxBits = 8; + const int kMaxSymbol = 1 << kMaxBits; - if (huffman_tree == NULL) goto End; - huffman_tree_extra_bits = - huffman_tree + (bit_lengths_size * sizeof(*huffman_tree)); - - for (i = 0; i < bit_lengths_size; ++i) { + // Check whether it's a small tree. + for (i = 0; i < bit_lengths_size && count < 3; ++i) { if (bit_lengths[i] != 0) { if (count < 2) symbols[count] = i; ++count; } } - if (count == 0) count = 1; - if (count <= 2 && symbols[0] < 256 && symbols[1] < 256) { + + if (count == 0) { // emit minimal tree for empty cases + // bits: small tree marker: 1, count-1: 0, large 8-bit code: 0, code: 0 + VP8LWriteBits(bw, 4, 0x01); + return 1; + } else if (count <= 2 && symbols[0] < kMaxSymbol && symbols[1] < kMaxSymbol) { VP8LWriteBits(bw, 1, 1); // Small tree marker to encode 1 or 2 symbols. VP8LWriteBits(bw, 1, count - 1); if (symbols[0] <= 1) { @@ -530,67 +603,10 @@ static int StoreHuffmanCode(VP8LBitWriter* const bw, if (count == 2) { VP8LWriteBits(bw, 8, symbols[1]); } - ok = 1; - goto End; + return 1; + } else { + return StoreFullHuffmanCode(bw, bit_lengths, bit_lengths_size); } - - VP8LWriteBits(bw, 1, 0); - VP8LCreateCompressedHuffmanTree(bit_lengths, bit_lengths_size, - &huffman_tree_size, huffman_tree, - huffman_tree_extra_bits); - memset(huffman_tree_histogram, 0, sizeof(huffman_tree_histogram)); - for (i = 0; i < huffman_tree_size; ++i) { - ++huffman_tree_histogram[huffman_tree[i]]; - } - memset(code_length_bitdepth, 0, sizeof(code_length_bitdepth)); - memset(code_length_bitdepth_symbols, 0, sizeof(code_length_bitdepth_symbols)); - - if (!VP8LCreateHuffmanTree(huffman_tree_histogram, CODE_LENGTH_CODES, - 7, code_length_bitdepth)) { - goto End; - } - VP8LConvertBitDepthsToSymbols(code_length_bitdepth, CODE_LENGTH_CODES, - code_length_bitdepth_symbols); - StoreHuffmanTreeOfHuffmanTreeToBitMask(bw, code_length_bitdepth); - ClearHuffmanTreeIfOnlyOneSymbol(CODE_LENGTH_CODES, - code_length_bitdepth, - code_length_bitdepth_symbols); - { - int num_trailing_zeros = 0; - int trailing_zero_bits = 0; - int trimmed_length; - int write_length; - int length; - for (i = huffman_tree_size; i > 0; --i) { - int ix = huffman_tree[i - 1]; - if (ix == 0 || ix == 17 || ix == 18) { - ++num_trailing_zeros; - trailing_zero_bits += code_length_bitdepth[ix]; - if (ix == 17) trailing_zero_bits += 3; - if (ix == 18) trailing_zero_bits += 7; - } else { - break; - } - } - trimmed_length = huffman_tree_size - num_trailing_zeros; - write_length = (trimmed_length > 1 && trailing_zero_bits > 12); - length = write_length ? trimmed_length : huffman_tree_size; - VP8LWriteBits(bw, 1, write_length); - if (write_length) { - const int nbits = VP8LBitsLog2Ceiling(trimmed_length - 1); - const int nbitpairs = nbits == 0 ? 1 : (nbits + 1) / 2; - VP8LWriteBits(bw, 3, nbitpairs - 1); - VP8LWriteBits(bw, nbitpairs * 2, trimmed_length - 2); - } - StoreHuffmanTreeToBitMask(bw, huffman_tree, huffman_tree_extra_bits, - length, code_length_bitdepth, - code_length_bitdepth_symbols); - } - ok = 1; - - End: - free(huffman_tree); - return ok; } static void StoreImageToBitMask(