Path: blob/master/thirdparty/libwebp/src/enc/backward_references_enc.c
9913 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>1516#include "src/dsp/dsp.h"17#include "src/dsp/lossless.h"18#include "src/dsp/lossless_common.h"19#include "src/enc/histogram_enc.h"20#include "src/enc/vp8i_enc.h"21#include "src/utils/color_cache_utils.h"22#include "src/utils/utils.h"23#include "src/webp/encode.h"2425#define MIN_BLOCK_SIZE 256 // minimum block size for backward references2627// 1M window (4M bytes) minus 120 special codes for short distances.28#define WINDOW_SIZE ((1 << WINDOW_SIZE_BITS) - 120)2930// Minimum number of pixels for which it is cheaper to encode a31// distance + length instead of each pixel as a literal.32#define MIN_LENGTH 43334// -----------------------------------------------------------------------------3536static const uint8_t plane_to_code_lut[128] = {3796, 73, 55, 39, 23, 13, 5, 1, 255, 255, 255, 255, 255, 255, 255, 255,38101, 78, 58, 42, 26, 16, 8, 2, 0, 3, 9, 17, 27, 43, 59, 79,39102, 86, 62, 46, 32, 20, 10, 6, 4, 7, 11, 21, 33, 47, 63, 87,40105, 90, 70, 52, 37, 28, 18, 14, 12, 15, 19, 29, 38, 53, 71, 91,41110, 99, 82, 66, 48, 35, 30, 24, 22, 25, 31, 36, 49, 67, 83, 100,42115, 108, 94, 76, 64, 50, 44, 40, 34, 41, 45, 51, 65, 77, 95, 109,43118, 113, 103, 92, 80, 68, 60, 56, 54, 57, 61, 69, 81, 93, 104, 114,44119, 116, 111, 106, 97, 88, 84, 74, 72, 75, 85, 89, 98, 107, 112, 11745};4647extern int VP8LDistanceToPlaneCode(int xsize, int dist);48int VP8LDistanceToPlaneCode(int xsize, int dist) {49const int yoffset = dist / xsize;50const int xoffset = dist - yoffset * xsize;51if (xoffset <= 8 && yoffset < 8) {52return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1;53} else if (xoffset > xsize - 8 && yoffset < 7) {54return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1;55}56return dist + 120;57}5859// Returns the exact index where array1 and array2 are different. For an index60// inferior or equal to best_len_match, the return value just has to be strictly61// inferior to best_len_match. The current behavior is to return 0 if this index62// is best_len_match, and the index itself otherwise.63// If no two elements are the same, it returns max_limit.64static WEBP_INLINE int FindMatchLength(const uint32_t* const array1,65const uint32_t* const array2,66int best_len_match, int max_limit) {67// Before 'expensive' linear match, check if the two arrays match at the68// current best length index.69if (array1[best_len_match] != array2[best_len_match]) return 0;7071return VP8LVectorMismatch(array1, array2, max_limit);72}7374// -----------------------------------------------------------------------------75// VP8LBackwardRefs7677struct PixOrCopyBlock {78PixOrCopyBlock* next_; // next block (or NULL)79PixOrCopy* start_; // data start80int size_; // currently used size81};8283extern void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs);84void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs) {85assert(refs != NULL);86if (refs->tail_ != NULL) {87*refs->tail_ = refs->free_blocks_; // recycle all blocks at once88}89refs->free_blocks_ = refs->refs_;90refs->tail_ = &refs->refs_;91refs->last_block_ = NULL;92refs->refs_ = NULL;93}9495void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs) {96assert(refs != NULL);97VP8LClearBackwardRefs(refs);98while (refs->free_blocks_ != NULL) {99PixOrCopyBlock* const next = refs->free_blocks_->next_;100WebPSafeFree(refs->free_blocks_);101refs->free_blocks_ = next;102}103}104105// Swaps the content of two VP8LBackwardRefs.106static void BackwardRefsSwap(VP8LBackwardRefs* const refs1,107VP8LBackwardRefs* const refs2) {108const int point_to_refs1 =109(refs1->tail_ != NULL && refs1->tail_ == &refs1->refs_);110const int point_to_refs2 =111(refs2->tail_ != NULL && refs2->tail_ == &refs2->refs_);112const VP8LBackwardRefs tmp = *refs1;113*refs1 = *refs2;114*refs2 = tmp;115if (point_to_refs2) refs1->tail_ = &refs1->refs_;116if (point_to_refs1) refs2->tail_ = &refs2->refs_;117}118119void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size) {120assert(refs != NULL);121memset(refs, 0, sizeof(*refs));122refs->tail_ = &refs->refs_;123refs->block_size_ =124(block_size < MIN_BLOCK_SIZE) ? MIN_BLOCK_SIZE : block_size;125}126127VP8LRefsCursor VP8LRefsCursorInit(const VP8LBackwardRefs* const refs) {128VP8LRefsCursor c;129c.cur_block_ = refs->refs_;130if (refs->refs_ != NULL) {131c.cur_pos = c.cur_block_->start_;132c.last_pos_ = c.cur_pos + c.cur_block_->size_;133} else {134c.cur_pos = NULL;135c.last_pos_ = NULL;136}137return c;138}139140void VP8LRefsCursorNextBlock(VP8LRefsCursor* const c) {141PixOrCopyBlock* const b = c->cur_block_->next_;142c->cur_pos = (b == NULL) ? NULL : b->start_;143c->last_pos_ = (b == NULL) ? NULL : b->start_ + b->size_;144c->cur_block_ = b;145}146147// Create a new block, either from the free list or allocated148static PixOrCopyBlock* BackwardRefsNewBlock(VP8LBackwardRefs* const refs) {149PixOrCopyBlock* b = refs->free_blocks_;150if (b == NULL) { // allocate new memory chunk151const size_t total_size =152sizeof(*b) + refs->block_size_ * sizeof(*b->start_);153b = (PixOrCopyBlock*)WebPSafeMalloc(1ULL, total_size);154if (b == NULL) {155refs->error_ |= 1;156return NULL;157}158b->start_ = (PixOrCopy*)((uint8_t*)b + sizeof(*b)); // not always aligned159} else { // recycle from free-list160refs->free_blocks_ = b->next_;161}162*refs->tail_ = b;163refs->tail_ = &b->next_;164refs->last_block_ = b;165b->next_ = NULL;166b->size_ = 0;167return b;168}169170// Return 1 on success, 0 on error.171static int BackwardRefsClone(const VP8LBackwardRefs* const from,172VP8LBackwardRefs* const to) {173const PixOrCopyBlock* block_from = from->refs_;174VP8LClearBackwardRefs(to);175while (block_from != NULL) {176PixOrCopyBlock* const block_to = BackwardRefsNewBlock(to);177if (block_to == NULL) return 0;178memcpy(block_to->start_, block_from->start_,179block_from->size_ * sizeof(PixOrCopy));180block_to->size_ = block_from->size_;181block_from = block_from->next_;182}183return 1;184}185186extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,187const PixOrCopy v);188void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,189const PixOrCopy v) {190PixOrCopyBlock* b = refs->last_block_;191if (b == NULL || b->size_ == refs->block_size_) {192b = BackwardRefsNewBlock(refs);193if (b == NULL) return; // refs->error_ is set194}195b->start_[b->size_++] = v;196}197198// -----------------------------------------------------------------------------199// Hash chains200201int VP8LHashChainInit(VP8LHashChain* const p, int size) {202assert(p->size_ == 0);203assert(p->offset_length_ == NULL);204assert(size > 0);205p->offset_length_ =206(uint32_t*)WebPSafeMalloc(size, sizeof(*p->offset_length_));207if (p->offset_length_ == NULL) return 0;208p->size_ = size;209210return 1;211}212213void VP8LHashChainClear(VP8LHashChain* const p) {214assert(p != NULL);215WebPSafeFree(p->offset_length_);216217p->size_ = 0;218p->offset_length_ = NULL;219}220221// -----------------------------------------------------------------------------222223static const uint32_t kHashMultiplierHi = 0xc6a4a793u;224static const uint32_t kHashMultiplierLo = 0x5bd1e996u;225226static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE227uint32_t GetPixPairHash64(const uint32_t* const argb) {228uint32_t key;229key = argb[1] * kHashMultiplierHi;230key += argb[0] * kHashMultiplierLo;231key = key >> (32 - HASH_BITS);232return key;233}234235// Returns the maximum number of hash chain lookups to do for a236// given compression quality. Return value in range [8, 86].237static int GetMaxItersForQuality(int quality) {238return 8 + (quality * quality) / 128;239}240241static int GetWindowSizeForHashChain(int quality, int xsize) {242const int max_window_size = (quality > 75) ? WINDOW_SIZE243: (quality > 50) ? (xsize << 8)244: (quality > 25) ? (xsize << 6)245: (xsize << 4);246assert(xsize > 0);247return (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE : max_window_size;248}249250static WEBP_INLINE int MaxFindCopyLength(int len) {251return (len < MAX_LENGTH) ? len : MAX_LENGTH;252}253254int VP8LHashChainFill(VP8LHashChain* const p, int quality,255const uint32_t* const argb, int xsize, int ysize,256int low_effort, const WebPPicture* const pic,257int percent_range, int* const percent) {258const int size = xsize * ysize;259const int iter_max = GetMaxItersForQuality(quality);260const uint32_t window_size = GetWindowSizeForHashChain(quality, xsize);261int remaining_percent = percent_range;262int percent_start = *percent;263int pos;264int argb_comp;265uint32_t base_position;266int32_t* hash_to_first_index;267// Temporarily use the p->offset_length_ as a hash chain.268int32_t* chain = (int32_t*)p->offset_length_;269assert(size > 0);270assert(p->size_ != 0);271assert(p->offset_length_ != NULL);272273if (size <= 2) {274p->offset_length_[0] = p->offset_length_[size - 1] = 0;275return 1;276}277278hash_to_first_index =279(int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index));280if (hash_to_first_index == NULL) {281return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);282}283284percent_range = remaining_percent / 2;285remaining_percent -= percent_range;286287// Set the int32_t array to -1.288memset(hash_to_first_index, 0xff, HASH_SIZE * sizeof(*hash_to_first_index));289// Fill the chain linking pixels with the same hash.290argb_comp = (argb[0] == argb[1]);291for (pos = 0; pos < size - 2;) {292uint32_t hash_code;293const int argb_comp_next = (argb[pos + 1] == argb[pos + 2]);294if (argb_comp && argb_comp_next) {295// Consecutive pixels with the same color will share the same hash.296// We therefore use a different hash: the color and its repetition297// length.298uint32_t tmp[2];299uint32_t len = 1;300tmp[0] = argb[pos];301// Figure out how far the pixels are the same.302// The last pixel has a different 64 bit hash, as its next pixel does303// not have the same color, so we just need to get to the last pixel equal304// to its follower.305while (pos + (int)len + 2 < size && argb[pos + len + 2] == argb[pos]) {306++len;307}308if (len > MAX_LENGTH) {309// Skip the pixels that match for distance=1 and length>MAX_LENGTH310// because they are linked to their predecessor and we automatically311// check that in the main for loop below. Skipping means setting no312// predecessor in the chain, hence -1.313memset(chain + pos, 0xff, (len - MAX_LENGTH) * sizeof(*chain));314pos += len - MAX_LENGTH;315len = MAX_LENGTH;316}317// Process the rest of the hash chain.318while (len) {319tmp[1] = len--;320hash_code = GetPixPairHash64(tmp);321chain[pos] = hash_to_first_index[hash_code];322hash_to_first_index[hash_code] = pos++;323}324argb_comp = 0;325} else {326// Just move one pixel forward.327hash_code = GetPixPairHash64(argb + pos);328chain[pos] = hash_to_first_index[hash_code];329hash_to_first_index[hash_code] = pos++;330argb_comp = argb_comp_next;331}332333if (!WebPReportProgress(334pic, percent_start + percent_range * pos / (size - 2), percent)) {335WebPSafeFree(hash_to_first_index);336return 0;337}338}339// Process the penultimate pixel.340chain[pos] = hash_to_first_index[GetPixPairHash64(argb + pos)];341342WebPSafeFree(hash_to_first_index);343344percent_start += percent_range;345if (!WebPReportProgress(pic, percent_start, percent)) return 0;346percent_range = remaining_percent;347348// Find the best match interval at each pixel, defined by an offset to the349// pixel and a length. The right-most pixel cannot match anything to the right350// (hence a best length of 0) and the left-most pixel nothing to the left351// (hence an offset of 0).352assert(size > 2);353p->offset_length_[0] = p->offset_length_[size - 1] = 0;354for (base_position = size - 2; base_position > 0;) {355const int max_len = MaxFindCopyLength(size - 1 - base_position);356const uint32_t* const argb_start = argb + base_position;357int iter = iter_max;358int best_length = 0;359uint32_t best_distance = 0;360uint32_t best_argb;361const int min_pos =362(base_position > window_size) ? base_position - window_size : 0;363const int length_max = (max_len < 256) ? max_len : 256;364uint32_t max_base_position;365366pos = chain[base_position];367if (!low_effort) {368int curr_length;369// Heuristic: use the comparison with the above line as an initialization.370if (base_position >= (uint32_t)xsize) {371curr_length = FindMatchLength(argb_start - xsize, argb_start,372best_length, max_len);373if (curr_length > best_length) {374best_length = curr_length;375best_distance = xsize;376}377--iter;378}379// Heuristic: compare to the previous pixel.380curr_length =381FindMatchLength(argb_start - 1, argb_start, best_length, max_len);382if (curr_length > best_length) {383best_length = curr_length;384best_distance = 1;385}386--iter;387// Skip the for loop if we already have the maximum.388if (best_length == MAX_LENGTH) pos = min_pos - 1;389}390best_argb = argb_start[best_length];391392for (; pos >= min_pos && --iter; pos = chain[pos]) {393int curr_length;394assert(base_position > (uint32_t)pos);395396if (argb[pos + best_length] != best_argb) continue;397398curr_length = VP8LVectorMismatch(argb + pos, argb_start, max_len);399if (best_length < curr_length) {400best_length = curr_length;401best_distance = base_position - pos;402best_argb = argb_start[best_length];403// Stop if we have reached a good enough length.404if (best_length >= length_max) break;405}406}407// We have the best match but in case the two intervals continue matching408// to the left, we have the best matches for the left-extended pixels.409max_base_position = base_position;410while (1) {411assert(best_length <= MAX_LENGTH);412assert(best_distance <= WINDOW_SIZE);413p->offset_length_[base_position] =414(best_distance << MAX_LENGTH_BITS) | (uint32_t)best_length;415--base_position;416// Stop if we don't have a match or if we are out of bounds.417if (best_distance == 0 || base_position == 0) break;418// Stop if we cannot extend the matching intervals to the left.419if (base_position < best_distance ||420argb[base_position - best_distance] != argb[base_position]) {421break;422}423// Stop if we are matching at its limit because there could be a closer424// matching interval with the same maximum length. Then again, if the425// matching interval is as close as possible (best_distance == 1), we will426// never find anything better so let's continue.427if (best_length == MAX_LENGTH && best_distance != 1 &&428base_position + MAX_LENGTH < max_base_position) {429break;430}431if (best_length < MAX_LENGTH) {432++best_length;433max_base_position = base_position;434}435}436437if (!WebPReportProgress(pic,438percent_start + percent_range *439(size - 2 - base_position) /440(size - 2),441percent)) {442return 0;443}444}445446return WebPReportProgress(pic, percent_start + percent_range, percent);447}448449static WEBP_INLINE void AddSingleLiteral(uint32_t pixel, int use_color_cache,450VP8LColorCache* const hashers,451VP8LBackwardRefs* const refs) {452PixOrCopy v;453if (use_color_cache) {454const uint32_t key = VP8LColorCacheGetIndex(hashers, pixel);455if (VP8LColorCacheLookup(hashers, key) == pixel) {456v = PixOrCopyCreateCacheIdx(key);457} else {458v = PixOrCopyCreateLiteral(pixel);459VP8LColorCacheSet(hashers, key, pixel);460}461} else {462v = PixOrCopyCreateLiteral(pixel);463}464VP8LBackwardRefsCursorAdd(refs, v);465}466467static int BackwardReferencesRle(int xsize, int ysize,468const uint32_t* const argb,469int cache_bits, VP8LBackwardRefs* const refs) {470const int pix_count = xsize * ysize;471int i, k;472const int use_color_cache = (cache_bits > 0);473VP8LColorCache hashers;474475if (use_color_cache && !VP8LColorCacheInit(&hashers, cache_bits)) {476return 0;477}478VP8LClearBackwardRefs(refs);479// Add first pixel as literal.480AddSingleLiteral(argb[0], use_color_cache, &hashers, refs);481i = 1;482while (i < pix_count) {483const int max_len = MaxFindCopyLength(pix_count - i);484const int rle_len = FindMatchLength(argb + i, argb + i - 1, 0, max_len);485const int prev_row_len = (i < xsize) ? 0 :486FindMatchLength(argb + i, argb + i - xsize, 0, max_len);487if (rle_len >= prev_row_len && rle_len >= MIN_LENGTH) {488VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(1, rle_len));489// We don't need to update the color cache here since it is always the490// same pixel being copied, and that does not change the color cache491// state.492i += rle_len;493} else if (prev_row_len >= MIN_LENGTH) {494VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(xsize, prev_row_len));495if (use_color_cache) {496for (k = 0; k < prev_row_len; ++k) {497VP8LColorCacheInsert(&hashers, argb[i + k]);498}499}500i += prev_row_len;501} else {502AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);503i++;504}505}506if (use_color_cache) VP8LColorCacheClear(&hashers);507return !refs->error_;508}509510static int BackwardReferencesLz77(int xsize, int ysize,511const uint32_t* const argb, int cache_bits,512const VP8LHashChain* const hash_chain,513VP8LBackwardRefs* const refs) {514int i;515int i_last_check = -1;516int ok = 0;517int cc_init = 0;518const int use_color_cache = (cache_bits > 0);519const int pix_count = xsize * ysize;520VP8LColorCache hashers;521522if (use_color_cache) {523cc_init = VP8LColorCacheInit(&hashers, cache_bits);524if (!cc_init) goto Error;525}526VP8LClearBackwardRefs(refs);527for (i = 0; i < pix_count;) {528// Alternative#1: Code the pixels starting at 'i' using backward reference.529int offset = 0;530int len = 0;531int j;532VP8LHashChainFindCopy(hash_chain, i, &offset, &len);533if (len >= MIN_LENGTH) {534const int len_ini = len;535int max_reach = 0;536const int j_max =537(i + len_ini >= pix_count) ? pix_count - 1 : i + len_ini;538// Only start from what we have not checked already.539i_last_check = (i > i_last_check) ? i : i_last_check;540// We know the best match for the current pixel but we try to find the541// best matches for the current pixel AND the next one combined.542// The naive method would use the intervals:543// [i,i+len) + [i+len, length of best match at i+len)544// while we check if we can use:545// [i,j) (where j<=i+len) + [j, length of best match at j)546for (j = i_last_check + 1; j <= j_max; ++j) {547const int len_j = VP8LHashChainFindLength(hash_chain, j);548const int reach =549j + (len_j >= MIN_LENGTH ? len_j : 1); // 1 for single literal.550if (reach > max_reach) {551len = j - i;552max_reach = reach;553if (max_reach >= pix_count) break;554}555}556} else {557len = 1;558}559// Go with literal or backward reference.560assert(len > 0);561if (len == 1) {562AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);563} else {564VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));565if (use_color_cache) {566for (j = i; j < i + len; ++j) VP8LColorCacheInsert(&hashers, argb[j]);567}568}569i += len;570}571572ok = !refs->error_;573Error:574if (cc_init) VP8LColorCacheClear(&hashers);575return ok;576}577578// Compute an LZ77 by forcing matches to happen within a given distance cost.579// We therefore limit the algorithm to the lowest 32 values in the PlaneCode580// definition.581#define WINDOW_OFFSETS_SIZE_MAX 32582static int BackwardReferencesLz77Box(int xsize, int ysize,583const uint32_t* const argb, int cache_bits,584const VP8LHashChain* const hash_chain_best,585VP8LHashChain* hash_chain,586VP8LBackwardRefs* const refs) {587int i;588const int pix_count = xsize * ysize;589uint16_t* counts;590int window_offsets[WINDOW_OFFSETS_SIZE_MAX] = {0};591int window_offsets_new[WINDOW_OFFSETS_SIZE_MAX] = {0};592int window_offsets_size = 0;593int window_offsets_new_size = 0;594uint16_t* const counts_ini =595(uint16_t*)WebPSafeMalloc(xsize * ysize, sizeof(*counts_ini));596int best_offset_prev = -1, best_length_prev = -1;597if (counts_ini == NULL) return 0;598599// counts[i] counts how many times a pixel is repeated starting at position i.600i = pix_count - 2;601counts = counts_ini + i;602counts[1] = 1;603for (; i >= 0; --i, --counts) {604if (argb[i] == argb[i + 1]) {605// Max out the counts to MAX_LENGTH.606counts[0] = counts[1] + (counts[1] != MAX_LENGTH);607} else {608counts[0] = 1;609}610}611612// Figure out the window offsets around a pixel. They are stored in a613// spiraling order around the pixel as defined by VP8LDistanceToPlaneCode.614{615int x, y;616for (y = 0; y <= 6; ++y) {617for (x = -6; x <= 6; ++x) {618const int offset = y * xsize + x;619int plane_code;620// Ignore offsets that bring us after the pixel.621if (offset <= 0) continue;622plane_code = VP8LDistanceToPlaneCode(xsize, offset) - 1;623if (plane_code >= WINDOW_OFFSETS_SIZE_MAX) continue;624window_offsets[plane_code] = offset;625}626}627// For narrow images, not all plane codes are reached, so remove those.628for (i = 0; i < WINDOW_OFFSETS_SIZE_MAX; ++i) {629if (window_offsets[i] == 0) continue;630window_offsets[window_offsets_size++] = window_offsets[i];631}632// Given a pixel P, find the offsets that reach pixels unreachable from P-1633// with any of the offsets in window_offsets[].634for (i = 0; i < window_offsets_size; ++i) {635int j;636int is_reachable = 0;637for (j = 0; j < window_offsets_size && !is_reachable; ++j) {638is_reachable |= (window_offsets[i] == window_offsets[j] + 1);639}640if (!is_reachable) {641window_offsets_new[window_offsets_new_size] = window_offsets[i];642++window_offsets_new_size;643}644}645}646647hash_chain->offset_length_[0] = 0;648for (i = 1; i < pix_count; ++i) {649int ind;650int best_length = VP8LHashChainFindLength(hash_chain_best, i);651int best_offset;652int do_compute = 1;653654if (best_length >= MAX_LENGTH) {655// Do not recompute the best match if we already have a maximal one in the656// window.657best_offset = VP8LHashChainFindOffset(hash_chain_best, i);658for (ind = 0; ind < window_offsets_size; ++ind) {659if (best_offset == window_offsets[ind]) {660do_compute = 0;661break;662}663}664}665if (do_compute) {666// Figure out if we should use the offset/length from the previous pixel667// as an initial guess and therefore only inspect the offsets in668// window_offsets_new[].669const int use_prev =670(best_length_prev > 1) && (best_length_prev < MAX_LENGTH);671const int num_ind =672use_prev ? window_offsets_new_size : window_offsets_size;673best_length = use_prev ? best_length_prev - 1 : 0;674best_offset = use_prev ? best_offset_prev : 0;675// Find the longest match in a window around the pixel.676for (ind = 0; ind < num_ind; ++ind) {677int curr_length = 0;678int j = i;679int j_offset =680use_prev ? i - window_offsets_new[ind] : i - window_offsets[ind];681if (j_offset < 0 || argb[j_offset] != argb[i]) continue;682// The longest match is the sum of how many times each pixel is683// repeated.684do {685const int counts_j_offset = counts_ini[j_offset];686const int counts_j = counts_ini[j];687if (counts_j_offset != counts_j) {688curr_length +=689(counts_j_offset < counts_j) ? counts_j_offset : counts_j;690break;691}692// The same color is repeated counts_pos times at j_offset and j.693curr_length += counts_j_offset;694j_offset += counts_j_offset;695j += counts_j_offset;696} while (curr_length <= MAX_LENGTH && j < pix_count &&697argb[j_offset] == argb[j]);698if (best_length < curr_length) {699best_offset =700use_prev ? window_offsets_new[ind] : window_offsets[ind];701if (curr_length >= MAX_LENGTH) {702best_length = MAX_LENGTH;703break;704} else {705best_length = curr_length;706}707}708}709}710711assert(i + best_length <= pix_count);712assert(best_length <= MAX_LENGTH);713if (best_length <= MIN_LENGTH) {714hash_chain->offset_length_[i] = 0;715best_offset_prev = 0;716best_length_prev = 0;717} else {718hash_chain->offset_length_[i] =719(best_offset << MAX_LENGTH_BITS) | (uint32_t)best_length;720best_offset_prev = best_offset;721best_length_prev = best_length;722}723}724hash_chain->offset_length_[0] = 0;725WebPSafeFree(counts_ini);726727return BackwardReferencesLz77(xsize, ysize, argb, cache_bits, hash_chain,728refs);729}730731// -----------------------------------------------------------------------------732733static void BackwardReferences2DLocality(int xsize,734const VP8LBackwardRefs* const refs) {735VP8LRefsCursor c = VP8LRefsCursorInit(refs);736while (VP8LRefsCursorOk(&c)) {737if (PixOrCopyIsCopy(c.cur_pos)) {738const int dist = c.cur_pos->argb_or_distance;739const int transformed_dist = VP8LDistanceToPlaneCode(xsize, dist);740c.cur_pos->argb_or_distance = transformed_dist;741}742VP8LRefsCursorNext(&c);743}744}745746// Evaluate optimal cache bits for the local color cache.747// The input *best_cache_bits sets the maximum cache bits to use (passing 0748// implies disabling the local color cache). The local color cache is also749// disabled for the lower (<= 25) quality.750// Returns 0 in case of memory error.751static int CalculateBestCacheSize(const uint32_t* argb, int quality,752const VP8LBackwardRefs* const refs,753int* const best_cache_bits) {754int i;755const int cache_bits_max = (quality <= 25) ? 0 : *best_cache_bits;756uint64_t entropy_min = WEBP_UINT64_MAX;757int cc_init[MAX_COLOR_CACHE_BITS + 1] = { 0 };758VP8LColorCache hashers[MAX_COLOR_CACHE_BITS + 1];759VP8LRefsCursor c = VP8LRefsCursorInit(refs);760VP8LHistogram* histos[MAX_COLOR_CACHE_BITS + 1] = { NULL };761int ok = 0;762763assert(cache_bits_max >= 0 && cache_bits_max <= MAX_COLOR_CACHE_BITS);764765if (cache_bits_max == 0) {766*best_cache_bits = 0;767// Local color cache is disabled.768return 1;769}770771// Allocate data.772for (i = 0; i <= cache_bits_max; ++i) {773histos[i] = VP8LAllocateHistogram(i);774if (histos[i] == NULL) goto Error;775VP8LHistogramInit(histos[i], i, /*init_arrays=*/ 1);776if (i == 0) continue;777cc_init[i] = VP8LColorCacheInit(&hashers[i], i);778if (!cc_init[i]) goto Error;779}780781// Find the cache_bits giving the lowest entropy. The search is done in a782// brute-force way as the function (entropy w.r.t cache_bits) can be783// anything in practice.784while (VP8LRefsCursorOk(&c)) {785const PixOrCopy* const v = c.cur_pos;786if (PixOrCopyIsLiteral(v)) {787const uint32_t pix = *argb++;788const uint32_t a = (pix >> 24) & 0xff;789const uint32_t r = (pix >> 16) & 0xff;790const uint32_t g = (pix >> 8) & 0xff;791const uint32_t b = (pix >> 0) & 0xff;792// The keys of the caches can be derived from the longest one.793int key = VP8LHashPix(pix, 32 - cache_bits_max);794// Do not use the color cache for cache_bits = 0.795++histos[0]->blue_[b];796++histos[0]->literal_[g];797++histos[0]->red_[r];798++histos[0]->alpha_[a];799// Deal with cache_bits > 0.800for (i = cache_bits_max; i >= 1; --i, key >>= 1) {801if (VP8LColorCacheLookup(&hashers[i], key) == pix) {802++histos[i]->literal_[NUM_LITERAL_CODES + NUM_LENGTH_CODES + key];803} else {804VP8LColorCacheSet(&hashers[i], key, pix);805++histos[i]->blue_[b];806++histos[i]->literal_[g];807++histos[i]->red_[r];808++histos[i]->alpha_[a];809}810}811} else {812int code, extra_bits, extra_bits_value;813// We should compute the contribution of the (distance,length)814// histograms but those are the same independently from the cache size.815// As those constant contributions are in the end added to the other816// histogram contributions, we can ignore them, except for the length817// prefix that is part of the literal_ histogram.818int len = PixOrCopyLength(v);819uint32_t argb_prev = *argb ^ 0xffffffffu;820VP8LPrefixEncode(len, &code, &extra_bits, &extra_bits_value);821for (i = 0; i <= cache_bits_max; ++i) {822++histos[i]->literal_[NUM_LITERAL_CODES + code];823}824// Update the color caches.825do {826if (*argb != argb_prev) {827// Efficiency: insert only if the color changes.828int key = VP8LHashPix(*argb, 32 - cache_bits_max);829for (i = cache_bits_max; i >= 1; --i, key >>= 1) {830hashers[i].colors_[key] = *argb;831}832argb_prev = *argb;833}834argb++;835} while (--len != 0);836}837VP8LRefsCursorNext(&c);838}839840for (i = 0; i <= cache_bits_max; ++i) {841const uint64_t entropy = VP8LHistogramEstimateBits(histos[i]);842if (i == 0 || entropy < entropy_min) {843entropy_min = entropy;844*best_cache_bits = i;845}846}847ok = 1;848Error:849for (i = 0; i <= cache_bits_max; ++i) {850if (cc_init[i]) VP8LColorCacheClear(&hashers[i]);851VP8LFreeHistogram(histos[i]);852}853return ok;854}855856// Update (in-place) backward references for specified cache_bits.857static int BackwardRefsWithLocalCache(const uint32_t* const argb,858int cache_bits,859VP8LBackwardRefs* const refs) {860int pixel_index = 0;861VP8LColorCache hashers;862VP8LRefsCursor c = VP8LRefsCursorInit(refs);863if (!VP8LColorCacheInit(&hashers, cache_bits)) return 0;864865while (VP8LRefsCursorOk(&c)) {866PixOrCopy* const v = c.cur_pos;867if (PixOrCopyIsLiteral(v)) {868const uint32_t argb_literal = v->argb_or_distance;869const int ix = VP8LColorCacheContains(&hashers, argb_literal);870if (ix >= 0) {871// hashers contains argb_literal872*v = PixOrCopyCreateCacheIdx(ix);873} else {874VP8LColorCacheInsert(&hashers, argb_literal);875}876++pixel_index;877} else {878// refs was created without local cache, so it can not have cache indexes.879int k;880assert(PixOrCopyIsCopy(v));881for (k = 0; k < v->len; ++k) {882VP8LColorCacheInsert(&hashers, argb[pixel_index++]);883}884}885VP8LRefsCursorNext(&c);886}887VP8LColorCacheClear(&hashers);888return 1;889}890891static VP8LBackwardRefs* GetBackwardReferencesLowEffort(892int width, int height, const uint32_t* const argb,893int* const cache_bits, const VP8LHashChain* const hash_chain,894VP8LBackwardRefs* const refs_lz77) {895*cache_bits = 0;896if (!BackwardReferencesLz77(width, height, argb, 0, hash_chain, refs_lz77)) {897return NULL;898}899BackwardReferences2DLocality(width, refs_lz77);900return refs_lz77;901}902903extern int VP8LBackwardReferencesTraceBackwards(904int xsize, int ysize, const uint32_t* const argb, int cache_bits,905const VP8LHashChain* const hash_chain,906const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst);907static int GetBackwardReferences(int width, int height,908const uint32_t* const argb, int quality,909int lz77_types_to_try, int cache_bits_max,910int do_no_cache,911const VP8LHashChain* const hash_chain,912VP8LBackwardRefs* const refs,913int* const cache_bits_best) {914VP8LHistogram* histo = NULL;915int i, lz77_type;916// Index 0 is for a color cache, index 1 for no cache (if needed).917int lz77_types_best[2] = {0, 0};918uint64_t bit_costs_best[2] = {WEBP_UINT64_MAX, WEBP_UINT64_MAX};919VP8LHashChain hash_chain_box;920VP8LBackwardRefs* const refs_tmp = &refs[do_no_cache ? 2 : 1];921int status = 0;922memset(&hash_chain_box, 0, sizeof(hash_chain_box));923924histo = VP8LAllocateHistogram(MAX_COLOR_CACHE_BITS);925if (histo == NULL) goto Error;926927for (lz77_type = 1; lz77_types_to_try;928lz77_types_to_try &= ~lz77_type, lz77_type <<= 1) {929int res = 0;930uint64_t bit_cost = 0u;931if ((lz77_types_to_try & lz77_type) == 0) continue;932switch (lz77_type) {933case kLZ77RLE:934res = BackwardReferencesRle(width, height, argb, 0, refs_tmp);935break;936case kLZ77Standard:937// Compute LZ77 with no cache (0 bits), as the ideal LZ77 with a color938// cache is not that different in practice.939res = BackwardReferencesLz77(width, height, argb, 0, hash_chain,940refs_tmp);941break;942case kLZ77Box:943if (!VP8LHashChainInit(&hash_chain_box, width * height)) goto Error;944res = BackwardReferencesLz77Box(width, height, argb, 0, hash_chain,945&hash_chain_box, refs_tmp);946break;947default:948assert(0);949}950if (!res) goto Error;951952// Start with the no color cache case.953for (i = 1; i >= 0; --i) {954int cache_bits = (i == 1) ? 0 : cache_bits_max;955956if (i == 1 && !do_no_cache) continue;957958if (i == 0) {959// Try with a color cache.960if (!CalculateBestCacheSize(argb, quality, refs_tmp, &cache_bits)) {961goto Error;962}963if (cache_bits > 0) {964if (!BackwardRefsWithLocalCache(argb, cache_bits, refs_tmp)) {965goto Error;966}967}968}969970if (i == 0 && do_no_cache && cache_bits == 0) {971// No need to re-compute bit_cost as it was computed at i == 1.972} else {973VP8LHistogramCreate(histo, refs_tmp, cache_bits);974bit_cost = VP8LHistogramEstimateBits(histo);975}976977if (bit_cost < bit_costs_best[i]) {978if (i == 1) {979// Do not swap as the full cache analysis would have the wrong980// VP8LBackwardRefs to start with.981if (!BackwardRefsClone(refs_tmp, &refs[1])) goto Error;982} else {983BackwardRefsSwap(refs_tmp, &refs[0]);984}985bit_costs_best[i] = bit_cost;986lz77_types_best[i] = lz77_type;987if (i == 0) *cache_bits_best = cache_bits;988}989}990}991assert(lz77_types_best[0] > 0);992assert(!do_no_cache || lz77_types_best[1] > 0);993994// Improve on simple LZ77 but only for high quality (TraceBackwards is995// costly).996for (i = 1; i >= 0; --i) {997if (i == 1 && !do_no_cache) continue;998if ((lz77_types_best[i] == kLZ77Standard ||999lz77_types_best[i] == kLZ77Box) &&1000quality >= 25) {1001const VP8LHashChain* const hash_chain_tmp =1002(lz77_types_best[i] == kLZ77Standard) ? hash_chain : &hash_chain_box;1003const int cache_bits = (i == 1) ? 0 : *cache_bits_best;1004uint64_t bit_cost_trace;1005if (!VP8LBackwardReferencesTraceBackwards(width, height, argb, cache_bits,1006hash_chain_tmp, &refs[i],1007refs_tmp)) {1008goto Error;1009}1010VP8LHistogramCreate(histo, refs_tmp, cache_bits);1011bit_cost_trace = VP8LHistogramEstimateBits(histo);1012if (bit_cost_trace < bit_costs_best[i]) {1013BackwardRefsSwap(refs_tmp, &refs[i]);1014}1015}10161017BackwardReferences2DLocality(width, &refs[i]);10181019if (i == 1 && lz77_types_best[0] == lz77_types_best[1] &&1020*cache_bits_best == 0) {1021// If the best cache size is 0 and we have the same best LZ77, just copy1022// the data over and stop here.1023if (!BackwardRefsClone(&refs[1], &refs[0])) goto Error;1024break;1025}1026}1027status = 1;10281029Error:1030VP8LHashChainClear(&hash_chain_box);1031VP8LFreeHistogram(histo);1032return status;1033}10341035int VP8LGetBackwardReferences(1036int width, int height, const uint32_t* const argb, int quality,1037int low_effort, int lz77_types_to_try, int cache_bits_max, int do_no_cache,1038const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs,1039int* const cache_bits_best, const WebPPicture* const pic, int percent_range,1040int* const percent) {1041if (low_effort) {1042VP8LBackwardRefs* refs_best;1043*cache_bits_best = cache_bits_max;1044refs_best = GetBackwardReferencesLowEffort(1045width, height, argb, cache_bits_best, hash_chain, refs);1046if (refs_best == NULL) {1047return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);1048}1049// Set it in first position.1050BackwardRefsSwap(refs_best, &refs[0]);1051} else {1052if (!GetBackwardReferences(width, height, argb, quality, lz77_types_to_try,1053cache_bits_max, do_no_cache, hash_chain, refs,1054cache_bits_best)) {1055return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);1056}1057}10581059return WebPReportProgress(pic, *percent + percent_range, percent);1060}106110621063