From 85b44d8a69e04f781ac5057b122b61cfaeeb2b25 Mon Sep 17 00:00:00 2001 From: Jyrki Alakuijala Date: Thu, 18 Jun 2015 14:54:33 +0000 Subject: [PATCH] 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 --- src/dsp/lossless.h | 3 + src/dsp/lossless_enc.c | 16 +++--- src/enc/vp8l.c | 126 +++++++++++++++++++++++++++++++---------- 3 files changed, 106 insertions(+), 39 deletions(-) diff --git a/src/dsp/lossless.h b/src/dsp/lossless.h index 6da695ac..a7430c49 100644 --- a/src/dsp/lossless.h +++ b/src/dsp/lossless.h @@ -221,6 +221,9 @@ double VP8LPopulationCost(const uint32_t* const population, int length, double VP8LGetCombinedEntropy(const uint32_t* const X, 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 // approximately maps to. double VP8LHistogramEstimateBits(const VP8LHistogram* const p); diff --git a/src/dsp/lossless_enc.c b/src/dsp/lossless_enc.c index 5bad055e..bf20af30 100644 --- a/src/dsp/lossless_enc.c +++ b/src/dsp/lossless_enc.c @@ -473,8 +473,8 @@ static WEBP_INLINE double BitsEntropyRefine(int nonzeros, int sum, int max_val, // 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 // value. Otherwise, set it to VP8L_NON_TRIVIAL_SYM. -static double BitsEntropy(const uint32_t* const array, int n, - uint32_t* const trivial_symbol) { +double VP8LBitsEntropy(const uint32_t* const array, int n, + uint32_t* const trivial_symbol) { double retval = 0.; uint32_t sum = 0; uint32_t nonzero_code = VP8L_NON_TRIVIAL_SYM; @@ -555,7 +555,7 @@ static double HuffmanCostCombined(const uint32_t* const X, double VP8LPopulationCost(const uint32_t* const population, int length, uint32_t* const trivial_sym) { return - BitsEntropy(population, length, trivial_sym) + + VP8LBitsEntropy(population, length, trivial_sym) + HuffmanCost(population, length); } @@ -579,12 +579,12 @@ double VP8LHistogramEstimateBits(const VP8LHistogram* const p) { double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) { return - BitsEntropy(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_), + VP8LBitsEntropy(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_), NULL) - + BitsEntropy(p->red_, NUM_LITERAL_CODES, NULL) - + BitsEntropy(p->blue_, NUM_LITERAL_CODES, NULL) - + BitsEntropy(p->alpha_, NUM_LITERAL_CODES, NULL) - + BitsEntropy(p->distance_, NUM_DISTANCE_CODES, NULL) + + VP8LBitsEntropy(p->red_, NUM_LITERAL_CODES, NULL) + + VP8LBitsEntropy(p->blue_, NUM_LITERAL_CODES, NULL) + + VP8LBitsEntropy(p->alpha_, NUM_LITERAL_CODES, NULL) + + VP8LBitsEntropy(p->distance_, NUM_DISTANCE_CODES, NULL) + VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES) + VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES); } diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index d57f9e8c..b7befb54 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -189,15 +189,6 @@ static int AnalyzeAndCreatePalette(const WebPPicture* const pic, 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. typedef enum { kDirect = 0, @@ -205,21 +196,52 @@ typedef enum { kSubGreen = 2, kSpatialSubGreen = 3, kPalette = 4, - kNumEntropyIx = 5, + kNumEntropyIx = 5 } 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, int width, int height, int argb_stride, int use_palette, EntropyIx* const min_entropy_ix, int* const red_and_blue_always_zero) { // Allocate histogram set with cache_bits = 0. - VP8LHistogramSet* const histo_set = VP8LAllocateHistogramSet(5, 0); - if (histo_set != NULL) { + uint32_t* const histo = + (uint32_t*)WebPSafeCalloc(kHistoTotal, sizeof(*histo) * 256); + if (histo != NULL) { int i, x, y; const uint32_t* prev_row = argb; const uint32_t* curr_row = argb + argb_stride; - VP8LHistogram* const * const histo = &histo_set->histograms[0]; for (y = 1; y < height; ++y) { uint32_t prev_pix = curr_row[0]; 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); if ((pix_diff == 0) || (pix == prev_row[x])) continue; prev_pix = pix; - { - const PixOrCopy pix_token = PixOrCopyCreateLiteral(pix); - const PixOrCopy pix_diff_token = PixOrCopyCreateLiteral(pix_diff); - VP8LHistogramAddSinglePixOrCopy(histo[kDirect], &pix_token); - VP8LHistogramAddSinglePixOrCopy(histo[kSpatial], &pix_diff_token); - AddSinglePixSubtractGreen(histo[kSubGreen], &pix_token); - AddSinglePixSubtractGreen(histo[kSpatialSubGreen], &pix_diff_token); - } + AddSingle(pix, + &histo[kHistoAlpha * 256], + &histo[kHistoRed * 256], + &histo[kHistoGreen * 256], + &histo[kHistoBlue * 256]); + AddSingle(pix_diff, + &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. const int hash = ((pix + (pix >> 19)) * 0x39c5fba7) >> 24; - ++histo[kPalette]->red_[hash & 0xff]; + ++histo[kHistoPalette * 256 + (hash & 0xff)]; } } prev_row = curr_row; curr_row += argb_stride; } { + double entropy_comp[kHistoTotal]; double entropy[kNumEntropyIx]; EntropyIx k; EntropyIx last_mode_to_analyze = 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; - for (k = kDirect; k <= last_mode_to_analyze; ++k) { - entropy[k] = VP8LHistogramEstimateBitsBulk(histo[k]); - if (k == kDirect || entropy[*min_entropy_ix] >= entropy[k]) { + for (k = kDirect + 1; k <= last_mode_to_analyze; ++k) { + if (entropy[*min_entropy_ix] >= entropy[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 // non-zero red and blue values. If all are zero, we can later skip // the cross color optimization. - for (i = 1; i < 256; ++i) { - if ((histo[*min_entropy_ix]->red_[i] | - histo[*min_entropy_ix]->blue_[i]) != 0) { - *red_and_blue_always_zero = 0; - break; + { + 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) { + if ((red_histo[i] | blue_histo[i]) != 0) { + *red_and_blue_always_zero = 0; + break; + } } } } - VP8LFreeHistogramSet(histo_set); + free(histo); return 1; } else { return 0;