diff --git a/src/enc/backward_references.c b/src/enc/backward_references.c index a458ecab..348805cd 100644 --- a/src/enc/backward_references.c +++ b/src/enc/backward_references.c @@ -810,32 +810,48 @@ static void BackwardReferences2DLocality(int xsize, } } +static int CalculateBestCacheSize(const uint32_t* const argb, + int xsize, int ysize, int quality, + VP8LHashChain* const hash_chain, + VP8LBackwardRefs* const refs, + int* const best_cache_bits); + VP8LBackwardRefs* VP8LGetBackwardReferences( int width, int height, const uint32_t* const argb, int quality, - int cache_bits, int use_2d_locality, VP8LHashChain* const hash_chain, + int* cache_bits, VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[2]) { int lz77_is_useful; double bit_cost_lz77, bit_cost_rle; VP8LBackwardRefs* best = NULL; VP8LBackwardRefs* const refs_lz77 = &refs_array[0]; VP8LBackwardRefs* const refs_rle = &refs_array[1]; - VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits); - if (histo == NULL) return NULL; + VP8LHistogram* histo = NULL; - if (!BackwardReferencesLz77(width, height, argb, cache_bits, quality, + if (!CalculateBestCacheSize(argb, width, height, quality, hash_chain, + refs_lz77, cache_bits)) { + goto Error; + } + + // TODO(vikasa): Use the refs_lz77 computed (with cache_bits=0) in the method + // 'CalculateBestCacheSize' to regenerate the lz77 references corresponding to + // the optimum cache_bits and avoid calling 'BackwardReferencesLz77' here. + if (!BackwardReferencesLz77(width, height, argb, *cache_bits, quality, hash_chain, refs_lz77)) { goto Error; } - if (!BackwardReferencesRle(width, height, argb, cache_bits, refs_rle)) { + if (!BackwardReferencesRle(width, height, argb, *cache_bits, refs_rle)) { goto Error; } + histo = VP8LAllocateHistogram(*cache_bits); + if (histo == NULL) goto Error; + { // Evaluate LZ77 coding. - VP8LHistogramCreate(histo, refs_lz77, cache_bits); + VP8LHistogramCreate(histo, refs_lz77, *cache_bits); bit_cost_lz77 = VP8LHistogramEstimateBits(histo); // Evaluate RLE coding. - VP8LHistogramCreate(histo, refs_rle, cache_bits); + 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); @@ -853,11 +869,11 @@ VP8LBackwardRefs* VP8LGetBackwardReferences( goto Error; } if (BackwardReferencesTraceBackwards(width, height, argb, quality, - cache_bits, hash_chain, + *cache_bits, hash_chain, refs_trace)) { double bit_cost_trace; // Evaluate LZ77 coding. - VP8LHistogramCreate(histo, refs_trace, cache_bits); + VP8LHistogramCreate(histo, refs_trace, *cache_bits); bit_cost_trace = VP8LHistogramEstimateBits(histo); if (bit_cost_trace < bit_cost_lz77) { best = refs_trace; @@ -868,7 +884,7 @@ VP8LBackwardRefs* VP8LGetBackwardReferences( best = refs_rle; } - if (use_2d_locality) BackwardReferences2DLocality(width, best); + BackwardReferences2DLocality(width, best); Error: VP8LFreeHistogram(histo); @@ -930,30 +946,33 @@ static double ComputeCacheEntropy(const uint32_t* const argb, return entropy; } -// *best_cache_bits will contain how many bits are to be used for a color cache. +// Evaluate optimal cache bits for the local color cache. +// The input *best_cache_bits sets the maximum cache bits to use (passing 0 +// implies disabling the local color cache). The local color cache is also +// disabled for the lower (<= 25) quality. // Returns 0 in case of memory error. -int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb, - int xsize, int ysize, int quality, - VP8LHashChain* const hash_chain, - VP8LBackwardRefs* const refs, - int* const best_cache_bits) { +static int CalculateBestCacheSize(const uint32_t* const argb, + int xsize, int ysize, int quality, + VP8LHashChain* const hash_chain, + VP8LBackwardRefs* const refs, + int* const best_cache_bits) { int eval_low = 1; int eval_high = 1; double entropy_low = MAX_ENTROPY; double entropy_high = MAX_ENTROPY; const double cost_mul = 5e-4; int cache_bits_low = 0; - int cache_bits_high = *best_cache_bits; + int cache_bits_high = (quality <= 25) ? 0 : *best_cache_bits; assert(cache_bits_high <= MAX_COLOR_CACHE_BITS); if (cache_bits_high == 0) { + *best_cache_bits = 0; // Local color cache is disabled. return 1; } - - if (!BackwardReferencesLz77(xsize, ysize, argb, 0, quality, hash_chain, - refs)) { + if (!BackwardReferencesLz77(xsize, ysize, argb, cache_bits_low, quality, + hash_chain, refs)) { return 0; } // Do a binary search to find the optimal entropy for cache_bits. diff --git a/src/enc/backward_references.h b/src/enc/backward_references.h index 886b093d..ddab9c00 100644 --- a/src/enc/backward_references.h +++ b/src/enc/backward_references.h @@ -185,21 +185,16 @@ static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) { // Main entry points // Evaluates best possible backward references for specified quality. -// Further optimize for 2D locality if use_2d_locality flag is set. +// The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache +// bits to use (passing 0 implies disabling the local color cache). +// The optimal cache bits is evaluated and set for the *cache_bits parameter. // The return value is the pointer to the best of the two backward refs viz, // refs[0] or refs[1]. VP8LBackwardRefs* VP8LGetBackwardReferences( int width, int height, const uint32_t* const argb, int quality, - int cache_bits, int use_2d_locality, VP8LHashChain* const hash_chain, + int* cache_bits, VP8LHashChain* const hash_chain, VP8LBackwardRefs refs[2]); -// Produce an estimate for a good color cache size for the image. -int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb, - int xsize, int ysize, int quality, - VP8LHashChain* const hash_chain, - VP8LBackwardRefs* const ref, - int* const best_cache_bits); - #ifdef __cplusplus } #endif diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 4960fde3..3bdd0c21 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -281,12 +281,6 @@ static int GetTransformBits(int method, int histo_bits) { return (histo_bits > max_transform_bits) ? max_transform_bits : histo_bits; } -static int GetCacheBits(int use_palette, float quality) { - // Color cache is disabled for compression at lower quality or when a palette - // is used. - return (use_palette || (quality <= 25.f)) ? 0 : MAX_COLOR_CACHE_BITS; -} - static int EvalSubtractGreenForPalette(int palette_size, float quality) { // Evaluate non-palette encoding (subtract green, prediction transforms etc) // for palette size in the mid-range (17-96) as for larger number of colors, @@ -351,7 +345,6 @@ static int AnalyzeAndInit(VP8LEncoder* const enc, WebPImageHint image_hint) { enc->use_subtract_green_ = 1; } } - enc->cache_bits_ = GetCacheBits(enc->use_palette_, quality); if (!enc->use_palette_) { if (image_hint == WEBP_HINT_PHOTO) { @@ -690,21 +683,28 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw, HuffmanTreeToken* tokens = NULL; HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } }; const uint16_t histogram_symbols[1] = { 0 }; // only one tree, one symbol - VP8LHistogramSet* const histogram_image = VP8LAllocateHistogramSet(1, 0); + int cache_bits = 0; + VP8LHistogramSet* histogram_image = NULL; HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc( 3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree)); - if (histogram_image == NULL || huff_tree == NULL) { + if (huff_tree == NULL) { err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; } // Calculate backward references from ARGB image. - refs = VP8LGetBackwardReferences(width, height, argb, quality, 0, 1, + refs = VP8LGetBackwardReferences(width, height, argb, quality, &cache_bits, hash_chain, refs_array); if (refs == NULL) { err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; } + histogram_image = VP8LAllocateHistogramSet(1, cache_bits); + if (histogram_image == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + // Build histogram image and symbols from backward references. VP8LHistogramStoreRefs(refs, histogram_image->histograms[0]); @@ -756,19 +756,16 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[2], int width, int height, int quality, - int cache_bits, + 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 int use_2d_locality = 1; - const int use_color_cache = (cache_bits > 0); const uint32_t histogram_image_xysize = VP8LSubSampleSize(width, histogram_bits) * VP8LSubSampleSize(height, histogram_bits); - VP8LHistogramSet* histogram_image = - VP8LAllocateHistogramSet(histogram_image_xysize, cache_bits); + VP8LHistogramSet* histogram_image = NULL; int histogram_image_size = 0; size_t bit_array_size = 0; HuffmanTree* huff_tree = NULL; @@ -785,25 +782,31 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, assert(data_size != NULL); VP8LBackwardRefsInit(&refs, refs_array[0].block_size_); - if (histogram_image == NULL || histogram_symbols == NULL) { + if (histogram_symbols == NULL) { err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; } + *cache_bits = MAX_COLOR_CACHE_BITS; // 'best_refs' is the reference to the best backward refs and points to one // of refs_array[0] or refs_array[1]. // Calculate backward references from ARGB image. best_refs = VP8LGetBackwardReferences(width, height, argb, quality, - cache_bits, use_2d_locality, - hash_chain, refs_array); + cache_bits, hash_chain, refs_array); if (best_refs == NULL || !VP8LBackwardRefsCopy(best_refs, &refs)) { err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; } + histogram_image = + VP8LAllocateHistogramSet(histogram_image_xysize, *cache_bits); + if (histogram_image == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + // Build histogram image and symbols from backward references. - if (!VP8LGetHistoImageSymbols(width, height, &refs, - quality, histogram_bits, cache_bits, - histogram_image, + if (!VP8LGetHistoImageSymbols(width, height, &refs, quality, histogram_bits, + *cache_bits, histogram_image, histogram_symbols)) { err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; @@ -823,9 +826,11 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, histogram_image = NULL; // Color Cache parameters. - VP8LPutBits(bw, use_color_cache, 1); - if (use_color_cache) { - VP8LPutBits(bw, cache_bits, 4); + if (*cache_bits > 0) { + VP8LPutBits(bw, 1, 1); + VP8LPutBits(bw, *cache_bits, 4); + } else { + VP8LPutBits(bw, 0, 1); } // Huffman image + meta huffman. @@ -1306,23 +1311,12 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, VP8LPutBits(bw, !TRANSFORM_PRESENT, 1); // No more transforms. - // --------------------------------------------------------------------------- - // Estimate the color cache size. - - if (!VP8LCalculateEstimateForCacheSize(enc->argb_, enc->current_width_, - height, quality, &enc->hash_chain_, - &enc->refs_[0], &enc->cache_bits_)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - // --------------------------------------------------------------------------- // Encode and write the transformed image. - err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_, enc->current_width_, height, quality, - enc->cache_bits_, enc->histo_bits_, - byte_position, &hdr_size, &data_size); + &enc->cache_bits_, enc->histo_bits_, byte_position, + &hdr_size, &data_size); if (err != VP8_ENC_OK) goto Error; if (picture->stats != NULL) {