mirror of
				https://github.com/webmproject/libwebp.git
				synced 2025-10-31 02:15:42 +01:00 
			
		
		
		
	lossless: encoding, don't compute unnecessary histo
share the computation between different modes 3-5 % speedup for lossless alpha 1 % for lossy alpha no change in compression density Change-Id: I5e31413b3efcd4319121587da8320ac4f14550b2
This commit is contained in:
		
				
					committed by
					
						 James Zern
						James Zern
					
				
			
			
				
	
			
			
			
						parent
						
							d92453f381
						
					
				
				
					commit
					85b44d8a69
				
			| @@ -221,6 +221,9 @@ double VP8LPopulationCost(const uint32_t* const population, int length, | |||||||
| double VP8LGetCombinedEntropy(const uint32_t* const X, | double VP8LGetCombinedEntropy(const uint32_t* const X, | ||||||
|                               const uint32_t* const Y, int length); |                               const uint32_t* const Y, int length); | ||||||
|  |  | ||||||
|  | double VP8LBitsEntropy(const uint32_t* const array, int n, | ||||||
|  |                        uint32_t* const trivial_symbol); | ||||||
|  |  | ||||||
| // Estimate how many bits the combined entropy of literals and distance | // Estimate how many bits the combined entropy of literals and distance | ||||||
| // approximately maps to. | // approximately maps to. | ||||||
| double VP8LHistogramEstimateBits(const VP8LHistogram* const p); | double VP8LHistogramEstimateBits(const VP8LHistogram* const p); | ||||||
|   | |||||||
| @@ -473,7 +473,7 @@ static WEBP_INLINE double BitsEntropyRefine(int nonzeros, int sum, int max_val, | |||||||
| // Returns the entropy for the symbols in the input array. | // Returns the entropy for the symbols in the input array. | ||||||
| // Also sets trivial_symbol to the code value, if the array has only one code | // Also sets trivial_symbol to the code value, if the array has only one code | ||||||
| // value. Otherwise, set it to VP8L_NON_TRIVIAL_SYM. | // value. Otherwise, set it to VP8L_NON_TRIVIAL_SYM. | ||||||
| static double BitsEntropy(const uint32_t* const array, int n, | double VP8LBitsEntropy(const uint32_t* const array, int n, | ||||||
|                        uint32_t* const trivial_symbol) { |                        uint32_t* const trivial_symbol) { | ||||||
|   double retval = 0.; |   double retval = 0.; | ||||||
|   uint32_t sum = 0; |   uint32_t sum = 0; | ||||||
| @@ -555,7 +555,7 @@ static double HuffmanCostCombined(const uint32_t* const X, | |||||||
| double VP8LPopulationCost(const uint32_t* const population, int length, | double VP8LPopulationCost(const uint32_t* const population, int length, | ||||||
|                           uint32_t* const trivial_sym) { |                           uint32_t* const trivial_sym) { | ||||||
|   return |   return | ||||||
|       BitsEntropy(population, length, trivial_sym) + |       VP8LBitsEntropy(population, length, trivial_sym) + | ||||||
|       HuffmanCost(population, length); |       HuffmanCost(population, length); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -579,12 +579,12 @@ double VP8LHistogramEstimateBits(const VP8LHistogram* const p) { | |||||||
|  |  | ||||||
| double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) { | double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) { | ||||||
|   return |   return | ||||||
|       BitsEntropy(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_), |       VP8LBitsEntropy(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_), | ||||||
|                   NULL) |                   NULL) | ||||||
|       + BitsEntropy(p->red_, NUM_LITERAL_CODES, NULL) |       + VP8LBitsEntropy(p->red_, NUM_LITERAL_CODES, NULL) | ||||||
|       + BitsEntropy(p->blue_, NUM_LITERAL_CODES, NULL) |       + VP8LBitsEntropy(p->blue_, NUM_LITERAL_CODES, NULL) | ||||||
|       + BitsEntropy(p->alpha_, NUM_LITERAL_CODES, NULL) |       + VP8LBitsEntropy(p->alpha_, NUM_LITERAL_CODES, NULL) | ||||||
|       + BitsEntropy(p->distance_, NUM_DISTANCE_CODES, NULL) |       + VP8LBitsEntropy(p->distance_, NUM_DISTANCE_CODES, NULL) | ||||||
|       + VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES) |       + VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES) | ||||||
|       + VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES); |       + VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										120
									
								
								src/enc/vp8l.c
									
									
									
									
									
								
							
							
						
						
									
										120
									
								
								src/enc/vp8l.c
									
									
									
									
									
								
							| @@ -189,15 +189,6 @@ static int AnalyzeAndCreatePalette(const WebPPicture* const pic, | |||||||
|   return 1; |   return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void AddSinglePixSubtractGreen(VP8LHistogram* const histo, |  | ||||||
|                                       const PixOrCopy* const v) { |  | ||||||
|   int green = PixOrCopyLiteral(v, 1); |  | ||||||
|   ++histo->alpha_[PixOrCopyLiteral(v, 3)]; |  | ||||||
|   ++histo->red_[(PixOrCopyLiteral(v, 2) - green) & 0xff]; |  | ||||||
|   ++histo->literal_[green]; |  | ||||||
|   ++histo->blue_[(PixOrCopyLiteral(v, 0) - green) & 0xff]; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // These five modes are evaluated and their respective entropy is computed. | // These five modes are evaluated and their respective entropy is computed. | ||||||
| typedef enum { | typedef enum { | ||||||
|   kDirect = 0, |   kDirect = 0, | ||||||
| @@ -205,21 +196,52 @@ typedef enum { | |||||||
|   kSubGreen = 2, |   kSubGreen = 2, | ||||||
|   kSpatialSubGreen = 3, |   kSpatialSubGreen = 3, | ||||||
|   kPalette = 4, |   kPalette = 4, | ||||||
|   kNumEntropyIx = 5, |   kNumEntropyIx = 5 | ||||||
| } EntropyIx; | } EntropyIx; | ||||||
|  |  | ||||||
|  | typedef enum { | ||||||
|  |   kHistoAlpha = 0, | ||||||
|  |   kHistoAlphaPred, | ||||||
|  |   kHistoGreen, | ||||||
|  |   kHistoGreenPred, | ||||||
|  |   kHistoRed, | ||||||
|  |   kHistoRedPred, | ||||||
|  |   kHistoBlue, | ||||||
|  |   kHistoBluePred, | ||||||
|  |   kHistoRedSubGreen, | ||||||
|  |   kHistoRedPredSubGreen, | ||||||
|  |   kHistoBlueSubGreen, | ||||||
|  |   kHistoBluePredSubGreen, | ||||||
|  |   kHistoPalette, | ||||||
|  |   kHistoTotal  // Must be last. | ||||||
|  | } HistoIx; | ||||||
|  |  | ||||||
|  | static void AddSingleSubGreen(uint32_t p, uint32_t* r, uint32_t* b) { | ||||||
|  |   const uint32_t green = (p >> 8) & 0xff; | ||||||
|  |   ++r[((p >> 16) - green) & 0xff]; | ||||||
|  |   ++b[(p - green) & 0xff]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void AddSingle(uint32_t p, | ||||||
|  |                       uint32_t* a, uint32_t* r, uint32_t* g, uint32_t* b) { | ||||||
|  |   ++a[p >> 24]; | ||||||
|  |   ++r[(p >> 16) & 0xff]; | ||||||
|  |   ++g[(p >> 8) & 0xff]; | ||||||
|  |   ++b[(p & 0xff)]; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int AnalyzeEntropy(const uint32_t* argb, | static int AnalyzeEntropy(const uint32_t* argb, | ||||||
|                           int width, int height, int argb_stride, |                           int width, int height, int argb_stride, | ||||||
|                           int use_palette, |                           int use_palette, | ||||||
|                           EntropyIx* const min_entropy_ix, |                           EntropyIx* const min_entropy_ix, | ||||||
|                           int* const red_and_blue_always_zero) { |                           int* const red_and_blue_always_zero) { | ||||||
|   // Allocate histogram set with cache_bits = 0. |   // Allocate histogram set with cache_bits = 0. | ||||||
|   VP8LHistogramSet* const histo_set = VP8LAllocateHistogramSet(5, 0); |   uint32_t* const histo = | ||||||
|   if (histo_set != NULL) { |       (uint32_t*)WebPSafeCalloc(kHistoTotal, sizeof(*histo) * 256); | ||||||
|  |   if (histo != NULL) { | ||||||
|     int i, x, y; |     int i, x, y; | ||||||
|     const uint32_t* prev_row = argb; |     const uint32_t* prev_row = argb; | ||||||
|     const uint32_t* curr_row = argb + argb_stride; |     const uint32_t* curr_row = argb + argb_stride; | ||||||
|     VP8LHistogram* const * const histo = &histo_set->histograms[0]; |  | ||||||
|     for (y = 1; y < height; ++y) { |     for (y = 1; y < height; ++y) { | ||||||
|       uint32_t prev_pix = curr_row[0]; |       uint32_t prev_pix = curr_row[0]; | ||||||
|       for (x = 1; x < width; ++x) { |       for (x = 1; x < width; ++x) { | ||||||
| @@ -227,32 +249,62 @@ static int AnalyzeEntropy(const uint32_t* argb, | |||||||
|         const uint32_t pix_diff = VP8LSubPixels(pix, prev_pix); |         const uint32_t pix_diff = VP8LSubPixels(pix, prev_pix); | ||||||
|         if ((pix_diff == 0) || (pix == prev_row[x])) continue; |         if ((pix_diff == 0) || (pix == prev_row[x])) continue; | ||||||
|         prev_pix = pix; |         prev_pix = pix; | ||||||
|         { |         AddSingle(pix, | ||||||
|           const PixOrCopy pix_token = PixOrCopyCreateLiteral(pix); |                   &histo[kHistoAlpha * 256], | ||||||
|           const PixOrCopy pix_diff_token = PixOrCopyCreateLiteral(pix_diff); |                   &histo[kHistoRed * 256], | ||||||
|           VP8LHistogramAddSinglePixOrCopy(histo[kDirect], &pix_token); |                   &histo[kHistoGreen * 256], | ||||||
|           VP8LHistogramAddSinglePixOrCopy(histo[kSpatial], &pix_diff_token); |                   &histo[kHistoBlue * 256]); | ||||||
|           AddSinglePixSubtractGreen(histo[kSubGreen], &pix_token); |         AddSingle(pix_diff, | ||||||
|           AddSinglePixSubtractGreen(histo[kSpatialSubGreen], &pix_diff_token); |                   &histo[kHistoAlphaPred * 256], | ||||||
|         } |                   &histo[kHistoRedPred * 256], | ||||||
|  |                   &histo[kHistoGreenPred * 256], | ||||||
|  |                   &histo[kHistoBluePred * 256]); | ||||||
|  |         AddSingleSubGreen(pix, | ||||||
|  |                           &histo[kHistoRedSubGreen * 256], | ||||||
|  |                           &histo[kHistoBlueSubGreen * 256]); | ||||||
|  |         AddSingleSubGreen(pix_diff, | ||||||
|  |                           &histo[kHistoRedPredSubGreen * 256], | ||||||
|  |                           &histo[kHistoBluePredSubGreen * 256]); | ||||||
|         { |         { | ||||||
|           // Approximate the palette by the entropy of the multiplicative hash. |           // Approximate the palette by the entropy of the multiplicative hash. | ||||||
|           const int hash = ((pix + (pix >> 19)) * 0x39c5fba7) >> 24; |           const int hash = ((pix + (pix >> 19)) * 0x39c5fba7) >> 24; | ||||||
|           ++histo[kPalette]->red_[hash & 0xff]; |           ++histo[kHistoPalette * 256 + (hash & 0xff)]; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       prev_row = curr_row; |       prev_row = curr_row; | ||||||
|       curr_row += argb_stride; |       curr_row += argb_stride; | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|  |       double entropy_comp[kHistoTotal]; | ||||||
|       double entropy[kNumEntropyIx]; |       double entropy[kNumEntropyIx]; | ||||||
|       EntropyIx k; |       EntropyIx k; | ||||||
|       EntropyIx last_mode_to_analyze = |       EntropyIx last_mode_to_analyze = | ||||||
|           use_palette ? kPalette : kSpatialSubGreen; |           use_palette ? kPalette : kSpatialSubGreen; | ||||||
|  |       int j; | ||||||
|  |       for (j = 0; j < kHistoTotal; ++j) { | ||||||
|  |         entropy_comp[j] = VP8LBitsEntropy(&histo[j * 256], 256, NULL); | ||||||
|  |       } | ||||||
|  |       entropy[kDirect] = entropy_comp[kHistoAlpha] + | ||||||
|  |           entropy_comp[kHistoRed] + | ||||||
|  |           entropy_comp[kHistoGreen] + | ||||||
|  |           entropy_comp[kHistoBlue]; | ||||||
|  |       entropy[kSpatial] = entropy_comp[kHistoAlphaPred] + | ||||||
|  |           entropy_comp[kHistoRedPred] + | ||||||
|  |           entropy_comp[kHistoGreenPred] + | ||||||
|  |           entropy_comp[kHistoBluePred]; | ||||||
|  |       entropy[kSubGreen] = entropy_comp[kHistoAlpha] + | ||||||
|  |           entropy_comp[kHistoRedSubGreen] + | ||||||
|  |           entropy_comp[kHistoGreen] + | ||||||
|  |           entropy_comp[kHistoBlueSubGreen]; | ||||||
|  |       entropy[kSpatialSubGreen] = entropy_comp[kHistoAlphaPred] + | ||||||
|  |           entropy_comp[kHistoRedPredSubGreen] + | ||||||
|  |           entropy_comp[kHistoGreenPred] + | ||||||
|  |           entropy_comp[kHistoBluePredSubGreen]; | ||||||
|  |       entropy[kPalette] = entropy_comp[kHistoPalette]; | ||||||
|  |  | ||||||
|       *min_entropy_ix = kDirect; |       *min_entropy_ix = kDirect; | ||||||
|       for (k = kDirect; k <= last_mode_to_analyze; ++k) { |       for (k = kDirect + 1; k <= last_mode_to_analyze; ++k) { | ||||||
|         entropy[k] = VP8LHistogramEstimateBitsBulk(histo[k]); |         if (entropy[*min_entropy_ix] >= entropy[k]) { | ||||||
|         if (k == kDirect || entropy[*min_entropy_ix] >= entropy[k]) { |  | ||||||
|           *min_entropy_ix = k; |           *min_entropy_ix = k; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| @@ -260,15 +312,27 @@ static int AnalyzeEntropy(const uint32_t* argb, | |||||||
|       // Let's check if the histogram of the chosen entropy mode has |       // Let's check if the histogram of the chosen entropy mode has | ||||||
|       // non-zero red and blue values. If all are zero, we can later skip |       // non-zero red and blue values. If all are zero, we can later skip | ||||||
|       // the cross color optimization. |       // the cross color optimization. | ||||||
|  |       { | ||||||
|  |         static const uint8_t kHistoPairs[5][2] = { | ||||||
|  |           { kHistoRed, kHistoBlue }, | ||||||
|  |           { kHistoRedPred, kHistoBluePred }, | ||||||
|  |           { kHistoRedSubGreen, kHistoBlueSubGreen }, | ||||||
|  |           { kHistoRedPredSubGreen, kHistoBluePredSubGreen }, | ||||||
|  |           { kHistoRed, kHistoBlue } | ||||||
|  |         }; | ||||||
|  |         const uint32_t* const red_histo = | ||||||
|  |             &histo[256 * kHistoPairs[*min_entropy_ix][0]]; | ||||||
|  |         const uint32_t* const blue_histo = | ||||||
|  |             &histo[256 * kHistoPairs[*min_entropy_ix][1]]; | ||||||
|         for (i = 1; i < 256; ++i) { |         for (i = 1; i < 256; ++i) { | ||||||
|         if ((histo[*min_entropy_ix]->red_[i] | |           if ((red_histo[i] | blue_histo[i]) != 0) { | ||||||
|              histo[*min_entropy_ix]->blue_[i]) != 0) { |  | ||||||
|             *red_and_blue_always_zero = 0; |             *red_and_blue_always_zero = 0; | ||||||
|             break; |             break; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     VP8LFreeHistogramSet(histo_set); |     } | ||||||
|  |     free(histo); | ||||||
|     return 1; |     return 1; | ||||||
|   } else { |   } else { | ||||||
|     return 0; |     return 0; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user