Smarter LZ77 for uniform regions.

No need to find backward references for pixels in uniform regions
by looking at all pixels.
Only pixels at the same distance from the end need to be compared to.

Change-Id: I4f187e965f0667d3a929775726a412f7e69f6473
This commit is contained in:
Vincent Rabaud 2016-08-25 09:17:02 +02:00
parent 6585075f8a
commit 85cd5d061c

View File

@ -249,13 +249,20 @@ int VP8LHashChainFill(VP8LHashChain* const p, int quality,
const int iter_min = iter_max - quality / 10;
const uint32_t window_size = GetWindowSizeForHashChain(quality, xsize);
int pos;
int argb_comp;
uint32_t base_position;
int32_t* hash_to_first_index;
// Temporarily use the p->offset_length_ as a hash chain.
int32_t* chain = (int32_t*)p->offset_length_;
assert(size > 0);
assert(p->size_ != 0);
assert(p->offset_length_ != NULL);
if (size <= 2) {
p->offset_length_[0] = p->offset_length_[size - 1] = 0;
return 1;
}
hash_to_first_index =
(int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index));
if (hash_to_first_index == NULL) return 0;
@ -263,19 +270,61 @@ int VP8LHashChainFill(VP8LHashChain* const p, int quality,
// 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);
chain[pos] = hash_to_first_index[hash_code];
hash_to_first_index[hash_code] = pos;
argb_comp = (argb[0] == argb[1]);
for (pos = 0; pos < size - 2;) {
uint32_t hash_code;
const int argb_comp_next = (argb[pos + 1] == argb[pos + 2]);
if (argb_comp && argb_comp_next) {
// Consecutive pixels with the same color will share the same hash.
// We therefore use a different hash: the color and its repetition
// length.
uint32_t tmp[2];
uint32_t len = 1;
tmp[0] = argb[pos];
// Figure out how far the pixels are the same.
// The last pixel has a different 64 bit hash, as its next pixel does
// not have the same color, so we just need to get to the last pixel equal
// to its follower.
while (pos + (int)len + 2 < size && argb[pos + len + 2] == argb[pos]) {
++len;
}
if (len > MAX_LENGTH) {
// Skip the pixels that match for distance=1 and length>MAX_LENGTH
// because they are linked to their predecessor and we automatically
// check that in the main for loop below. Skipping means setting no
// predecessor in the chain, hence -1.
memset(chain + pos, 0xff, (len - MAX_LENGTH) * sizeof(*chain));
pos += len - MAX_LENGTH;
len = MAX_LENGTH;
}
// Process the rest of the hash chain.
while (len) {
tmp[1] = len--;
hash_code = GetPixPairHash64(tmp);
chain[pos] = hash_to_first_index[hash_code];
hash_to_first_index[hash_code] = pos++;
}
argb_comp = 0;
} else {
// Just move one pixel forward.
hash_code = GetPixPairHash64(argb + pos);
chain[pos] = hash_to_first_index[hash_code];
hash_to_first_index[hash_code] = pos++;
argb_comp = argb_comp_next;
}
}
// Process the penultimate pixel.
chain[pos] = hash_to_first_index[GetPixPairHash64(argb + pos)];
WebPSafeFree(hash_to_first_index);
// Find the best match interval at each pixel, defined by an offset to the
// pixel and a length. The right-most pixel cannot match anything to the right
// (hence a best length of 0) and the left-most pixel nothing to the left
// (hence an offset of 0).
assert(size > 2);
p->offset_length_[0] = p->offset_length_[size - 1] = 0;
for (base_position = size - 2 < 0 ? 0 : size - 2; base_position > 0;) {
for (base_position = size - 2; base_position > 0;) {
const int max_len = MaxFindCopyLength(size - 1 - base_position);
const uint32_t* const argb_start = argb + base_position;
int iter = iter_max;
@ -288,14 +337,23 @@ int VP8LHashChainFill(VP8LHashChain* const p, int quality,
pos = chain[base_position];
if (!low_effort) {
int curr_length;
// Heuristic: use the comparison with the above line as an initialization.
if (base_position >= (uint32_t)xsize) {
const int curr_length = FindMatchLength(argb_start - xsize, argb_start,
curr_length = FindMatchLength(argb_start - xsize, argb_start,
best_length, max_len);
if (curr_length > best_length) {
best_length = curr_length;
best_distance = xsize;
}
--iter;
}
// Heuristic: compare to the previous pixel.
curr_length =
FindMatchLength(argb_start - 1, argb_start, best_length, max_len);
if (curr_length > best_length) {
best_length = curr_length;
best_distance = 1;
}
--iter;
// Skip the for loop if we already have the maximum.