mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-27 06:08:21 +01:00
Compute the hash chain once and for all for lossless compression.
In some cases, the hash chain for a function is filled several times: - GetBackwardReferences -> CalculateBestCacheSize -> BackwardReferencesLz77 that computes the hash chain - GetBackwardReferences -> (not always) BackwardReferencesTraceBackwards -> BackwardReferencesHashChainDistanceOnly that computes the hash chain in a slightly different way Speed and compression performance are slightly changed (+ or -) but will be homogneized in a later patch. Change-Id: I43f0ecc7a9312c2ed6cdba1c0fabc6c5ad91c953
This commit is contained in:
parent
eee788e26a
commit
d963775859
@ -181,23 +181,14 @@ int VP8LBackwardRefsCopy(const VP8LBackwardRefs* const src,
|
||||
// -----------------------------------------------------------------------------
|
||||
// Hash chains
|
||||
|
||||
// initialize as empty
|
||||
static void HashChainReset(VP8LHashChain* const p) {
|
||||
assert(p != NULL);
|
||||
// Set the int32_t arrays to -1.
|
||||
memset(p->chain_, 0xff, p->size_ * sizeof(*p->chain_));
|
||||
memset(p->hash_to_first_index_, 0xff,
|
||||
HASH_SIZE * sizeof(*p->hash_to_first_index_));
|
||||
}
|
||||
|
||||
int VP8LHashChainInit(VP8LHashChain* const p, int size) {
|
||||
assert(p->size_ == 0);
|
||||
assert(p->chain_ == NULL);
|
||||
assert(size > 0);
|
||||
p->chain_ = (int*)WebPSafeMalloc(size, sizeof(*p->chain_));
|
||||
p->chain_ = (int32_t*)WebPSafeMalloc(size, sizeof(*p->chain_));
|
||||
if (p->chain_ == NULL) return 0;
|
||||
p->size_ = size;
|
||||
HashChainReset(p);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -221,14 +212,6 @@ static WEBP_INLINE uint32_t GetPixPairHash64(const uint32_t* const argb) {
|
||||
return key;
|
||||
}
|
||||
|
||||
// Insertion of two pixels at a time.
|
||||
static void HashChainInsert(VP8LHashChain* const p,
|
||||
const uint32_t* const argb, int pos) {
|
||||
const uint32_t hash_code = GetPixPairHash64(argb);
|
||||
p->chain_[pos] = p->hash_to_first_index_[hash_code];
|
||||
p->hash_to_first_index_[hash_code] = pos;
|
||||
}
|
||||
|
||||
// Returns the maximum number of hash chain lookups to do for a
|
||||
// given compression quality. Return value in range [6, 86].
|
||||
static int GetMaxItersForQuality(int quality, int low_effort) {
|
||||
@ -248,6 +231,31 @@ static WEBP_INLINE int MaxFindCopyLength(int len) {
|
||||
return (len < MAX_LENGTH) ? len : MAX_LENGTH;
|
||||
}
|
||||
|
||||
int VP8LHashChainFill(VP8LHashChain* const p,
|
||||
const uint32_t* const argb, int xsize, int ysize) {
|
||||
const int size = xsize * ysize;
|
||||
int pos;
|
||||
int32_t* hash_to_first_index;
|
||||
assert(p->size_ != 0);
|
||||
assert(p->chain_ != NULL);
|
||||
|
||||
hash_to_first_index =
|
||||
(int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index));
|
||||
if (hash_to_first_index == NULL) return 0;
|
||||
|
||||
// Set the int32_t array to -1.
|
||||
memset(hash_to_first_index, 0xff, HASH_SIZE * sizeof(*hash_to_first_index));
|
||||
// Fill the chain linking pixels with the same hash.
|
||||
for (pos = 0; pos < size - 1; ++pos) {
|
||||
const uint32_t hash_code = GetPixPairHash64(argb + pos);
|
||||
p->chain_[pos] = hash_to_first_index[hash_code];
|
||||
hash_to_first_index[hash_code] = pos;
|
||||
}
|
||||
WebPSafeFree(hash_to_first_index);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void HashChainFindOffset(const VP8LHashChain* const p, int base_position,
|
||||
const uint32_t* const argb, int len,
|
||||
int window_size, int* const distance_ptr) {
|
||||
@ -256,7 +264,7 @@ static void HashChainFindOffset(const VP8LHashChain* const p, int base_position,
|
||||
(base_position > window_size) ? base_position - window_size : 0;
|
||||
int pos;
|
||||
assert(len <= MAX_LENGTH);
|
||||
for (pos = p->hash_to_first_index_[GetPixPairHash64(argb_start)];
|
||||
for (pos = p->chain_[base_position];
|
||||
pos >= min_pos;
|
||||
pos = p->chain_[pos]) {
|
||||
const int curr_length =
|
||||
@ -283,7 +291,7 @@ static int HashChainFindCopy(const VP8LHashChain* const p,
|
||||
if (max_len < length_max) {
|
||||
length_max = max_len;
|
||||
}
|
||||
for (pos = p->hash_to_first_index_[GetPixPairHash64(argb_start)];
|
||||
for (pos = p->chain_[base_position];
|
||||
pos >= min_pos;
|
||||
pos = p->chain_[pos]) {
|
||||
int curr_length;
|
||||
@ -372,7 +380,7 @@ static int BackwardReferencesRle(int xsize, int ysize,
|
||||
static int BackwardReferencesLz77(int xsize, int ysize,
|
||||
const uint32_t* const argb, int cache_bits,
|
||||
int quality, int low_effort,
|
||||
VP8LHashChain* const hash_chain,
|
||||
const VP8LHashChain* const hash_chain,
|
||||
VP8LBackwardRefs* const refs) {
|
||||
int i;
|
||||
int ok = 0;
|
||||
@ -389,7 +397,6 @@ static int BackwardReferencesLz77(int xsize, int ysize,
|
||||
if (!cc_init) goto Error;
|
||||
}
|
||||
ClearBackwardRefs(refs);
|
||||
HashChainReset(hash_chain);
|
||||
for (i = 0; i < pix_count - 2; ) {
|
||||
// Alternative#1: Code the pixels starting at 'i' using backward reference.
|
||||
int offset = 0;
|
||||
@ -402,7 +409,6 @@ static int BackwardReferencesLz77(int xsize, int ysize,
|
||||
int len2 = 0;
|
||||
int k;
|
||||
min_matches = 8;
|
||||
HashChainInsert(hash_chain, &argb[i], i);
|
||||
if ((len < (max_len >> 2)) && !low_effort) {
|
||||
// Evaluate Alternative#2: Insert the pixel at 'i' as literal, and code
|
||||
// the pixels starting at 'i + 1' using backward reference.
|
||||
@ -422,25 +428,13 @@ static int BackwardReferencesLz77(int xsize, int ysize,
|
||||
VP8LColorCacheInsert(&hashers, argb[i + k]);
|
||||
}
|
||||
}
|
||||
// Add to the hash_chain (but cannot add the last pixel).
|
||||
if (offset >= 3 && offset != xsize) {
|
||||
const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
|
||||
for (k = 2; k < last - 8; k += 2) {
|
||||
HashChainInsert(hash_chain, &argb[i + k], i + k);
|
||||
}
|
||||
for (; k < last; ++k) {
|
||||
HashChainInsert(hash_chain, &argb[i + k], i + k);
|
||||
}
|
||||
}
|
||||
i += len;
|
||||
} else {
|
||||
AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
|
||||
HashChainInsert(hash_chain, &argb[i], i);
|
||||
++i;
|
||||
--min_matches;
|
||||
if (min_matches <= 0) {
|
||||
AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
|
||||
HashChainInsert(hash_chain, &argb[i], i);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
@ -469,7 +463,7 @@ typedef struct {
|
||||
|
||||
static int BackwardReferencesTraceBackwards(
|
||||
int xsize, int ysize, const uint32_t* const argb, int quality,
|
||||
int cache_bits, VP8LHashChain* const hash_chain,
|
||||
int cache_bits, const VP8LHashChain* const hash_chain,
|
||||
VP8LBackwardRefs* const refs);
|
||||
|
||||
static void ConvertPopulationCountTableToBitEstimates(
|
||||
@ -545,16 +539,14 @@ static WEBP_INLINE double GetDistanceCost(const CostModel* const m,
|
||||
return m->distance_[code] + extra_bits;
|
||||
}
|
||||
|
||||
static void AddSingleLiteralWithCostModel(
|
||||
const uint32_t* const argb, VP8LHashChain* const hash_chain,
|
||||
VP8LColorCache* const hashers, const CostModel* const cost_model, int idx,
|
||||
int is_last, int use_color_cache, double prev_cost, float* const cost,
|
||||
uint16_t* const dist_array) {
|
||||
static void AddSingleLiteralWithCostModel(const uint32_t* const argb,
|
||||
VP8LColorCache* const hashers,
|
||||
const CostModel* const cost_model,
|
||||
int idx, int use_color_cache,
|
||||
double prev_cost, float* const cost,
|
||||
uint16_t* const dist_array) {
|
||||
double cost_val = prev_cost;
|
||||
const uint32_t color = argb[0];
|
||||
if (!is_last) {
|
||||
HashChainInsert(hash_chain, argb, idx);
|
||||
}
|
||||
if (use_color_cache && VP8LColorCacheContains(hashers, color)) {
|
||||
const double mul0 = 0.68;
|
||||
const int ix = VP8LColorCacheGetIndex(hashers, color);
|
||||
@ -1022,8 +1014,8 @@ static WEBP_INLINE void PushInterval(CostManager* const manager,
|
||||
}
|
||||
|
||||
static int BackwardReferencesHashChainDistanceOnly(
|
||||
int xsize, int ysize, const uint32_t* const argb,
|
||||
int quality, int cache_bits, VP8LHashChain* const hash_chain,
|
||||
int xsize, int ysize, const uint32_t* const argb, int quality,
|
||||
int cache_bits, const VP8LHashChain* const hash_chain,
|
||||
VP8LBackwardRefs* const refs, uint16_t* const dist_array) {
|
||||
int i;
|
||||
int ok = 0;
|
||||
@ -1061,11 +1053,10 @@ static int BackwardReferencesHashChainDistanceOnly(
|
||||
// We loop one pixel at a time, but store all currently best points to
|
||||
// non-processed locations from this point.
|
||||
dist_array[0] = 0;
|
||||
HashChainReset(hash_chain);
|
||||
// Add first pixel as literal.
|
||||
AddSingleLiteralWithCostModel(
|
||||
argb + 0, hash_chain, &hashers, cost_model, 0, 0, use_color_cache, 0.0,
|
||||
cost_manager->costs_, dist_array);
|
||||
AddSingleLiteralWithCostModel(argb + 0, &hashers, cost_model, 0,
|
||||
use_color_cache, 0.0, cost_manager->costs_,
|
||||
dist_array);
|
||||
|
||||
for (i = 1; i < pix_count - 1; ++i) {
|
||||
int offset = 0;
|
||||
@ -1094,15 +1085,7 @@ static int BackwardReferencesHashChainDistanceOnly(
|
||||
VP8LColorCacheInsert(&hashers, argb[i + k]);
|
||||
}
|
||||
}
|
||||
// 2) Add to the hash_chain (but cannot add the last pixel)
|
||||
{
|
||||
const int last = (len + i < pix_count - 1) ? len + i
|
||||
: pix_count - 1;
|
||||
for (k = i; k < last; ++k) {
|
||||
HashChainInsert(hash_chain, &argb[k], k);
|
||||
}
|
||||
}
|
||||
// 3) jump.
|
||||
// 2) jump.
|
||||
{
|
||||
const int i_next = i + len - 1; // for loop does ++i, thus -1 here.
|
||||
for (; i <= i_next; ++i) UpdateCostPerIndex(cost_manager, i + 1);
|
||||
@ -1128,8 +1111,8 @@ static int BackwardReferencesHashChainDistanceOnly(
|
||||
UpdateCostPerIndex(cost_manager, i + 1);
|
||||
}
|
||||
|
||||
AddSingleLiteralWithCostModel(argb + i, hash_chain, &hashers, cost_model, i,
|
||||
0, use_color_cache, prev_cost,
|
||||
AddSingleLiteralWithCostModel(argb + i, &hashers, cost_model, i,
|
||||
use_color_cache, prev_cost,
|
||||
cost_manager->costs_, dist_array);
|
||||
|
||||
next_symbol: ;
|
||||
@ -1137,7 +1120,7 @@ static int BackwardReferencesHashChainDistanceOnly(
|
||||
// Handle the last pixel.
|
||||
if (i == (pix_count - 1)) {
|
||||
AddSingleLiteralWithCostModel(
|
||||
argb + i, hash_chain, &hashers, cost_model, i, 1, use_color_cache,
|
||||
argb + i, &hashers, cost_model, i, use_color_cache,
|
||||
cost_manager->costs_[pix_count - 2], cost_manager->costs_, dist_array);
|
||||
}
|
||||
|
||||
@ -1170,12 +1153,11 @@ static void TraceBackwards(uint16_t* const dist_array,
|
||||
}
|
||||
|
||||
static int BackwardReferencesHashChainFollowChosenPath(
|
||||
int xsize, int ysize, const uint32_t* const argb,
|
||||
int xsize, const uint32_t* const argb,
|
||||
int quality, int cache_bits,
|
||||
const uint16_t* const chosen_path, int chosen_path_size,
|
||||
VP8LHashChain* const hash_chain,
|
||||
const VP8LHashChain* const hash_chain,
|
||||
VP8LBackwardRefs* const refs) {
|
||||
const int pix_count = xsize * ysize;
|
||||
const int use_color_cache = (cache_bits > 0);
|
||||
int ix;
|
||||
int i = 0;
|
||||
@ -1190,7 +1172,6 @@ static int BackwardReferencesHashChainFollowChosenPath(
|
||||
}
|
||||
|
||||
ClearBackwardRefs(refs);
|
||||
HashChainReset(hash_chain);
|
||||
for (ix = 0; ix < chosen_path_size; ++ix) {
|
||||
int offset = 0;
|
||||
const int len = chosen_path[ix];
|
||||
@ -1203,12 +1184,6 @@ static int BackwardReferencesHashChainFollowChosenPath(
|
||||
VP8LColorCacheInsert(&hashers, argb[i + k]);
|
||||
}
|
||||
}
|
||||
{
|
||||
const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
|
||||
for (k = 0; k < last; ++k) {
|
||||
HashChainInsert(hash_chain, &argb[i + k], i + k);
|
||||
}
|
||||
}
|
||||
i += len;
|
||||
} else {
|
||||
PixOrCopy v;
|
||||
@ -1221,9 +1196,6 @@ static int BackwardReferencesHashChainFollowChosenPath(
|
||||
v = PixOrCopyCreateLiteral(argb[i]);
|
||||
}
|
||||
BackwardRefsCursorAdd(refs, v);
|
||||
if (i + 1 < pix_count) {
|
||||
HashChainInsert(hash_chain, &argb[i], i);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
@ -1234,11 +1206,10 @@ static int BackwardReferencesHashChainFollowChosenPath(
|
||||
}
|
||||
|
||||
// Returns 1 on success.
|
||||
static int BackwardReferencesTraceBackwards(int xsize, int ysize,
|
||||
const uint32_t* const argb,
|
||||
int quality, int cache_bits,
|
||||
VP8LHashChain* const hash_chain,
|
||||
VP8LBackwardRefs* const refs) {
|
||||
static int BackwardReferencesTraceBackwards(
|
||||
int xsize, int ysize, const uint32_t* const argb, int quality,
|
||||
int cache_bits, const VP8LHashChain* const hash_chain,
|
||||
VP8LBackwardRefs* const refs) {
|
||||
int ok = 0;
|
||||
const int dist_array_size = xsize * ysize;
|
||||
uint16_t* chosen_path = NULL;
|
||||
@ -1255,7 +1226,7 @@ static int BackwardReferencesTraceBackwards(int xsize, int ysize,
|
||||
}
|
||||
TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size);
|
||||
if (!BackwardReferencesHashChainFollowChosenPath(
|
||||
xsize, ysize, argb, quality, cache_bits, chosen_path, chosen_path_size,
|
||||
xsize, argb, quality, cache_bits, chosen_path, chosen_path_size,
|
||||
hash_chain, refs)) {
|
||||
goto Error;
|
||||
}
|
||||
@ -1344,7 +1315,7 @@ static double ComputeCacheEntropy(const uint32_t* argb,
|
||||
// Returns 0 in case of memory error.
|
||||
static int CalculateBestCacheSize(const uint32_t* const argb,
|
||||
int xsize, int ysize, int quality,
|
||||
VP8LHashChain* const hash_chain,
|
||||
const VP8LHashChain* const hash_chain,
|
||||
VP8LBackwardRefs* const refs,
|
||||
int* const lz77_computed,
|
||||
int* const best_cache_bits) {
|
||||
@ -1431,7 +1402,7 @@ static int BackwardRefsWithLocalCache(const uint32_t* const argb,
|
||||
|
||||
static VP8LBackwardRefs* GetBackwardReferencesLowEffort(
|
||||
int width, int height, const uint32_t* const argb, int quality,
|
||||
int* const cache_bits, VP8LHashChain* const hash_chain,
|
||||
int* const cache_bits, const VP8LHashChain* const hash_chain,
|
||||
VP8LBackwardRefs refs_array[2]) {
|
||||
VP8LBackwardRefs* refs_lz77 = &refs_array[0];
|
||||
*cache_bits = 0;
|
||||
@ -1445,7 +1416,7 @@ static VP8LBackwardRefs* GetBackwardReferencesLowEffort(
|
||||
|
||||
static VP8LBackwardRefs* GetBackwardReferences(
|
||||
int width, int height, const uint32_t* const argb, int quality,
|
||||
int* const cache_bits, VP8LHashChain* const hash_chain,
|
||||
int* const cache_bits, const VP8LHashChain* const hash_chain,
|
||||
VP8LBackwardRefs refs_array[2]) {
|
||||
int lz77_is_useful;
|
||||
int lz77_computed;
|
||||
@ -1528,8 +1499,8 @@ static VP8LBackwardRefs* GetBackwardReferences(
|
||||
|
||||
VP8LBackwardRefs* VP8LGetBackwardReferences(
|
||||
int width, int height, const uint32_t* const argb, int quality,
|
||||
int low_effort, int* const cache_bits, VP8LHashChain* const hash_chain,
|
||||
VP8LBackwardRefs refs_array[2]) {
|
||||
int low_effort, int* const cache_bits,
|
||||
const VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[2]) {
|
||||
if (low_effort) {
|
||||
return GetBackwardReferencesLowEffort(width, height, argb, quality,
|
||||
cache_bits, hash_chain, refs_array);
|
||||
|
@ -115,8 +115,6 @@ static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy* const p) {
|
||||
|
||||
typedef struct VP8LHashChain VP8LHashChain;
|
||||
struct VP8LHashChain {
|
||||
// Stores the most recently added position with the given hash value.
|
||||
int32_t hash_to_first_index_[HASH_SIZE];
|
||||
// chain_[pos] stores the previous position with the same hash value
|
||||
// for every pixel in the image.
|
||||
int32_t* chain_;
|
||||
@ -127,6 +125,9 @@ struct VP8LHashChain {
|
||||
|
||||
// Must be called first, to set size.
|
||||
int VP8LHashChainInit(VP8LHashChain* const p, int size);
|
||||
// Pre-compute the best matches for argb.
|
||||
int VP8LHashChainFill(VP8LHashChain* const p,
|
||||
const uint32_t* const argb, int xsize, int ysize);
|
||||
void VP8LHashChainClear(VP8LHashChain* const p); // release memory
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -192,8 +193,8 @@ static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) {
|
||||
// refs[0] or refs[1].
|
||||
VP8LBackwardRefs* VP8LGetBackwardReferences(
|
||||
int width, int height, const uint32_t* const argb, int quality,
|
||||
int low_effort, int* const cache_bits, VP8LHashChain* const hash_chain,
|
||||
VP8LBackwardRefs refs[2]);
|
||||
int low_effort, int* const cache_bits,
|
||||
const VP8LHashChain* const hash_chain, VP8LBackwardRefs refs[2]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -761,6 +761,10 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
|
||||
}
|
||||
|
||||
// Calculate backward references from ARGB image.
|
||||
if (VP8LHashChainFill(hash_chain, argb, width, height) == 0) {
|
||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||
goto Error;
|
||||
}
|
||||
refs = VP8LGetBackwardReferences(width, height, argb, quality, 0, &cache_bits,
|
||||
hash_chain, refs_array);
|
||||
if (refs == NULL) {
|
||||
@ -861,6 +865,10 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw,
|
||||
// 'best_refs' is the reference to the best backward refs and points to one
|
||||
// of refs_array[0] or refs_array[1].
|
||||
// Calculate backward references from ARGB image.
|
||||
if (VP8LHashChainFill(hash_chain, argb, width, height) == 0) {
|
||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||
goto Error;
|
||||
}
|
||||
best_refs = VP8LGetBackwardReferences(width, height, argb, quality,
|
||||
low_effort, cache_bits, hash_chain,
|
||||
refs_array);
|
||||
|
Loading…
Reference in New Issue
Block a user