mirror of
				https://github.com/webmproject/libwebp.git
				synced 2025-10-30 18:05:36 +01:00 
			
		
		
		
	Refactor LZ77 handling in preparation for a new method.
Change-Id: If305c328c8f508bd778d1af108e4eb979fbd2eca
This commit is contained in:
		| @@ -688,26 +688,34 @@ extern int VP8LBackwardReferencesTraceBackwards( | |||||||
|     const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst); |     const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst); | ||||||
| static VP8LBackwardRefs* GetBackwardReferences( | static VP8LBackwardRefs* GetBackwardReferences( | ||||||
|     int width, int height, const uint32_t* const argb, int quality, |     int width, int height, const uint32_t* const argb, int quality, | ||||||
|     int* const cache_bits, const VP8LHashChain* const hash_chain, |     int lz77_types_to_try, int* const cache_bits, | ||||||
|     VP8LBackwardRefs* best, VP8LBackwardRefs* worst) { |     const VP8LHashChain* const hash_chain, VP8LBackwardRefs* best, | ||||||
|  |     VP8LBackwardRefs* worst) { | ||||||
|   const int cache_bits_initial = *cache_bits; |   const int cache_bits_initial = *cache_bits; | ||||||
|   double bit_cost_best = -1; |   double bit_cost_best = -1; | ||||||
|   VP8LHistogram* histo = NULL; |   VP8LHistogram* histo = NULL; | ||||||
|   int i, i_best = 0; |   int lz77_type, lz77_type_best = 0; | ||||||
|  |  | ||||||
|   histo = VP8LAllocateHistogram(MAX_COLOR_CACHE_BITS); |   histo = VP8LAllocateHistogram(MAX_COLOR_CACHE_BITS); | ||||||
|   if (histo == NULL) goto Error; |   if (histo == NULL) goto Error; | ||||||
|  |  | ||||||
|   // Try out RLE first, then LZ77. |   for (lz77_type = 1; lz77_types_to_try; | ||||||
|   for (i = 0; i <= 1; ++i) { |        lz77_types_to_try &= ~lz77_type, lz77_type <<= 1) { | ||||||
|     int res; |     int res = 0; | ||||||
|     double bit_cost; |     double bit_cost; | ||||||
|     int cache_bits_tmp = cache_bits_initial; |     int cache_bits_tmp = cache_bits_initial; | ||||||
|     // First, try out backward references with no cache (0 bits). |     if ((lz77_types_to_try & lz77_type) == 0) continue; | ||||||
|     if (i == 0) { |     switch (lz77_type) { | ||||||
|  |       case kLZ77RLE: | ||||||
|         res = BackwardReferencesRle(width, height, argb, 0, worst); |         res = BackwardReferencesRle(width, height, argb, 0, worst); | ||||||
|     } else { |         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); |         res = BackwardReferencesLz77(width, height, argb, 0, hash_chain, worst); | ||||||
|  |         break; | ||||||
|  |       default: | ||||||
|  |         assert(0); | ||||||
|     } |     } | ||||||
|     if (!res) goto Error; |     if (!res) goto Error; | ||||||
|  |  | ||||||
| @@ -724,19 +732,20 @@ static VP8LBackwardRefs* GetBackwardReferences( | |||||||
|     // Keep the best backward references. |     // Keep the best backward references. | ||||||
|     VP8LHistogramCreate(histo, worst, cache_bits_tmp); |     VP8LHistogramCreate(histo, worst, cache_bits_tmp); | ||||||
|     bit_cost = VP8LHistogramEstimateBits(histo); |     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; |       VP8LBackwardRefs* const tmp = worst; | ||||||
|       worst = best; |       worst = best; | ||||||
|       best = tmp; |       best = tmp; | ||||||
|       bit_cost_best = bit_cost; |       bit_cost_best = bit_cost; | ||||||
|       *cache_bits = cache_bits_tmp; |       *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 |   // Improve on simple LZ77 but only for high quality (TraceBackwards is | ||||||
|   // costly). |   // costly). | ||||||
|   if (i_best == 1 && quality >= 25) { |   if (lz77_type_best == kLZ77Standard && quality >= 25) { | ||||||
|     if (VP8LBackwardReferencesTraceBackwards(width, height, argb, *cache_bits, |     if (VP8LBackwardReferencesTraceBackwards(width, height, argb, *cache_bits, | ||||||
|                                              hash_chain, best, worst)) { |                                              hash_chain, best, worst)) { | ||||||
|       double bit_cost_trace; |       double bit_cost_trace; | ||||||
| @@ -748,21 +757,22 @@ static VP8LBackwardRefs* GetBackwardReferences( | |||||||
|  |  | ||||||
|   BackwardReferences2DLocality(width, best); |   BackwardReferences2DLocality(width, best); | ||||||
|  |  | ||||||
|  Error: | Error: | ||||||
|   VP8LFreeHistogram(histo); |   VP8LFreeHistogram(histo); | ||||||
|   return best; |   return best; | ||||||
| } | } | ||||||
|  |  | ||||||
| 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 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, |     const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1, | ||||||
|     VP8LBackwardRefs* const refs_tmp2) { |     VP8LBackwardRefs* const refs_tmp2) { | ||||||
|   if (low_effort) { |   if (low_effort) { | ||||||
|     return GetBackwardReferencesLowEffort(width, height, argb, cache_bits, |     return GetBackwardReferencesLowEffort(width, height, argb, cache_bits, | ||||||
|                                           hash_chain, refs_tmp1); |                                           hash_chain, refs_tmp1); | ||||||
|   } else { |   } else { | ||||||
|     return GetBackwardReferences(width, height, argb, quality, cache_bits, |     return GetBackwardReferences(width, height, argb, quality, | ||||||
|                                  hash_chain, refs_tmp1, refs_tmp2); |                                  lz77_types_to_try, cache_bits, hash_chain, | ||||||
|  |                                  refs_tmp1, refs_tmp2); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -213,6 +213,11 @@ static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) { | |||||||
| // ----------------------------------------------------------------------------- | // ----------------------------------------------------------------------------- | ||||||
| // Main entry points | // Main entry points | ||||||
|  |  | ||||||
|  | enum VP8LLZ77Type { | ||||||
|  |   kLZ77Standard = 1, | ||||||
|  |   kLZ77RLE = 2, | ||||||
|  | }; | ||||||
|  |  | ||||||
| // Evaluates best possible backward references for specified quality. | // Evaluates best possible backward references for specified quality. | ||||||
| // The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache | // The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache | ||||||
| // bits to use (passing 0 implies disabling the local color 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]. | // 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 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, |     const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1, | ||||||
|     VP8LBackwardRefs* const refs_tmp2); |     VP8LBackwardRefs* const refs_tmp2); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -356,9 +356,17 @@ static int GetTransformBits(int method, int histo_bits) { | |||||||
|   return res; |   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, | static int AnalyzeAndInit(VP8LEncoder* const enc, | ||||||
|                           int entropy_idx[kNumEntropyIx], |                           CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX], | ||||||
|                           int* const num_entropy_idx, |                           int* const crunch_configs_size, | ||||||
|                           int* const red_and_blue_always_zero) { |                           int* const red_and_blue_always_zero) { | ||||||
|   const WebPPicture* const pic = enc->pic_; |   const WebPPicture* const pic = enc->pic_; | ||||||
|   const int width = pic->width; |   const int width = pic->width; | ||||||
| @@ -386,8 +394,9 @@ static int AnalyzeAndInit(VP8LEncoder* const enc, | |||||||
|  |  | ||||||
|   if (low_effort) { |   if (low_effort) { | ||||||
|     // AnalyzeEntropy is somewhat slow. |     // AnalyzeEntropy is somewhat slow. | ||||||
|     entropy_idx[0] = use_palette ? kPalette : kSpatialSubGreen; |     crunch_configs[0].entropy_idx_ = use_palette ? kPalette : kSpatialSubGreen; | ||||||
|     *num_entropy_idx = 1; |     crunch_configs[0].lz77s_types_to_try_ = kLZ77Standard | kLZ77RLE; | ||||||
|  |     *crunch_configs_size = 1; | ||||||
|   } else { |   } else { | ||||||
|     EntropyIx min_entropy_ix; |     EntropyIx min_entropy_ix; | ||||||
|     if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride, use_palette, |     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)) { |                         &min_entropy_ix, red_and_blue_always_zero)) { | ||||||
|       return 0; |       return 0; | ||||||
|     } |     } | ||||||
|  |     *crunch_configs_size = 0; | ||||||
|     if (method == 6 && config->quality == 100) { |     if (method == 6 && config->quality == 100) { | ||||||
|       // Go brute force on all transforms. |       // Go brute force on all transforms. | ||||||
|       *num_entropy_idx = 0; |  | ||||||
|       for (i = 0; i < kNumEntropyIx; ++i) { |       for (i = 0; i < kNumEntropyIx; ++i) { | ||||||
|         if (i != kPalette || use_palette) { |         if (i != kPalette || use_palette) { | ||||||
|           entropy_idx[(*num_entropy_idx)++] = i; |           crunch_configs[*crunch_configs_size].entropy_idx_ = i; | ||||||
|           assert(*num_entropy_idx <= kNumEntropyIx); |           crunch_configs[(*crunch_configs_size)++].lz77s_types_to_try_ = | ||||||
|  |               kLZ77Standard | kLZ77RLE; | ||||||
|  |           assert(*crunch_configs_size <= CRUNCH_CONFIGS_MAX); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       // Only choose the guessed best transform. |       // Only choose the guessed best transform. | ||||||
|       entropy_idx[0] = min_entropy_ix; |       crunch_configs[*crunch_configs_size].entropy_idx_ = min_entropy_ix; | ||||||
|       *num_entropy_idx = 1; |       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; |     err = VP8_ENC_ERROR_OUT_OF_MEMORY; | ||||||
|     goto Error; |     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); |                                    hash_chain, refs_tmp1, refs_tmp2); | ||||||
|   if (refs == NULL) { |   if (refs == NULL) { | ||||||
|     err = VP8_ENC_ERROR_OUT_OF_MEMORY; |     err = VP8_ENC_ERROR_OUT_OF_MEMORY; | ||||||
| @@ -829,17 +842,12 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw, | |||||||
|   return err; |   return err; | ||||||
| } | } | ||||||
|  |  | ||||||
| static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, | static WebPEncodingError EncodeImageInternal( | ||||||
|                                              const uint32_t* const argb, |     VP8LBitWriter* const bw, const uint32_t* const argb, | ||||||
|                                              VP8LHashChain* const hash_chain, |     VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[3], int width, | ||||||
|                                              VP8LBackwardRefs refs_array[3], |     int height, int quality, int low_effort, int use_cache, int lz77s_to_try, | ||||||
|                                              int width, int height, int quality, |     int* cache_bits, int histogram_bits, size_t init_byte_position, | ||||||
|                                              int low_effort, |     int* const hdr_size, int* const data_size) { | ||||||
|                                              int use_cache, int* cache_bits, |  | ||||||
|                                              int histogram_bits, |  | ||||||
|                                              size_t init_byte_position, |  | ||||||
|                                              int* const hdr_size, |  | ||||||
|                                              int* const data_size) { |  | ||||||
|   WebPEncodingError err = VP8_ENC_OK; |   WebPEncodingError err = VP8_ENC_OK; | ||||||
|   const uint32_t histogram_image_xysize = |   const uint32_t histogram_image_xysize = | ||||||
|       VP8LSubSampleSize(width, histogram_bits) * |       VP8LSubSampleSize(width, histogram_bits) * | ||||||
| @@ -881,9 +889,9 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, | |||||||
|     err = VP8_ENC_ERROR_OUT_OF_MEMORY; |     err = VP8_ENC_ERROR_OUT_OF_MEMORY; | ||||||
|     goto Error; |     goto Error; | ||||||
|   } |   } | ||||||
|   refs_best = VP8LGetBackwardReferences(width, height, argb, quality, |   refs_best = VP8LGetBackwardReferences( | ||||||
|                                         low_effort, cache_bits, hash_chain, |       width, height, argb, quality, low_effort, lz77s_to_try, cache_bits, | ||||||
|                                         &refs_array[0], &refs_array[1]); |       hash_chain, &refs_array[0], &refs_array[1]); | ||||||
|   if (refs_best == NULL) { |   if (refs_best == NULL) { | ||||||
|     err = VP8_ENC_ERROR_OUT_OF_MEMORY; |     err = VP8_ENC_ERROR_OUT_OF_MEMORY; | ||||||
|     goto Error; |     goto Error; | ||||||
| @@ -1506,8 +1514,8 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, | |||||||
|   int hdr_size = 0; |   int hdr_size = 0; | ||||||
|   int data_size = 0; |   int data_size = 0; | ||||||
|   int use_delta_palette = 0; |   int use_delta_palette = 0; | ||||||
|   int entropy_idx[kNumEntropyIx]; |   CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX]; | ||||||
|   int num_entropy_idx = 0; |   int num_crunch_configs = 0; | ||||||
|   int idx; |   int idx; | ||||||
|   int red_and_blue_always_zero = 0; |   int red_and_blue_always_zero = 0; | ||||||
|   size_t best_size = 0; |   size_t best_size = 0; | ||||||
| @@ -1521,18 +1529,19 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, | |||||||
|   // --------------------------------------------------------------------------- |   // --------------------------------------------------------------------------- | ||||||
|   // Analyze image (entropy, num_palettes etc) |   // 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)) { |                       &red_and_blue_always_zero)) { | ||||||
|     err = VP8_ENC_ERROR_OUT_OF_MEMORY; |     err = VP8_ENC_ERROR_OUT_OF_MEMORY; | ||||||
|     goto Error; |     goto Error; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   for (idx = 0; idx < num_entropy_idx; ++idx) { |   for (idx = 0; idx < num_crunch_configs; ++idx) { | ||||||
|     enc->use_palette_ = (entropy_idx[idx] == kPalette); |     const int entropy_idx = crunch_configs[idx].entropy_idx_; | ||||||
|     enc->use_subtract_green_ = (entropy_idx[idx] == kSubGreen) || |     enc->use_palette_ = (entropy_idx == kPalette); | ||||||
|                                (entropy_idx[idx] == kSpatialSubGreen); |     enc->use_subtract_green_ = | ||||||
|     enc->use_predict_ = (entropy_idx[idx] == kSpatial) || |         (entropy_idx == kSubGreen) || (entropy_idx == kSpatialSubGreen); | ||||||
|                         (entropy_idx[idx] == kSpatialSubGreen); |     enc->use_predict_ = | ||||||
|  |         (entropy_idx == kSpatial) || (entropy_idx == kSpatialSubGreen); | ||||||
|     if (low_effort) { |     if (low_effort) { | ||||||
|       enc->use_cross_color_ = 0; |       enc->use_cross_color_ = 0; | ||||||
|     } else { |     } else { | ||||||
| @@ -1626,10 +1635,11 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, | |||||||
|  |  | ||||||
|     // ------------------------------------------------------------------------- |     // ------------------------------------------------------------------------- | ||||||
|     // Encode and write the transformed image. |     // Encode and write the transformed image. | ||||||
|     err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_, |     err = EncodeImageInternal( | ||||||
|                               enc->current_width_, height, quality, low_effort, |         bw, enc->argb_, &enc->hash_chain_, enc->refs_, enc->current_width_, | ||||||
|                               use_cache, &enc->cache_bits_, enc->histo_bits_, |         height, quality, low_effort, use_cache, | ||||||
|                               byte_position, &hdr_size, &data_size); |         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 (err != VP8_ENC_OK) goto Error; | ||||||
|  |  | ||||||
|     // If we are better than what we already have. |     // 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. |     // 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); |   VP8LBitWriterSwap(&bw_best, bw); | ||||||
|  |  | ||||||
| @@ -1664,6 +1674,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, | |||||||
|   VP8LBitWriterWipeOut(&bw_best); |   VP8LBitWriterWipeOut(&bw_best); | ||||||
|   return err; |   return err; | ||||||
| } | } | ||||||
|  | #undef CRUNCH_CONFIGS_MAX | ||||||
|  |  | ||||||
| int VP8LEncodeImage(const WebPConfig* const config, | int VP8LEncodeImage(const WebPConfig* const config, | ||||||
|                     const WebPPicture* const picture) { |                     const WebPPicture* const picture) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user