mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-27 06:08:21 +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:
parent
d92453f381
commit
85b44d8a69
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
126
src/enc/vp8l.c
126
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;
|
||||
|
Loading…
Reference in New Issue
Block a user