mirror of
				https://github.com/webmproject/libwebp.git
				synced 2025-10-31 02:15:42 +01:00 
			
		
		
		
	Updated VP8LGetBackwardReferences and color cache.
- The optimal cache bits is evaluated inside the method 'VP8LGetBackwardReferences'. - The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache bits to use (passing 0 implies disabling the local color cache). - The local color cache is disabled for lowerf (<= 25) quality levels (as before). - Enabled local color cache for palette images as well. This saves additional 0.017% bytes with a slight (2-3%) improvement in the compression speed. - Removed 'use_2d_locality' parameter from method VP8LGetBackwardReferences, as this option is not an option now (after we freeze the lossless bit-stream). Change-Id: I33430401e465474fa1be899f330387cd2b466280
This commit is contained in:
		| @@ -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( | VP8LBackwardRefs* VP8LGetBackwardReferences( | ||||||
|     int width, int height, const uint32_t* const argb, int quality, |     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]) { |     VP8LBackwardRefs refs_array[2]) { | ||||||
|   int lz77_is_useful; |   int lz77_is_useful; | ||||||
|   double bit_cost_lz77, bit_cost_rle; |   double bit_cost_lz77, bit_cost_rle; | ||||||
|   VP8LBackwardRefs* best = NULL; |   VP8LBackwardRefs* best = NULL; | ||||||
|   VP8LBackwardRefs* const refs_lz77 = &refs_array[0]; |   VP8LBackwardRefs* const refs_lz77 = &refs_array[0]; | ||||||
|   VP8LBackwardRefs* const refs_rle = &refs_array[1]; |   VP8LBackwardRefs* const refs_rle = &refs_array[1]; | ||||||
|   VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits); |   VP8LHistogram* histo = NULL; | ||||||
|   if (histo == NULL) return 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)) { |                               hash_chain, refs_lz77)) { | ||||||
|     goto Error; |     goto Error; | ||||||
|   } |   } | ||||||
|   if (!BackwardReferencesRle(width, height, argb, cache_bits, refs_rle)) { |   if (!BackwardReferencesRle(width, height, argb, *cache_bits, refs_rle)) { | ||||||
|     goto Error; |     goto Error; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   histo = VP8LAllocateHistogram(*cache_bits); | ||||||
|  |   if (histo == NULL) goto Error; | ||||||
|  |  | ||||||
|   { |   { | ||||||
|     // Evaluate LZ77 coding. |     // Evaluate LZ77 coding. | ||||||
|     VP8LHistogramCreate(histo, refs_lz77, cache_bits); |     VP8LHistogramCreate(histo, refs_lz77, *cache_bits); | ||||||
|     bit_cost_lz77 = VP8LHistogramEstimateBits(histo); |     bit_cost_lz77 = VP8LHistogramEstimateBits(histo); | ||||||
|     // Evaluate RLE coding. |     // Evaluate RLE coding. | ||||||
|     VP8LHistogramCreate(histo, refs_rle, cache_bits); |     VP8LHistogramCreate(histo, refs_rle, *cache_bits); | ||||||
|     bit_cost_rle = VP8LHistogramEstimateBits(histo); |     bit_cost_rle = VP8LHistogramEstimateBits(histo); | ||||||
|     // Decide if LZ77 is useful. |     // Decide if LZ77 is useful. | ||||||
|     lz77_is_useful = (bit_cost_lz77 < bit_cost_rle); |     lz77_is_useful = (bit_cost_lz77 < bit_cost_rle); | ||||||
| @@ -853,11 +869,11 @@ VP8LBackwardRefs* VP8LGetBackwardReferences( | |||||||
|         goto Error; |         goto Error; | ||||||
|       } |       } | ||||||
|       if (BackwardReferencesTraceBackwards(width, height, argb, quality, |       if (BackwardReferencesTraceBackwards(width, height, argb, quality, | ||||||
|                                            cache_bits, hash_chain, |                                            *cache_bits, hash_chain, | ||||||
|                                            refs_trace)) { |                                            refs_trace)) { | ||||||
|         double bit_cost_trace; |         double bit_cost_trace; | ||||||
|         // Evaluate LZ77 coding. |         // Evaluate LZ77 coding. | ||||||
|         VP8LHistogramCreate(histo, refs_trace, cache_bits); |         VP8LHistogramCreate(histo, refs_trace, *cache_bits); | ||||||
|         bit_cost_trace = VP8LHistogramEstimateBits(histo); |         bit_cost_trace = VP8LHistogramEstimateBits(histo); | ||||||
|         if (bit_cost_trace < bit_cost_lz77) { |         if (bit_cost_trace < bit_cost_lz77) { | ||||||
|           best = refs_trace; |           best = refs_trace; | ||||||
| @@ -868,7 +884,7 @@ VP8LBackwardRefs* VP8LGetBackwardReferences( | |||||||
|     best = refs_rle; |     best = refs_rle; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (use_2d_locality) BackwardReferences2DLocality(width, best); |   BackwardReferences2DLocality(width, best); | ||||||
|  |  | ||||||
|  Error: |  Error: | ||||||
|   VP8LFreeHistogram(histo); |   VP8LFreeHistogram(histo); | ||||||
| @@ -930,30 +946,33 @@ static double ComputeCacheEntropy(const uint32_t* const argb, | |||||||
|   return entropy; |   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. | // Returns 0 in case of memory error. | ||||||
| int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb, | static int CalculateBestCacheSize(const uint32_t* const argb, | ||||||
|                                       int xsize, int ysize, int quality, |                                   int xsize, int ysize, int quality, | ||||||
|                                       VP8LHashChain* const hash_chain, |                                   VP8LHashChain* const hash_chain, | ||||||
|                                       VP8LBackwardRefs* const refs, |                                   VP8LBackwardRefs* const refs, | ||||||
|                                       int* const best_cache_bits) { |                                   int* const best_cache_bits) { | ||||||
|   int eval_low = 1; |   int eval_low = 1; | ||||||
|   int eval_high = 1; |   int eval_high = 1; | ||||||
|   double entropy_low = MAX_ENTROPY; |   double entropy_low = MAX_ENTROPY; | ||||||
|   double entropy_high = MAX_ENTROPY; |   double entropy_high = MAX_ENTROPY; | ||||||
|   const double cost_mul = 5e-4; |   const double cost_mul = 5e-4; | ||||||
|   int cache_bits_low = 0; |   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); |   assert(cache_bits_high <= MAX_COLOR_CACHE_BITS); | ||||||
|  |  | ||||||
|   if (cache_bits_high == 0) { |   if (cache_bits_high == 0) { | ||||||
|  |     *best_cache_bits = 0; | ||||||
|     // Local color cache is disabled. |     // Local color cache is disabled. | ||||||
|     return 1; |     return 1; | ||||||
|   } |   } | ||||||
|  |   if (!BackwardReferencesLz77(xsize, ysize, argb, cache_bits_low, quality, | ||||||
|   if (!BackwardReferencesLz77(xsize, ysize, argb, 0, quality, hash_chain, |                               hash_chain, refs)) { | ||||||
|                               refs)) { |  | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|   // Do a binary search to find the optimal entropy for cache_bits. |   // Do a binary search to find the optimal entropy for cache_bits. | ||||||
|   | |||||||
| @@ -185,21 +185,16 @@ static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) { | |||||||
| // Main entry points | // Main entry points | ||||||
|  |  | ||||||
| // Evaluates best possible backward references for specified quality. | // 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, | // The return value is the pointer to the best of the two backward refs viz, | ||||||
| // refs[0] or refs[1]. | // refs[0] or refs[1]. | ||||||
| VP8LBackwardRefs* VP8LGetBackwardReferences( | VP8LBackwardRefs* VP8LGetBackwardReferences( | ||||||
|     int width, int height, const uint32_t* const argb, int quality, |     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]); |     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 | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -281,12 +281,6 @@ static int GetTransformBits(int method, int histo_bits) { | |||||||
|   return (histo_bits > max_transform_bits) ? max_transform_bits : 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) { | static int EvalSubtractGreenForPalette(int palette_size, float quality) { | ||||||
|   // Evaluate non-palette encoding (subtract green, prediction transforms etc) |   // Evaluate non-palette encoding (subtract green, prediction transforms etc) | ||||||
|   // for palette size in the mid-range (17-96) as for larger number of colors, |   // 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->use_subtract_green_ = 1; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   enc->cache_bits_ = GetCacheBits(enc->use_palette_, quality); |  | ||||||
|  |  | ||||||
|   if (!enc->use_palette_) { |   if (!enc->use_palette_) { | ||||||
|     if (image_hint == WEBP_HINT_PHOTO) { |     if (image_hint == WEBP_HINT_PHOTO) { | ||||||
| @@ -690,21 +683,28 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw, | |||||||
|   HuffmanTreeToken* tokens = NULL; |   HuffmanTreeToken* tokens = NULL; | ||||||
|   HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } }; |   HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } }; | ||||||
|   const uint16_t histogram_symbols[1] = { 0 };    // only one tree, one symbol |   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( |   HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc( | ||||||
|         3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree)); |         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; |     err = VP8_ENC_ERROR_OUT_OF_MEMORY; | ||||||
|     goto Error; |     goto Error; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Calculate backward references from ARGB image. |   // 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); |                                    hash_chain, refs_array); | ||||||
|   if (refs == NULL) { |   if (refs == NULL) { | ||||||
|     err = VP8_ENC_ERROR_OUT_OF_MEMORY; |     err = VP8_ENC_ERROR_OUT_OF_MEMORY; | ||||||
|     goto Error; |     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. |   // Build histogram image and symbols from backward references. | ||||||
|   VP8LHistogramStoreRefs(refs, histogram_image->histograms[0]); |   VP8LHistogramStoreRefs(refs, histogram_image->histograms[0]); | ||||||
|  |  | ||||||
| @@ -756,19 +756,16 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, | |||||||
|                                              VP8LHashChain* const hash_chain, |                                              VP8LHashChain* const hash_chain, | ||||||
|                                              VP8LBackwardRefs refs_array[2], |                                              VP8LBackwardRefs refs_array[2], | ||||||
|                                              int width, int height, int quality, |                                              int width, int height, int quality, | ||||||
|                                              int cache_bits, |                                              int* cache_bits, | ||||||
|                                              int histogram_bits, |                                              int histogram_bits, | ||||||
|                                              size_t init_byte_position, |                                              size_t init_byte_position, | ||||||
|                                              int* const hdr_size, |                                              int* const hdr_size, | ||||||
|                                              int* const data_size) { |                                              int* const data_size) { | ||||||
|   WebPEncodingError err = VP8_ENC_OK; |   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 = |   const uint32_t histogram_image_xysize = | ||||||
|       VP8LSubSampleSize(width, histogram_bits) * |       VP8LSubSampleSize(width, histogram_bits) * | ||||||
|       VP8LSubSampleSize(height, histogram_bits); |       VP8LSubSampleSize(height, histogram_bits); | ||||||
|   VP8LHistogramSet* histogram_image = |   VP8LHistogramSet* histogram_image = NULL; | ||||||
|       VP8LAllocateHistogramSet(histogram_image_xysize, cache_bits); |  | ||||||
|   int histogram_image_size = 0; |   int histogram_image_size = 0; | ||||||
|   size_t bit_array_size = 0; |   size_t bit_array_size = 0; | ||||||
|   HuffmanTree* huff_tree = NULL; |   HuffmanTree* huff_tree = NULL; | ||||||
| @@ -785,25 +782,31 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, | |||||||
|   assert(data_size != NULL); |   assert(data_size != NULL); | ||||||
|  |  | ||||||
|   VP8LBackwardRefsInit(&refs, refs_array[0].block_size_); |   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; |     err = VP8_ENC_ERROR_OUT_OF_MEMORY; | ||||||
|     goto Error; |     goto Error; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   *cache_bits = MAX_COLOR_CACHE_BITS; | ||||||
|   // 'best_refs' is the reference to the best backward refs and points to one |   // 'best_refs' is the reference to the best backward refs and points to one | ||||||
|   // of refs_array[0] or refs_array[1]. |   // of refs_array[0] or refs_array[1]. | ||||||
|   // Calculate backward references from ARGB image. |   // Calculate backward references from ARGB image. | ||||||
|   best_refs = VP8LGetBackwardReferences(width, height, argb, quality, |   best_refs = VP8LGetBackwardReferences(width, height, argb, quality, | ||||||
|                                         cache_bits, use_2d_locality, |                                         cache_bits, hash_chain, refs_array); | ||||||
|                                         hash_chain, refs_array); |  | ||||||
|   if (best_refs == NULL || !VP8LBackwardRefsCopy(best_refs, &refs)) { |   if (best_refs == NULL || !VP8LBackwardRefsCopy(best_refs, &refs)) { | ||||||
|     err = VP8_ENC_ERROR_OUT_OF_MEMORY; |     err = VP8_ENC_ERROR_OUT_OF_MEMORY; | ||||||
|     goto Error; |     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. |   // Build histogram image and symbols from backward references. | ||||||
|   if (!VP8LGetHistoImageSymbols(width, height, &refs, |   if (!VP8LGetHistoImageSymbols(width, height, &refs, quality, histogram_bits, | ||||||
|                                 quality, histogram_bits, cache_bits, |                                 *cache_bits, histogram_image, | ||||||
|                                 histogram_image, |  | ||||||
|                                 histogram_symbols)) { |                                 histogram_symbols)) { | ||||||
|     err = VP8_ENC_ERROR_OUT_OF_MEMORY; |     err = VP8_ENC_ERROR_OUT_OF_MEMORY; | ||||||
|     goto Error; |     goto Error; | ||||||
| @@ -823,9 +826,11 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, | |||||||
|   histogram_image = NULL; |   histogram_image = NULL; | ||||||
|  |  | ||||||
|   // Color Cache parameters. |   // Color Cache parameters. | ||||||
|   VP8LPutBits(bw, use_color_cache, 1); |   if (*cache_bits > 0) { | ||||||
|   if (use_color_cache) { |     VP8LPutBits(bw, 1, 1); | ||||||
|     VP8LPutBits(bw, cache_bits, 4); |     VP8LPutBits(bw, *cache_bits, 4); | ||||||
|  |   } else { | ||||||
|  |     VP8LPutBits(bw, 0, 1); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Huffman image + meta huffman. |   // Huffman image + meta huffman. | ||||||
| @@ -1306,23 +1311,12 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, | |||||||
|  |  | ||||||
|   VP8LPutBits(bw, !TRANSFORM_PRESENT, 1);  // No more transforms. |   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. |   // Encode and write the transformed image. | ||||||
|  |  | ||||||
|   err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_, |   err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_, | ||||||
|                             enc->current_width_, height, quality, |                             enc->current_width_, height, quality, | ||||||
|                             enc->cache_bits_, enc->histo_bits_, |                             &enc->cache_bits_, enc->histo_bits_, byte_position, | ||||||
|                             byte_position, &hdr_size, &data_size); |                             &hdr_size, &data_size); | ||||||
|   if (err != VP8_ENC_OK) goto Error; |   if (err != VP8_ENC_OK) goto Error; | ||||||
|  |  | ||||||
|   if (picture->stats != NULL) { |   if (picture->stats != NULL) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user