Refactor VP8LHistogram to hide initializations from the user.

This will make it easier to update some future statistics

Change-Id: I3a3ec64d3c9c53ebcf491007e3a4d916e122c87f
This commit is contained in:
Vincent Rabaud 2025-04-11 16:16:37 +02:00
parent 00338240c1
commit 5225592f6b
4 changed files with 69 additions and 57 deletions

View File

@ -64,18 +64,13 @@ static void ConvertPopulationCountTableToBitEstimates(
static int CostModelBuild(CostModel* const m, int xsize, int cache_bits,
const VP8LBackwardRefs* const refs) {
int ok = 0;
VP8LRefsCursor c = VP8LRefsCursorInit(refs);
VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits);
if (histo == NULL) goto Error;
// The following code is similar to VP8LHistogramCreate but converts the
// distance to plane code.
VP8LHistogramInit(histo, cache_bits, /*init_arrays=*/ 1);
while (VP8LRefsCursorOk(&c)) {
VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos, VP8LDistanceToPlaneCode,
xsize);
VP8LRefsCursorNext(&c);
}
VP8LHistogramStoreRefs(refs, VP8LDistanceToPlaneCode, xsize, histo);
ConvertPopulationCountTableToBitEstimates(
VP8LHistogramNumCodes(histo->palette_code_bits), histo->literal,

View File

@ -79,15 +79,6 @@ void VP8LFreeHistogramSet(VP8LHistogramSet* const histo) {
WebPSafeFree(histo);
}
void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
VP8LHistogram* const histo) {
VP8LRefsCursor c = VP8LRefsCursorInit(refs);
while (VP8LRefsCursorOk(&c)) {
VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos, NULL, 0);
VP8LRefsCursorNext(&c);
}
}
void VP8LHistogramCreate(VP8LHistogram* const p,
const VP8LBackwardRefs* const refs,
int palette_code_bits) {
@ -95,7 +86,8 @@ void VP8LHistogramCreate(VP8LHistogram* const p,
p->palette_code_bits = palette_code_bits;
}
HistogramClear(p);
VP8LHistogramStoreRefs(refs, p);
VP8LHistogramStoreRefs(refs, /*distance_modifier=*/NULL,
/*distance_modifier_arg0=*/0, p);
}
void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits,
@ -201,10 +193,9 @@ static void HistogramSetRemoveHistogram(VP8LHistogramSet* const set, int i,
// -----------------------------------------------------------------------------
void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
const PixOrCopy* const v,
int (*const distance_modifier)(int, int),
int distance_modifier_arg0) {
static void HistogramAddSinglePixOrCopy(
VP8LHistogram* const histo, const PixOrCopy* const v,
int (*const distance_modifier)(int, int), int distance_modifier_arg0) {
if (PixOrCopyIsLiteral(v)) {
++histo->alpha[PixOrCopyLiteral(v, 3)];
++histo->red[PixOrCopyLiteral(v, 2)];
@ -230,6 +221,18 @@ void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
}
}
void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
int (*const distance_modifier)(int, int),
int distance_modifier_arg0,
VP8LHistogram* const histo) {
VP8LRefsCursor c = VP8LRefsCursorInit(refs);
while (VP8LRefsCursorOk(&c)) {
HistogramAddSinglePixOrCopy(histo, c.cur_pos, distance_modifier,
distance_modifier_arg0);
VP8LRefsCursorNext(&c);
}
}
// -----------------------------------------------------------------------------
// Entropy-related functions.
@ -324,12 +327,37 @@ static uint64_t PopulationCost(const uint32_t* const population, int length,
// trivial_at_end is 1 if the two histograms only have one element that is
// non-zero: both the zero-th one, or both the last one.
static WEBP_INLINE uint64_t GetCombinedEntropy(const uint32_t* const X,
const uint32_t* const Y,
int length, int is_X_used,
int is_Y_used,
int trivial_at_end) {
// 'index' is the index of the symbol in the histogram (literal, red, blue,
// alpha, distance).
static WEBP_INLINE uint64_t GetCombinedEntropy(
const VP8LHistogram* const histo_X, const VP8LHistogram* const histo_Y,
int index, int trivial_at_end) {
const uint32_t* X;
const uint32_t* Y;
int length;
VP8LStreaks stats;
if (index == 0) {
X = histo_X->literal;
Y = histo_Y->literal;
length = VP8LHistogramNumCodes(histo_X->palette_code_bits);
} else if (index == 1) {
X = histo_X->red;
Y = histo_Y->red;
length = NUM_LITERAL_CODES;
} else if (index == 2) {
X = histo_X->blue;
Y = histo_Y->blue;
length = NUM_LITERAL_CODES;
} else if (index == 3) {
X = histo_X->alpha;
Y = histo_Y->alpha;
length = NUM_LITERAL_CODES;
} else {
assert(index == 4);
X = histo_X->distance;
Y = histo_Y->distance;
length = NUM_DISTANCE_CODES;
}
if (trivial_at_end) {
// This configuration is due to palettization that transforms an indexed
// pixel into 0xff000000 | (pixel << 8) in VP8LBundleColorMap.
@ -343,6 +371,8 @@ static WEBP_INLINE uint64_t GetCombinedEntropy(const uint32_t* const X,
stats.streaks[0][1] = length - 1;
return FinalHuffmanCost(&stats);
} else {
const int is_X_used = histo_X->is_used[index];
const int is_Y_used = histo_Y->is_used[index];
VP8LBitEntropy bit_entropy;
if (is_X_used) {
if (is_Y_used) {
@ -396,16 +426,13 @@ static WEBP_INLINE void SaturateAdd(uint64_t a, int64_t* b) {
WEBP_NODISCARD static int GetCombinedHistogramEntropy(
const VP8LHistogram* const a, const VP8LHistogram* const b,
int64_t cost_threshold_in, uint64_t* cost) {
const int palette_code_bits = a->palette_code_bits;
int trivial_at_end = 0;
int trivial_at_end = 0, i;
const uint64_t cost_threshold = (uint64_t)cost_threshold_in;
assert(a->palette_code_bits == b->palette_code_bits);
if (cost_threshold_in <= 0) return 0;
*cost = GetCombinedEntropy(a->literal, b->literal,
VP8LHistogramNumCodes(palette_code_bits),
a->is_used[0], b->is_used[0], 0);
// No need to add the extra cost as it is a constant that does not influence
// the histograms.
*cost = GetCombinedEntropy(a, b, /*index=*/0, /*trivial_at_end=*/0);
// No need to add the extra cost for lengths as it is a constant that does not
// influence the histograms.
if (*cost >= cost_threshold) return 0;
if (a->trivial_symbol != VP8L_NON_TRIVIAL_SYM &&
@ -421,23 +448,13 @@ WEBP_NODISCARD static int GetCombinedHistogramEntropy(
}
}
*cost += GetCombinedEntropy(a->red, b->red, NUM_LITERAL_CODES, a->is_used[1],
b->is_used[1], trivial_at_end);
if (*cost >= cost_threshold) return 0;
*cost += GetCombinedEntropy(a->blue, b->blue, NUM_LITERAL_CODES,
a->is_used[2], b->is_used[2], trivial_at_end);
if (*cost >= cost_threshold) return 0;
*cost += GetCombinedEntropy(a->alpha, b->alpha, NUM_LITERAL_CODES,
a->is_used[3], b->is_used[3], trivial_at_end);
if (*cost >= cost_threshold) return 0;
*cost += GetCombinedEntropy(a->distance, b->distance, NUM_DISTANCE_CODES,
a->is_used[4], b->is_used[4], 0);
// No need to add the extra cost as it is a constant that does not influence
// the histograms.
for (i = 1; i <= 4; ++i) {
*cost += GetCombinedEntropy(a, b, i,
/*trivial_at_end=*/i <= 3 ? trivial_at_end : 0);
if (*cost >= cost_threshold) return 0;
}
// No need to add the extra cost for distances as it is a constant that does
// not influence the histograms.
return 1;
}
@ -586,7 +603,7 @@ static void HistogramBuild(
while (VP8LRefsCursorOk(&c)) {
const PixOrCopy* const v = c.cur_pos;
const int ix = (y >> histo_bits) * histo_xsize + (x >> histo_bits);
VP8LHistogramAddSinglePixOrCopy(histograms[ix], v, NULL, 0);
HistogramAddSinglePixOrCopy(histograms[ix], v, NULL, 0);
x += PixOrCopyLength(v);
while (x >= xsize) {
x -= xsize;

View File

@ -70,7 +70,11 @@ void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits,
int init_arrays);
// Collect all the references into a histogram (without reset)
// The distance modifier function is applied to the distance before
// the histogram is updated. It can be NULL.
void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
int (*const distance_modifier)(int, int),
int distance_modifier_arg0,
VP8LHistogram* const histo);
// Free the memory allocated for the histogram.
@ -91,12 +95,6 @@ void VP8LHistogramSetClear(VP8LHistogramSet* const set);
// Special case of VP8LAllocateHistogramSet, with size equals 1.
VP8LHistogram* VP8LAllocateHistogram(int cache_bits);
// Accumulate a token 'v' into a histogram.
void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
const PixOrCopy* const v,
int (*const distance_modifier)(int, int),
int distance_modifier_arg0);
static WEBP_INLINE int VP8LHistogramNumCodes(int palette_code_bits) {
return NUM_LITERAL_CODES + NUM_LENGTH_CODES +
((palette_code_bits > 0) ? (1 << palette_code_bits) : 0);

View File

@ -797,7 +797,9 @@ static int EncodeImageNoHuffman(VP8LBitWriter* const bw,
VP8LHistogramSetClear(histogram_image);
// Build histogram image and symbols from backward references.
VP8LHistogramStoreRefs(refs, histogram_image->histograms[0]);
VP8LHistogramStoreRefs(refs, /*distance_modifier=*/NULL,
/*distance_modifier_arg0=*/0,
histogram_image->histograms[0]);
// Create Huffman bit lengths and codes for each histogram image.
assert(histogram_image->size == 1);