From 1a210ef1a9e307218500c5c7088e5b4e1a1436f0 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Mon, 30 Apr 2012 12:18:50 +0000 Subject: [PATCH] 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);