From a1779a017b46090095fdd9cf6c1c42bf90d38cc8 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Wed, 7 Jun 2017 15:13:35 +0200 Subject: [PATCH] Refactor LZ77 handling in preparation for a new method. Change-Id: If305c328c8f508bd778d1af108e4eb979fbd2eca --- src/enc/backward_references_enc.c | 46 +++++++++------- src/enc/backward_references_enc.h | 7 ++- src/enc/vp8l_enc.c | 87 +++++++++++++++++-------------- 3 files changed, 83 insertions(+), 57 deletions(-) diff --git a/src/enc/backward_references_enc.c b/src/enc/backward_references_enc.c index 3b6e7614..b649c090 100644 --- a/src/enc/backward_references_enc.c +++ b/src/enc/backward_references_enc.c @@ -688,26 +688,34 @@ extern int VP8LBackwardReferencesTraceBackwards( const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst); static VP8LBackwardRefs* GetBackwardReferences( int width, int height, const uint32_t* const argb, int quality, - int* const cache_bits, const VP8LHashChain* const hash_chain, - VP8LBackwardRefs* best, VP8LBackwardRefs* worst) { + int lz77_types_to_try, int* const cache_bits, + const VP8LHashChain* const hash_chain, VP8LBackwardRefs* best, + VP8LBackwardRefs* worst) { const int cache_bits_initial = *cache_bits; double bit_cost_best = -1; VP8LHistogram* histo = NULL; - int i, i_best = 0; + int lz77_type, lz77_type_best = 0; histo = VP8LAllocateHistogram(MAX_COLOR_CACHE_BITS); if (histo == NULL) goto Error; - // Try out RLE first, then LZ77. - for (i = 0; i <= 1; ++i) { - int res; + for (lz77_type = 1; lz77_types_to_try; + lz77_types_to_try &= ~lz77_type, lz77_type <<= 1) { + int res = 0; double bit_cost; int cache_bits_tmp = cache_bits_initial; - // First, try out backward references with no cache (0 bits). - if (i == 0) { - res = BackwardReferencesRle(width, height, argb, 0, worst); - } else { - res = BackwardReferencesLz77(width, height, argb, 0, hash_chain, worst); + if ((lz77_types_to_try & lz77_type) == 0) continue; + switch (lz77_type) { + case kLZ77RLE: + res = BackwardReferencesRle(width, height, argb, 0, worst); + break; + case kLZ77Standard: + // Compute LZ77 with no cache (0 bits), as the ideal LZ77 with a color + // cache is not that different in practice. + res = BackwardReferencesLz77(width, height, argb, 0, hash_chain, worst); + break; + default: + assert(0); } if (!res) goto Error; @@ -724,19 +732,20 @@ static VP8LBackwardRefs* GetBackwardReferences( // Keep the best backward references. VP8LHistogramCreate(histo, worst, cache_bits_tmp); bit_cost = VP8LHistogramEstimateBits(histo); - if (i == 0 || bit_cost < bit_cost_best) { + if (lz77_type_best == 0 || bit_cost < bit_cost_best) { VP8LBackwardRefs* const tmp = worst; worst = best; best = tmp; bit_cost_best = bit_cost; *cache_bits = cache_bits_tmp; - i_best = i; + lz77_type_best = lz77_type; } } + assert(lz77_type_best > 0); // Improve on simple LZ77 but only for high quality (TraceBackwards is // costly). - if (i_best == 1 && quality >= 25) { + if (lz77_type_best == kLZ77Standard && quality >= 25) { if (VP8LBackwardReferencesTraceBackwards(width, height, argb, *cache_bits, hash_chain, best, worst)) { double bit_cost_trace; @@ -748,21 +757,22 @@ static VP8LBackwardRefs* GetBackwardReferences( BackwardReferences2DLocality(width, best); - Error: +Error: VP8LFreeHistogram(histo); return best; } VP8LBackwardRefs* VP8LGetBackwardReferences( int width, int height, const uint32_t* const argb, int quality, - int low_effort, int* const cache_bits, + int low_effort, int lz77_types_to_try, int* const cache_bits, const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1, VP8LBackwardRefs* const refs_tmp2) { if (low_effort) { return GetBackwardReferencesLowEffort(width, height, argb, cache_bits, hash_chain, refs_tmp1); } else { - return GetBackwardReferences(width, height, argb, quality, cache_bits, - hash_chain, refs_tmp1, refs_tmp2); + return GetBackwardReferences(width, height, argb, quality, + lz77_types_to_try, cache_bits, hash_chain, + refs_tmp1, refs_tmp2); } } diff --git a/src/enc/backward_references_enc.h b/src/enc/backward_references_enc.h index 2d8e0ca8..ed27c372 100644 --- a/src/enc/backward_references_enc.h +++ b/src/enc/backward_references_enc.h @@ -213,6 +213,11 @@ static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) { // ----------------------------------------------------------------------------- // Main entry points +enum VP8LLZ77Type { + kLZ77Standard = 1, + kLZ77RLE = 2, +}; + // Evaluates best possible backward references for specified quality. // The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache // bits to use (passing 0 implies disabling the local color cache). @@ -221,7 +226,7 @@ static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) { // refs[0] or refs[1]. VP8LBackwardRefs* VP8LGetBackwardReferences( int width, int height, const uint32_t* const argb, int quality, - int low_effort, int* const cache_bits, + int low_effort, int lz77_types_to_try, int* const cache_bits, const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1, VP8LBackwardRefs* const refs_tmp2); diff --git a/src/enc/vp8l_enc.c b/src/enc/vp8l_enc.c index 1d499dd0..4b66d2d2 100644 --- a/src/enc/vp8l_enc.c +++ b/src/enc/vp8l_enc.c @@ -356,9 +356,17 @@ static int GetTransformBits(int method, int histo_bits) { return res; } +// Se of parameters to be used in each iteration of the cruncher. +typedef struct { + int entropy_idx_; + int lz77s_types_to_try_; +} CrunchConfig; + +#define CRUNCH_CONFIGS_MAX kNumEntropyIx + static int AnalyzeAndInit(VP8LEncoder* const enc, - int entropy_idx[kNumEntropyIx], - int* const num_entropy_idx, + CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX], + int* const crunch_configs_size, int* const red_and_blue_always_zero) { const WebPPicture* const pic = enc->pic_; const int width = pic->width; @@ -386,8 +394,9 @@ static int AnalyzeAndInit(VP8LEncoder* const enc, if (low_effort) { // AnalyzeEntropy is somewhat slow. - entropy_idx[0] = use_palette ? kPalette : kSpatialSubGreen; - *num_entropy_idx = 1; + crunch_configs[0].entropy_idx_ = use_palette ? kPalette : kSpatialSubGreen; + crunch_configs[0].lz77s_types_to_try_ = kLZ77Standard | kLZ77RLE; + *crunch_configs_size = 1; } else { EntropyIx min_entropy_ix; if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride, use_palette, @@ -395,19 +404,22 @@ static int AnalyzeAndInit(VP8LEncoder* const enc, &min_entropy_ix, red_and_blue_always_zero)) { return 0; } + *crunch_configs_size = 0; if (method == 6 && config->quality == 100) { // Go brute force on all transforms. - *num_entropy_idx = 0; for (i = 0; i < kNumEntropyIx; ++i) { if (i != kPalette || use_palette) { - entropy_idx[(*num_entropy_idx)++] = i; - assert(*num_entropy_idx <= kNumEntropyIx); + crunch_configs[*crunch_configs_size].entropy_idx_ = i; + crunch_configs[(*crunch_configs_size)++].lz77s_types_to_try_ = + kLZ77Standard | kLZ77RLE; + assert(*crunch_configs_size <= CRUNCH_CONFIGS_MAX); } } } else { // Only choose the guessed best transform. - entropy_idx[0] = min_entropy_ix; - *num_entropy_idx = 1; + crunch_configs[*crunch_configs_size].entropy_idx_ = min_entropy_ix; + crunch_configs[(*crunch_configs_size)++].lz77s_types_to_try_ = + kLZ77Standard | kLZ77RLE; } } @@ -771,7 +783,8 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw, err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; } - refs = VP8LGetBackwardReferences(width, height, argb, quality, 0, &cache_bits, + refs = VP8LGetBackwardReferences(width, height, argb, quality, 0, + kLZ77Standard | kLZ77RLE, &cache_bits, hash_chain, refs_tmp1, refs_tmp2); if (refs == NULL) { err = VP8_ENC_ERROR_OUT_OF_MEMORY; @@ -829,17 +842,12 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw, return err; } -static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, - const uint32_t* const argb, - VP8LHashChain* const hash_chain, - VP8LBackwardRefs refs_array[3], - int width, int height, int quality, - int low_effort, - int use_cache, int* cache_bits, - int histogram_bits, - size_t init_byte_position, - int* const hdr_size, - int* const data_size) { +static WebPEncodingError EncodeImageInternal( + VP8LBitWriter* const bw, const uint32_t* const argb, + VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[3], int width, + int height, int quality, int low_effort, int use_cache, int lz77s_to_try, + int* cache_bits, int histogram_bits, size_t init_byte_position, + int* const hdr_size, int* const data_size) { WebPEncodingError err = VP8_ENC_OK; const uint32_t histogram_image_xysize = VP8LSubSampleSize(width, histogram_bits) * @@ -881,9 +889,9 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; } - refs_best = VP8LGetBackwardReferences(width, height, argb, quality, - low_effort, cache_bits, hash_chain, - &refs_array[0], &refs_array[1]); + refs_best = VP8LGetBackwardReferences( + width, height, argb, quality, low_effort, lz77s_to_try, cache_bits, + hash_chain, &refs_array[0], &refs_array[1]); if (refs_best == NULL) { err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; @@ -1506,8 +1514,8 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, int hdr_size = 0; int data_size = 0; int use_delta_palette = 0; - int entropy_idx[kNumEntropyIx]; - int num_entropy_idx = 0; + CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX]; + int num_crunch_configs = 0; int idx; int red_and_blue_always_zero = 0; size_t best_size = 0; @@ -1521,18 +1529,19 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, // --------------------------------------------------------------------------- // Analyze image (entropy, num_palettes etc) - if (!AnalyzeAndInit(enc, entropy_idx, &num_entropy_idx, + if (!AnalyzeAndInit(enc, crunch_configs, &num_crunch_configs, &red_and_blue_always_zero)) { err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; } - for (idx = 0; idx < num_entropy_idx; ++idx) { - enc->use_palette_ = (entropy_idx[idx] == kPalette); - enc->use_subtract_green_ = (entropy_idx[idx] == kSubGreen) || - (entropy_idx[idx] == kSpatialSubGreen); - enc->use_predict_ = (entropy_idx[idx] == kSpatial) || - (entropy_idx[idx] == kSpatialSubGreen); + for (idx = 0; idx < num_crunch_configs; ++idx) { + const int entropy_idx = crunch_configs[idx].entropy_idx_; + enc->use_palette_ = (entropy_idx == kPalette); + enc->use_subtract_green_ = + (entropy_idx == kSubGreen) || (entropy_idx == kSpatialSubGreen); + enc->use_predict_ = + (entropy_idx == kSpatial) || (entropy_idx == kSpatialSubGreen); if (low_effort) { enc->use_cross_color_ = 0; } else { @@ -1626,10 +1635,11 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, // ------------------------------------------------------------------------- // Encode and write the transformed image. - err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_, - enc->current_width_, height, quality, low_effort, - use_cache, &enc->cache_bits_, enc->histo_bits_, - byte_position, &hdr_size, &data_size); + err = EncodeImageInternal( + bw, enc->argb_, &enc->hash_chain_, enc->refs_, enc->current_width_, + height, quality, low_effort, use_cache, + crunch_configs[idx].lz77s_types_to_try_, &enc->cache_bits_, + enc->histo_bits_, byte_position, &hdr_size, &data_size); if (err != VP8_ENC_OK) goto Error; // If we are better than what we already have. @@ -1655,7 +1665,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, } } // Reset the bit writer for the following iteration if any. - if (num_entropy_idx > 1) VP8LBitWriterReset(&bw_init, bw); + if (num_crunch_configs > 1) VP8LBitWriterReset(&bw_init, bw); } VP8LBitWriterSwap(&bw_best, bw); @@ -1664,6 +1674,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, VP8LBitWriterWipeOut(&bw_best); return err; } +#undef CRUNCH_CONFIGS_MAX int VP8LEncodeImage(const WebPConfig* const config, const WebPPicture* const picture) {