Refactor VP8LHistogram histogram_enc.cc

- move HistogramAdd to histogram_enc.cc: it is too high level
- homogenize the argument naming (e.g. h for histogram, p for
population)
- separate a bit the data from the stats (only used within
VP8LGetHistoImageSymbols)

Change-Id: I274546e3ff96297383bcae0a95696c11f18decbf
This commit is contained in:
Vincent Rabaud 2025-04-23 19:12:21 +02:00
parent 7191a602b0
commit 57e324e2eb
7 changed files with 134 additions and 138 deletions

View File

@ -15,12 +15,8 @@
#ifndef WEBP_DSP_LOSSLESS_H_
#define WEBP_DSP_LOSSLESS_H_
#include "src/webp/types.h"
#include "src/webp/decode.h"
#include "src/dsp/dsp.h"
#include "src/enc/histogram_enc.h"
#include "src/utils/utils.h"
#include "src/webp/decode.h"
#ifdef __cplusplus
extern "C" {
@ -245,9 +241,6 @@ extern VP8LAddVectorFunc VP8LAddVector;
typedef void (*VP8LAddVectorEqFunc)(const uint32_t* WEBP_RESTRICT a,
uint32_t* WEBP_RESTRICT out, int size);
extern VP8LAddVectorEqFunc VP8LAddVectorEq;
void VP8LHistogramAdd(const VP8LHistogram* WEBP_RESTRICT const a,
const VP8LHistogram* WEBP_RESTRICT const b,
VP8LHistogram* WEBP_RESTRICT const out);
// -----------------------------------------------------------------------------
// PrefixEncode()

View File

@ -598,58 +598,6 @@ static void AddVectorEq_C(const uint32_t* WEBP_RESTRICT a,
for (i = 0; i < size; ++i) out[i] += a[i];
}
#define ADD(X, ARG, LEN) do { \
if (a->is_used[X]) { \
if (b->is_used[X]) { \
VP8LAddVector(a->ARG, b->ARG, out->ARG, (LEN)); \
} else { \
memcpy(&out->ARG[0], &a->ARG[0], (LEN) * sizeof(out->ARG[0])); \
} \
} else if (b->is_used[X]) { \
memcpy(&out->ARG[0], &b->ARG[0], (LEN) * sizeof(out->ARG[0])); \
} else { \
memset(&out->ARG[0], 0, (LEN) * sizeof(out->ARG[0])); \
} \
} while (0)
#define ADD_EQ(X, ARG, LEN) do { \
if (a->is_used[X]) { \
if (out->is_used[X]) { \
VP8LAddVectorEq(a->ARG, out->ARG, (LEN)); \
} else { \
memcpy(&out->ARG[0], &a->ARG[0], (LEN) * sizeof(out->ARG[0])); \
} \
} \
} while (0)
void VP8LHistogramAdd(const VP8LHistogram* WEBP_RESTRICT const a,
const VP8LHistogram* WEBP_RESTRICT const b,
VP8LHistogram* WEBP_RESTRICT const out) {
int i;
const int literal_size = VP8LHistogramNumCodes(a->palette_code_bits);
assert(a->palette_code_bits == b->palette_code_bits);
if (b != out) {
ADD(0, literal, literal_size);
ADD(1, red, NUM_LITERAL_CODES);
ADD(2, blue, NUM_LITERAL_CODES);
ADD(3, alpha, NUM_LITERAL_CODES);
ADD(4, distance, NUM_DISTANCE_CODES);
for (i = 0; i < 5; ++i) {
out->is_used[i] = (a->is_used[i] | b->is_used[i]);
}
} else {
ADD_EQ(0, literal, literal_size);
ADD_EQ(1, red, NUM_LITERAL_CODES);
ADD_EQ(2, blue, NUM_LITERAL_CODES);
ADD_EQ(3, alpha, NUM_LITERAL_CODES);
ADD_EQ(4, distance, NUM_DISTANCE_CODES);
for (i = 0; i < 5; ++i) out->is_used[i] |= a->is_used[i];
}
}
#undef ADD
#undef ADD_EQ
//------------------------------------------------------------------------------
// Image transforms.

View File

@ -23,6 +23,7 @@
#include "src/dsp/lossless.h"
#include "src/dsp/lossless_common.h"
#include "src/utils/utils.h"
#include "src/webp/format_constants.h"
#include "src/webp/types.h"
// For sign-extended multiplying constants, pre-shifted by 5:

View File

@ -19,6 +19,7 @@
#include "src/dsp/lossless.h"
#include "src/dsp/neon.h"
#include "src/webp/format_constants.h"
//------------------------------------------------------------------------------
// Colorspace conversion functions

View File

@ -22,6 +22,7 @@
#include "src/dsp/cpu.h"
#include "src/dsp/lossless.h"
#include "src/dsp/lossless_common.h"
#include "src/webp/format_constants.h"
#include "src/webp/types.h"
//------------------------------------------------------------------------------

View File

@ -52,20 +52,32 @@ static int GetHistogramSize(int cache_bits) {
return (int)total_size;
}
static void HistogramClear(VP8LHistogram* const p) {
uint32_t* const literal = p->literal;
const int cache_bits = p->palette_code_bits;
static void HistogramStatsClear(VP8LHistogram* const h) {
int i;
for (i = 0; i < 5; ++i) {
h->trivial_symbol[i] = VP8L_NON_TRIVIAL_SYM;
// By default, the histogram is assumed to be used.
h->is_used[i] = 1;
}
h->bit_cost = 0;
memset(h->costs, 0, sizeof(h->costs));
}
static void HistogramClear(VP8LHistogram* const h) {
uint32_t* const literal = h->literal;
const int cache_bits = h->palette_code_bits;
const int histo_size = GetHistogramSize(cache_bits);
memset(p, 0, histo_size);
p->palette_code_bits = cache_bits;
p->literal = literal;
memset(h, 0, histo_size);
h->palette_code_bits = cache_bits;
h->literal = literal;
HistogramStatsClear(h);
}
// Swap two histogram pointers.
static void HistogramSwap(VP8LHistogram** const A, VP8LHistogram** const B) {
VP8LHistogram* const tmp = *A;
*A = *B;
*B = tmp;
static void HistogramSwap(VP8LHistogram** const h1, VP8LHistogram** const h2) {
VP8LHistogram* const tmp = *h1;
*h1 = *h2;
*h2 = tmp;
}
static void HistogramCopy(const VP8LHistogram* const src,
@ -80,36 +92,30 @@ static void HistogramCopy(const VP8LHistogram* const src,
memcpy(dst->literal, src->literal, literal_size * sizeof(*dst->literal));
}
void VP8LFreeHistogram(VP8LHistogram* const histo) {
WebPSafeFree(histo);
void VP8LFreeHistogram(VP8LHistogram* const h) { WebPSafeFree(h); }
void VP8LFreeHistogramSet(VP8LHistogramSet* const histograms) {
WebPSafeFree(histograms);
}
void VP8LFreeHistogramSet(VP8LHistogramSet* const histo) {
WebPSafeFree(histo);
}
void VP8LHistogramCreate(VP8LHistogram* const p,
void VP8LHistogramCreate(VP8LHistogram* const h,
const VP8LBackwardRefs* const refs,
int palette_code_bits) {
if (palette_code_bits >= 0) {
p->palette_code_bits = palette_code_bits;
h->palette_code_bits = palette_code_bits;
}
HistogramClear(p);
HistogramClear(h);
VP8LHistogramStoreRefs(refs, /*distance_modifier=*/NULL,
/*distance_modifier_arg0=*/0, p);
/*distance_modifier_arg0=*/0, h);
}
void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits,
void VP8LHistogramInit(VP8LHistogram* const h, int palette_code_bits,
int init_arrays) {
p->palette_code_bits = palette_code_bits;
h->palette_code_bits = palette_code_bits;
if (init_arrays) {
HistogramClear(p);
HistogramClear(h);
} else {
int i;
for (i = 0; i < 5; ++i) p->trivial_symbol[i] = VP8L_NON_TRIVIAL_SYM;
p->bit_cost = 0;
memset(p->costs, 0, sizeof(p->costs));
memset(p->is_used, 0, sizeof(p->is_used));
HistogramStatsClear(h);
}
}
@ -327,8 +333,10 @@ static uint64_t PopulationCost(const uint32_t* const population, int length,
*trivial_sym = (bit_entropy.nonzeros == 1) ? bit_entropy.nonzero_code
: VP8L_NON_TRIVIAL_SYM;
}
if (is_used != NULL) {
// The histogram is used if there is at least one non-zero streak.
*is_used = (stats.streaks[1][0] != 0 || stats.streaks[1][1] != 0);
}
return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats);
}
@ -365,45 +373,47 @@ static WEBP_INLINE void GetPopulationInfo(const VP8LHistogram* const histo,
// non-zero: both the zero-th one, or both the last one.
// '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, HistogramIndex index) {
static WEBP_INLINE uint64_t GetCombinedEntropy(const VP8LHistogram* const h1,
const VP8LHistogram* const h2,
HistogramIndex index) {
const uint32_t* X;
const uint32_t* Y;
int length;
VP8LStreaks stats;
VP8LBitEntropy bit_entropy;
const int is_X_used = histo_X->is_used[index];
const int is_Y_used = histo_Y->is_used[index];
const int is_trivial =
histo_X->trivial_symbol[index] != VP8L_NON_TRIVIAL_SYM &&
histo_X->trivial_symbol[index] == histo_Y->trivial_symbol[index];
const int is_h1_used = h1->is_used[index];
const int is_h2_used = h2->is_used[index];
const int is_trivial = h1->trivial_symbol[index] != VP8L_NON_TRIVIAL_SYM &&
h1->trivial_symbol[index] == h2->trivial_symbol[index];
if (is_trivial || !is_X_used || !is_Y_used) {
if (is_X_used) return histo_X->costs[index];
return histo_Y->costs[index];
if (is_trivial || !is_h1_used || !is_h2_used) {
if (is_h1_used) return h1->costs[index];
return h2->costs[index];
}
assert(is_X_used && is_Y_used);
assert(is_h1_used && is_h2_used);
GetPopulationInfo(histo_X, index, &X, &length);
GetPopulationInfo(histo_Y, index, &Y, &length);
GetPopulationInfo(h1, index, &X, &length);
GetPopulationInfo(h2, index, &Y, &length);
VP8LGetCombinedEntropyUnrefined(X, Y, length, &bit_entropy, &stats);
return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats);
}
// Estimates the Entropy + Huffman + other block overhead size cost.
uint64_t VP8LHistogramEstimateBits(VP8LHistogram* const p) {
return PopulationCost(p->literal, VP8LHistogramNumCodes(p->palette_code_bits),
NULL, &p->is_used[LITERAL]) +
PopulationCost(p->red, NUM_LITERAL_CODES, NULL, &p->is_used[RED]) +
PopulationCost(p->blue, NUM_LITERAL_CODES, NULL, &p->is_used[BLUE]) +
PopulationCost(p->alpha, NUM_LITERAL_CODES, NULL, &p->is_used[ALPHA]) +
PopulationCost(p->distance, NUM_DISTANCE_CODES, NULL,
&p->is_used[DISTANCE]) +
((uint64_t)(VP8LExtraCost(p->literal + NUM_LITERAL_CODES,
uint64_t VP8LHistogramEstimateBits(const VP8LHistogram* const h) {
int i;
uint64_t cost = 0;
for (i = 0; i < 5; ++i) {
int length;
const uint32_t* population;
GetPopulationInfo(h, (HistogramIndex)i, &population, &length);
cost += PopulationCost(population, length, /*trivial_sym=*/NULL,
/*is_used=*/NULL);
}
cost += ((uint64_t)(VP8LExtraCost(h->literal + NUM_LITERAL_CODES,
NUM_LENGTH_CODES) +
VP8LExtraCost(p->distance, NUM_DISTANCE_CODES))
VP8LExtraCost(h->distance, NUM_DISTANCE_CODES))
<< LOG_2_PRECISION_BITS);
return cost;
}
// -----------------------------------------------------------------------------
@ -440,15 +450,57 @@ WEBP_NODISCARD static int GetCombinedHistogramEntropy(
return 1;
}
static WEBP_INLINE void HistogramAdd(const VP8LHistogram* const a,
const VP8LHistogram* const b,
VP8LHistogram* const out) {
static WEBP_INLINE void HistogramAdd(const VP8LHistogram* const h1,
const VP8LHistogram* const h2,
VP8LHistogram* const hout) {
int i;
VP8LHistogramAdd(a, b, out);
assert(h1->palette_code_bits == h2->palette_code_bits);
for (i = 0; i < 5; ++i) {
out->trivial_symbol[i] = a->trivial_symbol[i] == b->trivial_symbol[i]
? a->trivial_symbol[i]
int length;
const uint32_t *p1, *p2, *pout_const;
uint32_t* pout;
GetPopulationInfo(h1, (HistogramIndex)i, &p1, &length);
GetPopulationInfo(h2, (HistogramIndex)i, &p2, &length);
GetPopulationInfo(hout, (HistogramIndex)i, &pout_const, &length);
pout = (uint32_t*)pout_const;
if (h2 == hout) {
if (h1->is_used[i]) {
if (hout->is_used[i]) {
VP8LAddVectorEq(p1, pout, length);
} else {
memcpy(pout, p1, length * sizeof(pout[0]));
}
}
} else {
if (h1->is_used[i]) {
if (h2->is_used[i]) {
VP8LAddVector(p1, p2, pout, length);
} else {
memcpy(pout, p1, length * sizeof(pout[0]));
}
} else if (h2->is_used[i]) {
memcpy(pout, p2, length * sizeof(pout[0]));
} else {
memset(pout, 0, length * sizeof(pout[0]));
}
}
}
for (i = 0; i < 5; ++i) {
hout->trivial_symbol[i] = h1->trivial_symbol[i] == h2->trivial_symbol[i]
? h1->trivial_symbol[i]
: VP8L_NON_TRIVIAL_SYM;
hout->is_used[i] = h1->is_used[i] || h2->is_used[i];
}
}
static void UpdateHistogramCost(uint64_t bit_cost, uint64_t costs[5],
VP8LHistogram* const h) {
int i;
h->bit_cost = bit_cost;
for (i = 0; i < 5; ++i) {
h->costs[i] = costs[i];
}
}
@ -465,14 +517,14 @@ WEBP_NODISCARD static int HistogramAddEval(const VP8LHistogram* const a,
VP8LHistogram* const out,
int64_t cost_threshold) {
const uint64_t sum_cost = a->bit_cost + b->bit_cost;
uint64_t bit_cost, costs[5];
SaturateAdd(sum_cost, &cost_threshold);
if (!GetCombinedHistogramEntropy(a, b, cost_threshold, &out->bit_cost,
out->costs)) {
if (!GetCombinedHistogramEntropy(a, b, cost_threshold, &bit_cost, costs)) {
return 0;
}
HistogramAdd(a, b, out);
out->palette_code_bits = a->palette_code_bits;
UpdateHistogramCost(bit_cost, costs, out);
return 1;
}
@ -528,7 +580,7 @@ static void UpdateDominantCostRange(
if (c->blue_min > h->costs[BLUE]) c->blue_min = h->costs[BLUE];
}
static void UpdateHistogramCost(VP8LHistogram* const h) {
static void ComputeHistogramCost(VP8LHistogram* const h) {
int i;
// No need to add the extra cost for length and distance as it is a constant
// that does not influence the histograms.
@ -602,7 +654,7 @@ static void HistogramCopyAndAnalyze(VP8LHistogramSet* const orig_histo,
image_histo->size = 0;
for (i = 0; i < orig_histo->max_size; ++i) {
VP8LHistogram* const histo = orig_histograms[i];
UpdateHistogramCost(histo);
ComputeHistogramCost(histo);
// Skip the histogram if it is completely empty, which can happen for tiles
// with no information (when they are skipped because of LZ77).
@ -716,7 +768,7 @@ static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
// for low_effort case, update the final cost when everything is merged
for (idx = 0; idx < image_histo->size; ++idx) {
if (histograms[idx] == NULL) continue;
UpdateHistogramCost(histograms[idx]);
ComputeHistogramCost(histograms[idx]);
}
}
}
@ -877,9 +929,8 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo,
const int idx1 = histo_queue.queue[0].idx1;
const int idx2 = histo_queue.queue[0].idx2;
HistogramAdd(histograms[idx2], histograms[idx1], histograms[idx1]);
histograms[idx1]->bit_cost = histo_queue.queue[0].cost_combo;
memcpy(histograms[idx1]->costs, histo_queue.queue[0].costs,
sizeof(histograms[idx1]->costs));
UpdateHistogramCost(histo_queue.queue[0].cost_combo,
histo_queue.queue[0].costs, histograms[idx1]);
// Remove merged histogram.
HistogramSetRemoveHistogram(image_histo, idx2, num_used);
@ -1000,9 +1051,8 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
// Merge the histograms and remove best_idx2 from the queue.
HistogramAdd(histograms[best_idx2], histograms[best_idx1],
histograms[best_idx1]);
histograms[best_idx1]->bit_cost = histo_queue.queue[0].cost_combo;
memcpy(histograms[best_idx1]->costs, histo_queue.queue[0].costs,
sizeof(histograms[best_idx1]->costs));
UpdateHistogramCost(histo_queue.queue[0].cost_combo,
histo_queue.queue[0].costs, histograms[best_idx1]);
HistogramSetRemoveHistogram(image_histo, best_idx2, num_used);
// Parse the queue and update each pair that deals with best_idx1,
// best_idx2 or image_histo_size.

View File

@ -37,6 +37,8 @@ typedef struct {
// Backward reference prefix-code histogram.
uint32_t distance[NUM_DISTANCE_CODES];
int palette_code_bits;
// The following members are only used within VP8LGetHistoImageSymbols.
// Index of the unique value of a histogram if any, VP8L_NON_TRIVIAL_SYM
// otherwise.
uint16_t trivial_symbol[5];
@ -60,13 +62,13 @@ typedef struct {
// The input data is the PixOrCopy data, which models the literals, stop
// codes and backward references (both distances and lengths). Also: if
// palette_code_bits is >= 0, initialize the histogram with this value.
void VP8LHistogramCreate(VP8LHistogram* const p,
void VP8LHistogramCreate(VP8LHistogram* const h,
const VP8LBackwardRefs* const refs,
int palette_code_bits);
// Set the palette_code_bits and reset the stats.
// If init_arrays is true, the arrays are also filled with 0's.
void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits,
void VP8LHistogramInit(VP8LHistogram* const h, int palette_code_bits,
int init_arrays);
// Collect all the references into a histogram (without reset)
@ -116,7 +118,7 @@ uint64_t VP8LBitsEntropy(const uint32_t* const array, int n);
// Estimate how many bits the combined entropy of literals and distance
// approximately maps to.
uint64_t VP8LHistogramEstimateBits(VP8LHistogram* const p);
uint64_t VP8LHistogramEstimateBits(const VP8LHistogram* const h);
#ifdef __cplusplus
}