Path: blob/master/thirdparty/libwebp/src/enc/backward_references_enc.c
21437 views
// Copyright 2012 Google Inc. All Rights Reserved.1//2// Use of this source code is governed by a BSD-style license3// that can be found in the COPYING file in the root of the source4// tree. An additional intellectual property rights grant can be found5// in the file PATENTS. All contributing project authors may6// be found in the AUTHORS file in the root of the source tree.7// -----------------------------------------------------------------------------8//9// Author: Jyrki Alakuijala ([email protected])10//1112#include "src/enc/backward_references_enc.h"1314#include <assert.h>15#include <string.h>1617#include "src/dsp/cpu.h"18#include "src/dsp/lossless.h"19#include "src/dsp/lossless_common.h"20#include "src/enc/histogram_enc.h"21#include "src/enc/vp8i_enc.h"22#include "src/utils/color_cache_utils.h"23#include "src/utils/utils.h"24#include "src/webp/encode.h"25#include "src/webp/format_constants.h"26#include "src/webp/types.h"2728#define MIN_BLOCK_SIZE 256 // minimum block size for backward references2930// 1M window (4M bytes) minus 120 special codes for short distances.31#define WINDOW_SIZE ((1 << WINDOW_SIZE_BITS) - 120)3233// Minimum number of pixels for which it is cheaper to encode a34// distance + length instead of each pixel as a literal.35#define MIN_LENGTH 43637// -----------------------------------------------------------------------------3839static const uint8_t plane_to_code_lut[128] = {4096, 73, 55, 39, 23, 13, 5, 1, 255, 255, 255, 255, 255, 255, 255, 255,41101, 78, 58, 42, 26, 16, 8, 2, 0, 3, 9, 17, 27, 43, 59, 79,42102, 86, 62, 46, 32, 20, 10, 6, 4, 7, 11, 21, 33, 47, 63, 87,43105, 90, 70, 52, 37, 28, 18, 14, 12, 15, 19, 29, 38, 53, 71, 91,44110, 99, 82, 66, 48, 35, 30, 24, 22, 25, 31, 36, 49, 67, 83, 100,45115, 108, 94, 76, 64, 50, 44, 40, 34, 41, 45, 51, 65, 77, 95, 109,46118, 113, 103, 92, 80, 68, 60, 56, 54, 57, 61, 69, 81, 93, 104, 114,47119, 116, 111, 106, 97, 88, 84, 74, 72, 75, 85, 89, 98, 107, 112, 11748};4950extern int VP8LDistanceToPlaneCode(int xsize, int dist);51int VP8LDistanceToPlaneCode(int xsize, int dist) {52const int yoffset = dist / xsize;53const int xoffset = dist - yoffset * xsize;54if (xoffset <= 8 && yoffset < 8) {55return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1;56} else if (xoffset > xsize - 8 && yoffset < 7) {57return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1;58}59return dist + 120;60}6162// Returns the exact index where array1 and array2 are different. For an index63// inferior or equal to best_len_match, the return value just has to be strictly64// inferior to best_len_match. The current behavior is to return 0 if this index65// is best_len_match, and the index itself otherwise.66// If no two elements are the same, it returns max_limit.67static WEBP_INLINE int FindMatchLength(const uint32_t* const array1,68const uint32_t* const array2,69int best_len_match, int max_limit) {70// Before 'expensive' linear match, check if the two arrays match at the71// current best length index.72if (array1[best_len_match] != array2[best_len_match]) return 0;7374return VP8LVectorMismatch(array1, array2, max_limit);75}7677// -----------------------------------------------------------------------------78// VP8LBackwardRefs7980struct PixOrCopyBlock {81PixOrCopyBlock* next; // next block (or NULL)82PixOrCopy* start; // data start83int size; // currently used size84};8586extern void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs);87void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs) {88assert(refs != NULL);89if (refs->tail != NULL) {90*refs->tail = refs->free_blocks; // recycle all blocks at once91}92refs->free_blocks = refs->refs;93refs->tail = &refs->refs;94refs->last_block = NULL;95refs->refs = NULL;96}9798void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs) {99assert(refs != NULL);100VP8LClearBackwardRefs(refs);101while (refs->free_blocks != NULL) {102PixOrCopyBlock* const next = refs->free_blocks->next;103WebPSafeFree(refs->free_blocks);104refs->free_blocks = next;105}106}107108// Swaps the content of two VP8LBackwardRefs.109static void BackwardRefsSwap(VP8LBackwardRefs* const refs1,110VP8LBackwardRefs* const refs2) {111const int point_to_refs1 =112(refs1->tail != NULL && refs1->tail == &refs1->refs);113const int point_to_refs2 =114(refs2->tail != NULL && refs2->tail == &refs2->refs);115const VP8LBackwardRefs tmp = *refs1;116*refs1 = *refs2;117*refs2 = tmp;118if (point_to_refs2) refs1->tail = &refs1->refs;119if (point_to_refs1) refs2->tail = &refs2->refs;120}121122void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size) {123assert(refs != NULL);124memset(refs, 0, sizeof(*refs));125refs->tail = &refs->refs;126refs->block_size =127(block_size < MIN_BLOCK_SIZE) ? MIN_BLOCK_SIZE : block_size;128}129130VP8LRefsCursor VP8LRefsCursorInit(const VP8LBackwardRefs* const refs) {131VP8LRefsCursor c;132c.cur_block = refs->refs;133if (refs->refs != NULL) {134c.cur_pos = c.cur_block->start;135c.last_pos = c.cur_pos + c.cur_block->size;136} else {137c.cur_pos = NULL;138c.last_pos = NULL;139}140return c;141}142143void VP8LRefsCursorNextBlock(VP8LRefsCursor* const c) {144PixOrCopyBlock* const b = c->cur_block->next;145c->cur_pos = (b == NULL) ? NULL : b->start;146c->last_pos = (b == NULL) ? NULL : b->start + b->size;147c->cur_block = b;148}149150// Create a new block, either from the free list or allocated151static PixOrCopyBlock* BackwardRefsNewBlock(VP8LBackwardRefs* const refs) {152PixOrCopyBlock* b = refs->free_blocks;153if (b == NULL) { // allocate new memory chunk154const size_t total_size =155sizeof(*b) + refs->block_size * sizeof(*b->start);156b = (PixOrCopyBlock*)WebPSafeMalloc(1ULL, total_size);157if (b == NULL) {158refs->error |= 1;159return NULL;160}161b->start = (PixOrCopy*)((uint8_t*)b + sizeof(*b)); // not always aligned162} else { // recycle from free-list163refs->free_blocks = b->next;164}165*refs->tail = b;166refs->tail = &b->next;167refs->last_block = b;168b->next = NULL;169b->size = 0;170return b;171}172173// Return 1 on success, 0 on error.174static int BackwardRefsClone(const VP8LBackwardRefs* const from,175VP8LBackwardRefs* const to) {176const PixOrCopyBlock* block_from = from->refs;177VP8LClearBackwardRefs(to);178while (block_from != NULL) {179PixOrCopyBlock* const block_to = BackwardRefsNewBlock(to);180if (block_to == NULL) return 0;181memcpy(block_to->start, block_from->start,182block_from->size * sizeof(PixOrCopy));183block_to->size = block_from->size;184block_from = block_from->next;185}186return 1;187}188189extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,190const PixOrCopy v);191void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,192const PixOrCopy v) {193PixOrCopyBlock* b = refs->last_block;194if (b == NULL || b->size == refs->block_size) {195b = BackwardRefsNewBlock(refs);196if (b == NULL) return; // refs->error is set197}198b->start[b->size++] = v;199}200201// -----------------------------------------------------------------------------202// Hash chains203204int VP8LHashChainInit(VP8LHashChain* const p, int size) {205assert(p->size == 0);206assert(p->offset_length == NULL);207assert(size > 0);208p->offset_length =209(uint32_t*)WebPSafeMalloc(size, sizeof(*p->offset_length));210if (p->offset_length == NULL) return 0;211p->size = size;212213return 1;214}215216void VP8LHashChainClear(VP8LHashChain* const p) {217assert(p != NULL);218WebPSafeFree(p->offset_length);219220p->size = 0;221p->offset_length = NULL;222}223224// -----------------------------------------------------------------------------225226static const uint32_t kHashMultiplierHi = 0xc6a4a793u;227static const uint32_t kHashMultiplierLo = 0x5bd1e996u;228229static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE230uint32_t GetPixPairHash64(const uint32_t* const argb) {231uint32_t key;232key = argb[1] * kHashMultiplierHi;233key += argb[0] * kHashMultiplierLo;234key = key >> (32 - HASH_BITS);235return key;236}237238// Returns the maximum number of hash chain lookups to do for a239// given compression quality. Return value in range [8, 86].240static int GetMaxItersForQuality(int quality) {241return 8 + (quality * quality) / 128;242}243244static int GetWindowSizeForHashChain(int quality, int xsize) {245const int max_window_size = (quality > 75) ? WINDOW_SIZE246: (quality > 50) ? (xsize << 8)247: (quality > 25) ? (xsize << 6)248: (xsize << 4);249assert(xsize > 0);250return (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE : max_window_size;251}252253static WEBP_INLINE int MaxFindCopyLength(int len) {254return (len < MAX_LENGTH) ? len : MAX_LENGTH;255}256257int VP8LHashChainFill(VP8LHashChain* const p, int quality,258const uint32_t* const argb, int xsize, int ysize,259int low_effort, const WebPPicture* const pic,260int percent_range, int* const percent) {261const int size = xsize * ysize;262const int iter_max = GetMaxItersForQuality(quality);263const uint32_t window_size = GetWindowSizeForHashChain(quality, xsize);264int remaining_percent = percent_range;265int percent_start = *percent;266int pos;267int argb_comp;268uint32_t base_position;269int32_t* hash_to_first_index;270// Temporarily use the p->offset_length as a hash chain.271int32_t* chain = (int32_t*)p->offset_length;272assert(size > 0);273assert(p->size != 0);274assert(p->offset_length != NULL);275276if (size <= 2) {277p->offset_length[0] = p->offset_length[size - 1] = 0;278return 1;279}280281hash_to_first_index =282(int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index));283if (hash_to_first_index == NULL) {284return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);285}286287percent_range = remaining_percent / 2;288remaining_percent -= percent_range;289290// Set the int32_t array to -1.291memset(hash_to_first_index, 0xff, HASH_SIZE * sizeof(*hash_to_first_index));292// Fill the chain linking pixels with the same hash.293argb_comp = (argb[0] == argb[1]);294for (pos = 0; pos < size - 2;) {295uint32_t hash_code;296const int argb_comp_next = (argb[pos + 1] == argb[pos + 2]);297if (argb_comp && argb_comp_next) {298// Consecutive pixels with the same color will share the same hash.299// We therefore use a different hash: the color and its repetition300// length.301uint32_t tmp[2];302uint32_t len = 1;303tmp[0] = argb[pos];304// Figure out how far the pixels are the same.305// The last pixel has a different 64 bit hash, as its next pixel does306// not have the same color, so we just need to get to the last pixel equal307// to its follower.308while (pos + (int)len + 2 < size && argb[pos + len + 2] == argb[pos]) {309++len;310}311if (len > MAX_LENGTH) {312// Skip the pixels that match for distance=1 and length>MAX_LENGTH313// because they are linked to their predecessor and we automatically314// check that in the main for loop below. Skipping means setting no315// predecessor in the chain, hence -1.316memset(chain + pos, 0xff, (len - MAX_LENGTH) * sizeof(*chain));317pos += len - MAX_LENGTH;318len = MAX_LENGTH;319}320// Process the rest of the hash chain.321while (len) {322tmp[1] = len--;323hash_code = GetPixPairHash64(tmp);324chain[pos] = hash_to_first_index[hash_code];325hash_to_first_index[hash_code] = pos++;326}327argb_comp = 0;328} else {329// Just move one pixel forward.330hash_code = GetPixPairHash64(argb + pos);331chain[pos] = hash_to_first_index[hash_code];332hash_to_first_index[hash_code] = pos++;333argb_comp = argb_comp_next;334}335336if (!WebPReportProgress(337pic, percent_start + percent_range * pos / (size - 2), percent)) {338WebPSafeFree(hash_to_first_index);339return 0;340}341}342// Process the penultimate pixel.343chain[pos] = hash_to_first_index[GetPixPairHash64(argb + pos)];344345WebPSafeFree(hash_to_first_index);346347percent_start += percent_range;348if (!WebPReportProgress(pic, percent_start, percent)) return 0;349percent_range = remaining_percent;350351// Find the best match interval at each pixel, defined by an offset to the352// pixel and a length. The right-most pixel cannot match anything to the right353// (hence a best length of 0) and the left-most pixel nothing to the left354// (hence an offset of 0).355assert(size > 2);356p->offset_length[0] = p->offset_length[size - 1] = 0;357for (base_position = size - 2; base_position > 0;) {358const int max_len = MaxFindCopyLength(size - 1 - base_position);359const uint32_t* const argb_start = argb + base_position;360int iter = iter_max;361int best_length = 0;362uint32_t best_distance = 0;363uint32_t best_argb;364const int min_pos =365(base_position > window_size) ? base_position - window_size : 0;366const int length_max = (max_len < 256) ? max_len : 256;367uint32_t max_base_position;368369pos = chain[base_position];370if (!low_effort) {371int curr_length;372// Heuristic: use the comparison with the above line as an initialization.373if (base_position >= (uint32_t)xsize) {374curr_length = FindMatchLength(argb_start - xsize, argb_start,375best_length, max_len);376if (curr_length > best_length) {377best_length = curr_length;378best_distance = xsize;379}380--iter;381}382// Heuristic: compare to the previous pixel.383curr_length =384FindMatchLength(argb_start - 1, argb_start, best_length, max_len);385if (curr_length > best_length) {386best_length = curr_length;387best_distance = 1;388}389--iter;390// Skip the for loop if we already have the maximum.391if (best_length == MAX_LENGTH) pos = min_pos - 1;392}393best_argb = argb_start[best_length];394395for (; pos >= min_pos && --iter; pos = chain[pos]) {396int curr_length;397assert(base_position > (uint32_t)pos);398399if (argb[pos + best_length] != best_argb) continue;400401curr_length = VP8LVectorMismatch(argb + pos, argb_start, max_len);402if (best_length < curr_length) {403best_length = curr_length;404best_distance = base_position - pos;405best_argb = argb_start[best_length];406// Stop if we have reached a good enough length.407if (best_length >= length_max) break;408}409}410// We have the best match but in case the two intervals continue matching411// to the left, we have the best matches for the left-extended pixels.412max_base_position = base_position;413while (1) {414assert(best_length <= MAX_LENGTH);415assert(best_distance <= WINDOW_SIZE);416p->offset_length[base_position] =417(best_distance << MAX_LENGTH_BITS) | (uint32_t)best_length;418--base_position;419// Stop if we don't have a match or if we are out of bounds.420if (best_distance == 0 || base_position == 0) break;421// Stop if we cannot extend the matching intervals to the left.422if (base_position < best_distance ||423argb[base_position - best_distance] != argb[base_position]) {424break;425}426// Stop if we are matching at its limit because there could be a closer427// matching interval with the same maximum length. Then again, if the428// matching interval is as close as possible (best_distance == 1), we will429// never find anything better so let's continue.430if (best_length == MAX_LENGTH && best_distance != 1 &&431base_position + MAX_LENGTH < max_base_position) {432break;433}434if (best_length < MAX_LENGTH) {435++best_length;436max_base_position = base_position;437}438}439440if (!WebPReportProgress(pic,441percent_start + percent_range *442(size - 2 - base_position) /443(size - 2),444percent)) {445return 0;446}447}448449return WebPReportProgress(pic, percent_start + percent_range, percent);450}451452static WEBP_INLINE void AddSingleLiteral(uint32_t pixel, int use_color_cache,453VP8LColorCache* const hashers,454VP8LBackwardRefs* const refs) {455PixOrCopy v;456if (use_color_cache) {457const uint32_t key = VP8LColorCacheGetIndex(hashers, pixel);458if (VP8LColorCacheLookup(hashers, key) == pixel) {459v = PixOrCopyCreateCacheIdx(key);460} else {461v = PixOrCopyCreateLiteral(pixel);462VP8LColorCacheSet(hashers, key, pixel);463}464} else {465v = PixOrCopyCreateLiteral(pixel);466}467VP8LBackwardRefsCursorAdd(refs, v);468}469470static int BackwardReferencesRle(int xsize, int ysize,471const uint32_t* const argb,472int cache_bits, VP8LBackwardRefs* const refs) {473const int pix_count = xsize * ysize;474int i, k;475const int use_color_cache = (cache_bits > 0);476VP8LColorCache hashers;477478if (use_color_cache && !VP8LColorCacheInit(&hashers, cache_bits)) {479return 0;480}481VP8LClearBackwardRefs(refs);482// Add first pixel as literal.483AddSingleLiteral(argb[0], use_color_cache, &hashers, refs);484i = 1;485while (i < pix_count) {486const int max_len = MaxFindCopyLength(pix_count - i);487const int rle_len = FindMatchLength(argb + i, argb + i - 1, 0, max_len);488const int prev_row_len = (i < xsize) ? 0 :489FindMatchLength(argb + i, argb + i - xsize, 0, max_len);490if (rle_len >= prev_row_len && rle_len >= MIN_LENGTH) {491VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(1, rle_len));492// We don't need to update the color cache here since it is always the493// same pixel being copied, and that does not change the color cache494// state.495i += rle_len;496} else if (prev_row_len >= MIN_LENGTH) {497VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(xsize, prev_row_len));498if (use_color_cache) {499for (k = 0; k < prev_row_len; ++k) {500VP8LColorCacheInsert(&hashers, argb[i + k]);501}502}503i += prev_row_len;504} else {505AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);506i++;507}508}509if (use_color_cache) VP8LColorCacheClear(&hashers);510return !refs->error;511}512513static int BackwardReferencesLz77(int xsize, int ysize,514const uint32_t* const argb, int cache_bits,515const VP8LHashChain* const hash_chain,516VP8LBackwardRefs* const refs) {517int i;518int i_last_check = -1;519int ok = 0;520int cc_init = 0;521const int use_color_cache = (cache_bits > 0);522const int pix_count = xsize * ysize;523VP8LColorCache hashers;524525if (use_color_cache) {526cc_init = VP8LColorCacheInit(&hashers, cache_bits);527if (!cc_init) goto Error;528}529VP8LClearBackwardRefs(refs);530for (i = 0; i < pix_count;) {531// Alternative#1: Code the pixels starting at 'i' using backward reference.532int offset = 0;533int len = 0;534int j;535VP8LHashChainFindCopy(hash_chain, i, &offset, &len);536if (len >= MIN_LENGTH) {537const int len_ini = len;538int max_reach = 0;539const int j_max =540(i + len_ini >= pix_count) ? pix_count - 1 : i + len_ini;541// Only start from what we have not checked already.542i_last_check = (i > i_last_check) ? i : i_last_check;543// We know the best match for the current pixel but we try to find the544// best matches for the current pixel AND the next one combined.545// The naive method would use the intervals:546// [i,i+len) + [i+len, length of best match at i+len)547// while we check if we can use:548// [i,j) (where j<=i+len) + [j, length of best match at j)549for (j = i_last_check + 1; j <= j_max; ++j) {550const int len_j = VP8LHashChainFindLength(hash_chain, j);551const int reach =552j + (len_j >= MIN_LENGTH ? len_j : 1); // 1 for single literal.553if (reach > max_reach) {554len = j - i;555max_reach = reach;556if (max_reach >= pix_count) break;557}558}559} else {560len = 1;561}562// Go with literal or backward reference.563assert(len > 0);564if (len == 1) {565AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);566} else {567VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));568if (use_color_cache) {569for (j = i; j < i + len; ++j) VP8LColorCacheInsert(&hashers, argb[j]);570}571}572i += len;573}574575ok = !refs->error;576Error:577if (cc_init) VP8LColorCacheClear(&hashers);578return ok;579}580581// Compute an LZ77 by forcing matches to happen within a given distance cost.582// We therefore limit the algorithm to the lowest 32 values in the PlaneCode583// definition.584#define WINDOW_OFFSETS_SIZE_MAX 32585static int BackwardReferencesLz77Box(int xsize, int ysize,586const uint32_t* const argb, int cache_bits,587const VP8LHashChain* const hash_chain_best,588VP8LHashChain* hash_chain,589VP8LBackwardRefs* const refs) {590int i;591const int pix_count = xsize * ysize;592uint16_t* counts;593int window_offsets[WINDOW_OFFSETS_SIZE_MAX] = {0};594int window_offsets_new[WINDOW_OFFSETS_SIZE_MAX] = {0};595int window_offsets_size = 0;596int window_offsets_new_size = 0;597uint16_t* const counts_ini =598(uint16_t*)WebPSafeMalloc(xsize * ysize, sizeof(*counts_ini));599int best_offset_prev = -1, best_length_prev = -1;600if (counts_ini == NULL) return 0;601602// counts[i] counts how many times a pixel is repeated starting at position i.603i = pix_count - 2;604counts = counts_ini + i;605counts[1] = 1;606for (; i >= 0; --i, --counts) {607if (argb[i] == argb[i + 1]) {608// Max out the counts to MAX_LENGTH.609counts[0] = counts[1] + (counts[1] != MAX_LENGTH);610} else {611counts[0] = 1;612}613}614615// Figure out the window offsets around a pixel. They are stored in a616// spiraling order around the pixel as defined by VP8LDistanceToPlaneCode.617{618int x, y;619for (y = 0; y <= 6; ++y) {620for (x = -6; x <= 6; ++x) {621const int offset = y * xsize + x;622int plane_code;623// Ignore offsets that bring us after the pixel.624if (offset <= 0) continue;625plane_code = VP8LDistanceToPlaneCode(xsize, offset) - 1;626if (plane_code >= WINDOW_OFFSETS_SIZE_MAX) continue;627window_offsets[plane_code] = offset;628}629}630// For narrow images, not all plane codes are reached, so remove those.631for (i = 0; i < WINDOW_OFFSETS_SIZE_MAX; ++i) {632if (window_offsets[i] == 0) continue;633window_offsets[window_offsets_size++] = window_offsets[i];634}635// Given a pixel P, find the offsets that reach pixels unreachable from P-1636// with any of the offsets in window_offsets[].637for (i = 0; i < window_offsets_size; ++i) {638int j;639int is_reachable = 0;640for (j = 0; j < window_offsets_size && !is_reachable; ++j) {641is_reachable |= (window_offsets[i] == window_offsets[j] + 1);642}643if (!is_reachable) {644window_offsets_new[window_offsets_new_size] = window_offsets[i];645++window_offsets_new_size;646}647}648}649650hash_chain->offset_length[0] = 0;651for (i = 1; i < pix_count; ++i) {652int ind;653int best_length = VP8LHashChainFindLength(hash_chain_best, i);654int best_offset;655int do_compute = 1;656657if (best_length >= MAX_LENGTH) {658// Do not recompute the best match if we already have a maximal one in the659// window.660best_offset = VP8LHashChainFindOffset(hash_chain_best, i);661for (ind = 0; ind < window_offsets_size; ++ind) {662if (best_offset == window_offsets[ind]) {663do_compute = 0;664break;665}666}667}668if (do_compute) {669// Figure out if we should use the offset/length from the previous pixel670// as an initial guess and therefore only inspect the offsets in671// window_offsets_new[].672const int use_prev =673(best_length_prev > 1) && (best_length_prev < MAX_LENGTH);674const int num_ind =675use_prev ? window_offsets_new_size : window_offsets_size;676best_length = use_prev ? best_length_prev - 1 : 0;677best_offset = use_prev ? best_offset_prev : 0;678// Find the longest match in a window around the pixel.679for (ind = 0; ind < num_ind; ++ind) {680int curr_length = 0;681int j = i;682int j_offset =683use_prev ? i - window_offsets_new[ind] : i - window_offsets[ind];684if (j_offset < 0 || argb[j_offset] != argb[i]) continue;685// The longest match is the sum of how many times each pixel is686// repeated.687do {688const int counts_j_offset = counts_ini[j_offset];689const int counts_j = counts_ini[j];690if (counts_j_offset != counts_j) {691curr_length +=692(counts_j_offset < counts_j) ? counts_j_offset : counts_j;693break;694}695// The same color is repeated counts_pos times at j_offset and j.696curr_length += counts_j_offset;697j_offset += counts_j_offset;698j += counts_j_offset;699} while (curr_length <= MAX_LENGTH && j < pix_count &&700argb[j_offset] == argb[j]);701if (best_length < curr_length) {702best_offset =703use_prev ? window_offsets_new[ind] : window_offsets[ind];704if (curr_length >= MAX_LENGTH) {705best_length = MAX_LENGTH;706break;707} else {708best_length = curr_length;709}710}711}712}713714assert(i + best_length <= pix_count);715assert(best_length <= MAX_LENGTH);716if (best_length <= MIN_LENGTH) {717hash_chain->offset_length[i] = 0;718best_offset_prev = 0;719best_length_prev = 0;720} else {721hash_chain->offset_length[i] =722(best_offset << MAX_LENGTH_BITS) | (uint32_t)best_length;723best_offset_prev = best_offset;724best_length_prev = best_length;725}726}727hash_chain->offset_length[0] = 0;728WebPSafeFree(counts_ini);729730return BackwardReferencesLz77(xsize, ysize, argb, cache_bits, hash_chain,731refs);732}733734// -----------------------------------------------------------------------------735736static void BackwardReferences2DLocality(int xsize,737const VP8LBackwardRefs* const refs) {738VP8LRefsCursor c = VP8LRefsCursorInit(refs);739while (VP8LRefsCursorOk(&c)) {740if (PixOrCopyIsCopy(c.cur_pos)) {741const int dist = c.cur_pos->argb_or_distance;742const int transformed_dist = VP8LDistanceToPlaneCode(xsize, dist);743c.cur_pos->argb_or_distance = transformed_dist;744}745VP8LRefsCursorNext(&c);746}747}748749// Evaluate optimal cache bits for the local color cache.750// The input *best_cache_bits sets the maximum cache bits to use (passing 0751// implies disabling the local color cache). The local color cache is also752// disabled for the lower (<= 25) quality.753// Returns 0 in case of memory error.754static int CalculateBestCacheSize(const uint32_t* argb, int quality,755const VP8LBackwardRefs* const refs,756int* const best_cache_bits) {757int i;758const int cache_bits_max = (quality <= 25) ? 0 : *best_cache_bits;759uint64_t entropy_min = WEBP_UINT64_MAX;760int cc_init[MAX_COLOR_CACHE_BITS + 1] = { 0 };761VP8LColorCache hashers[MAX_COLOR_CACHE_BITS + 1];762VP8LRefsCursor c = VP8LRefsCursorInit(refs);763VP8LHistogram* histos[MAX_COLOR_CACHE_BITS + 1] = { NULL };764int ok = 0;765766assert(cache_bits_max >= 0 && cache_bits_max <= MAX_COLOR_CACHE_BITS);767768if (cache_bits_max == 0) {769*best_cache_bits = 0;770// Local color cache is disabled.771return 1;772}773774// Allocate data.775for (i = 0; i <= cache_bits_max; ++i) {776histos[i] = VP8LAllocateHistogram(i);777if (histos[i] == NULL) goto Error;778VP8LHistogramInit(histos[i], i, /*init_arrays=*/ 1);779if (i == 0) continue;780cc_init[i] = VP8LColorCacheInit(&hashers[i], i);781if (!cc_init[i]) goto Error;782}783784// Find the cache_bits giving the lowest entropy. The search is done in a785// brute-force way as the function (entropy w.r.t cache_bits) can be786// anything in practice.787while (VP8LRefsCursorOk(&c)) {788const PixOrCopy* const v = c.cur_pos;789if (PixOrCopyIsLiteral(v)) {790const uint32_t pix = *argb++;791const uint32_t a = (pix >> 24) & 0xff;792const uint32_t r = (pix >> 16) & 0xff;793const uint32_t g = (pix >> 8) & 0xff;794const uint32_t b = (pix >> 0) & 0xff;795// The keys of the caches can be derived from the longest one.796int key = VP8LHashPix(pix, 32 - cache_bits_max);797// Do not use the color cache for cache_bits = 0.798++histos[0]->blue[b];799++histos[0]->literal[g];800++histos[0]->red[r];801++histos[0]->alpha[a];802// Deal with cache_bits > 0.803for (i = cache_bits_max; i >= 1; --i, key >>= 1) {804if (VP8LColorCacheLookup(&hashers[i], key) == pix) {805++histos[i]->literal[NUM_LITERAL_CODES + NUM_LENGTH_CODES + key];806} else {807VP8LColorCacheSet(&hashers[i], key, pix);808++histos[i]->blue[b];809++histos[i]->literal[g];810++histos[i]->red[r];811++histos[i]->alpha[a];812}813}814} else {815int code, extra_bits, extra_bits_value;816// We should compute the contribution of the (distance,length)817// histograms but those are the same independently from the cache size.818// As those constant contributions are in the end added to the other819// histogram contributions, we can ignore them, except for the length820// prefix that is part of the 'literal' histogram.821int len = PixOrCopyLength(v);822uint32_t argb_prev = *argb ^ 0xffffffffu;823VP8LPrefixEncode(len, &code, &extra_bits, &extra_bits_value);824for (i = 0; i <= cache_bits_max; ++i) {825++histos[i]->literal[NUM_LITERAL_CODES + code];826}827// Update the color caches.828do {829if (*argb != argb_prev) {830// Efficiency: insert only if the color changes.831int key = VP8LHashPix(*argb, 32 - cache_bits_max);832for (i = cache_bits_max; i >= 1; --i, key >>= 1) {833hashers[i].colors[key] = *argb;834}835argb_prev = *argb;836}837argb++;838} while (--len != 0);839}840VP8LRefsCursorNext(&c);841}842843for (i = 0; i <= cache_bits_max; ++i) {844const uint64_t entropy = VP8LHistogramEstimateBits(histos[i]);845if (i == 0 || entropy < entropy_min) {846entropy_min = entropy;847*best_cache_bits = i;848}849}850ok = 1;851Error:852for (i = 0; i <= cache_bits_max; ++i) {853if (cc_init[i]) VP8LColorCacheClear(&hashers[i]);854VP8LFreeHistogram(histos[i]);855}856return ok;857}858859// Update (in-place) backward references for specified cache_bits.860static int BackwardRefsWithLocalCache(const uint32_t* const argb,861int cache_bits,862VP8LBackwardRefs* const refs) {863int pixel_index = 0;864VP8LColorCache hashers;865VP8LRefsCursor c = VP8LRefsCursorInit(refs);866if (!VP8LColorCacheInit(&hashers, cache_bits)) return 0;867868while (VP8LRefsCursorOk(&c)) {869PixOrCopy* const v = c.cur_pos;870if (PixOrCopyIsLiteral(v)) {871const uint32_t argb_literal = v->argb_or_distance;872const int ix = VP8LColorCacheContains(&hashers, argb_literal);873if (ix >= 0) {874// hashers contains argb_literal875*v = PixOrCopyCreateCacheIdx(ix);876} else {877VP8LColorCacheInsert(&hashers, argb_literal);878}879++pixel_index;880} else {881// refs was created without local cache, so it can not have cache indexes.882int k;883assert(PixOrCopyIsCopy(v));884for (k = 0; k < v->len; ++k) {885VP8LColorCacheInsert(&hashers, argb[pixel_index++]);886}887}888VP8LRefsCursorNext(&c);889}890VP8LColorCacheClear(&hashers);891return 1;892}893894static VP8LBackwardRefs* GetBackwardReferencesLowEffort(895int width, int height, const uint32_t* const argb,896int* const cache_bits, const VP8LHashChain* const hash_chain,897VP8LBackwardRefs* const refs_lz77) {898*cache_bits = 0;899if (!BackwardReferencesLz77(width, height, argb, 0, hash_chain, refs_lz77)) {900return NULL;901}902BackwardReferences2DLocality(width, refs_lz77);903return refs_lz77;904}905906extern int VP8LBackwardReferencesTraceBackwards(907int xsize, int ysize, const uint32_t* const argb, int cache_bits,908const VP8LHashChain* const hash_chain,909const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst);910static int GetBackwardReferences(int width, int height,911const uint32_t* const argb, int quality,912int lz77_types_to_try, int cache_bits_max,913int do_no_cache,914const VP8LHashChain* const hash_chain,915VP8LBackwardRefs* const refs,916int* const cache_bits_best) {917VP8LHistogram* histo = NULL;918int i, lz77_type;919// Index 0 is for a color cache, index 1 for no cache (if needed).920int lz77_types_best[2] = {0, 0};921uint64_t bit_costs_best[2] = {WEBP_UINT64_MAX, WEBP_UINT64_MAX};922VP8LHashChain hash_chain_box;923VP8LBackwardRefs* const refs_tmp = &refs[do_no_cache ? 2 : 1];924int status = 0;925memset(&hash_chain_box, 0, sizeof(hash_chain_box));926927histo = VP8LAllocateHistogram(MAX_COLOR_CACHE_BITS);928if (histo == NULL) goto Error;929930for (lz77_type = 1; lz77_types_to_try;931lz77_types_to_try &= ~lz77_type, lz77_type <<= 1) {932int res = 0;933uint64_t bit_cost = 0u;934if ((lz77_types_to_try & lz77_type) == 0) continue;935switch (lz77_type) {936case kLZ77RLE:937res = BackwardReferencesRle(width, height, argb, 0, refs_tmp);938break;939case kLZ77Standard:940// Compute LZ77 with no cache (0 bits), as the ideal LZ77 with a color941// cache is not that different in practice.942res = BackwardReferencesLz77(width, height, argb, 0, hash_chain,943refs_tmp);944break;945case kLZ77Box:946if (!VP8LHashChainInit(&hash_chain_box, width * height)) goto Error;947res = BackwardReferencesLz77Box(width, height, argb, 0, hash_chain,948&hash_chain_box, refs_tmp);949break;950default:951assert(0);952}953if (!res) goto Error;954955// Start with the no color cache case.956for (i = 1; i >= 0; --i) {957int cache_bits = (i == 1) ? 0 : cache_bits_max;958959if (i == 1 && !do_no_cache) continue;960961if (i == 0) {962// Try with a color cache.963if (!CalculateBestCacheSize(argb, quality, refs_tmp, &cache_bits)) {964goto Error;965}966if (cache_bits > 0) {967if (!BackwardRefsWithLocalCache(argb, cache_bits, refs_tmp)) {968goto Error;969}970}971}972973if (i == 0 && do_no_cache && cache_bits == 0) {974// No need to re-compute bit_cost as it was computed at i == 1.975} else {976VP8LHistogramCreate(histo, refs_tmp, cache_bits);977bit_cost = VP8LHistogramEstimateBits(histo);978}979980if (bit_cost < bit_costs_best[i]) {981if (i == 1) {982// Do not swap as the full cache analysis would have the wrong983// VP8LBackwardRefs to start with.984if (!BackwardRefsClone(refs_tmp, &refs[1])) goto Error;985} else {986BackwardRefsSwap(refs_tmp, &refs[0]);987}988bit_costs_best[i] = bit_cost;989lz77_types_best[i] = lz77_type;990if (i == 0) *cache_bits_best = cache_bits;991}992}993}994assert(lz77_types_best[0] > 0);995assert(!do_no_cache || lz77_types_best[1] > 0);996997// Improve on simple LZ77 but only for high quality (TraceBackwards is998// costly).999for (i = 1; i >= 0; --i) {1000if (i == 1 && !do_no_cache) continue;1001if ((lz77_types_best[i] == kLZ77Standard ||1002lz77_types_best[i] == kLZ77Box) &&1003quality >= 25) {1004const VP8LHashChain* const hash_chain_tmp =1005(lz77_types_best[i] == kLZ77Standard) ? hash_chain : &hash_chain_box;1006const int cache_bits = (i == 1) ? 0 : *cache_bits_best;1007uint64_t bit_cost_trace;1008if (!VP8LBackwardReferencesTraceBackwards(width, height, argb, cache_bits,1009hash_chain_tmp, &refs[i],1010refs_tmp)) {1011goto Error;1012}1013VP8LHistogramCreate(histo, refs_tmp, cache_bits);1014bit_cost_trace = VP8LHistogramEstimateBits(histo);1015if (bit_cost_trace < bit_costs_best[i]) {1016BackwardRefsSwap(refs_tmp, &refs[i]);1017}1018}10191020BackwardReferences2DLocality(width, &refs[i]);10211022if (i == 1 && lz77_types_best[0] == lz77_types_best[1] &&1023*cache_bits_best == 0) {1024// If the best cache size is 0 and we have the same best LZ77, just copy1025// the data over and stop here.1026if (!BackwardRefsClone(&refs[1], &refs[0])) goto Error;1027break;1028}1029}1030status = 1;10311032Error:1033VP8LHashChainClear(&hash_chain_box);1034VP8LFreeHistogram(histo);1035return status;1036}10371038int VP8LGetBackwardReferences(1039int width, int height, const uint32_t* const argb, int quality,1040int low_effort, int lz77_types_to_try, int cache_bits_max, int do_no_cache,1041const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs,1042int* const cache_bits_best, const WebPPicture* const pic, int percent_range,1043int* const percent) {1044if (low_effort) {1045VP8LBackwardRefs* refs_best;1046*cache_bits_best = cache_bits_max;1047refs_best = GetBackwardReferencesLowEffort(1048width, height, argb, cache_bits_best, hash_chain, refs);1049if (refs_best == NULL) {1050return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);1051}1052// Set it in first position.1053BackwardRefsSwap(refs_best, &refs[0]);1054} else {1055if (!GetBackwardReferences(width, height, argb, quality, lz77_types_to_try,1056cache_bits_max, do_no_cache, hash_chain, refs,1057cache_bits_best)) {1058return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);1059}1060}10611062return WebPReportProgress(pic, *percent + percent_range, percent);1063}106410651066