Path: blob/master/3rdparty/libwebp/src/enc/backward_references_enc.c
16349 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 <assert.h>13#include <math.h>1415#include "src/enc/backward_references_enc.h"16#include "src/enc/histogram_enc.h"17#include "src/dsp/lossless.h"18#include "src/dsp/lossless_common.h"19#include "src/dsp/dsp.h"20#include "src/utils/color_cache_utils.h"21#include "src/utils/utils.h"2223#define MIN_BLOCK_SIZE 256 // minimum block size for backward references2425#define MAX_ENTROPY (1e30f)2627// 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}104105void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size) {106assert(refs != NULL);107memset(refs, 0, sizeof(*refs));108refs->tail_ = &refs->refs_;109refs->block_size_ =110(block_size < MIN_BLOCK_SIZE) ? MIN_BLOCK_SIZE : block_size;111}112113VP8LRefsCursor VP8LRefsCursorInit(const VP8LBackwardRefs* const refs) {114VP8LRefsCursor c;115c.cur_block_ = refs->refs_;116if (refs->refs_ != NULL) {117c.cur_pos = c.cur_block_->start_;118c.last_pos_ = c.cur_pos + c.cur_block_->size_;119} else {120c.cur_pos = NULL;121c.last_pos_ = NULL;122}123return c;124}125126void VP8LRefsCursorNextBlock(VP8LRefsCursor* const c) {127PixOrCopyBlock* const b = c->cur_block_->next_;128c->cur_pos = (b == NULL) ? NULL : b->start_;129c->last_pos_ = (b == NULL) ? NULL : b->start_ + b->size_;130c->cur_block_ = b;131}132133// Create a new block, either from the free list or allocated134static PixOrCopyBlock* BackwardRefsNewBlock(VP8LBackwardRefs* const refs) {135PixOrCopyBlock* b = refs->free_blocks_;136if (b == NULL) { // allocate new memory chunk137const size_t total_size =138sizeof(*b) + refs->block_size_ * sizeof(*b->start_);139b = (PixOrCopyBlock*)WebPSafeMalloc(1ULL, total_size);140if (b == NULL) {141refs->error_ |= 1;142return NULL;143}144b->start_ = (PixOrCopy*)((uint8_t*)b + sizeof(*b)); // not always aligned145} else { // recycle from free-list146refs->free_blocks_ = b->next_;147}148*refs->tail_ = b;149refs->tail_ = &b->next_;150refs->last_block_ = b;151b->next_ = NULL;152b->size_ = 0;153return b;154}155156extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,157const PixOrCopy v);158void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,159const PixOrCopy v) {160PixOrCopyBlock* b = refs->last_block_;161if (b == NULL || b->size_ == refs->block_size_) {162b = BackwardRefsNewBlock(refs);163if (b == NULL) return; // refs->error_ is set164}165b->start_[b->size_++] = v;166}167168// -----------------------------------------------------------------------------169// Hash chains170171int VP8LHashChainInit(VP8LHashChain* const p, int size) {172assert(p->size_ == 0);173assert(p->offset_length_ == NULL);174assert(size > 0);175p->offset_length_ =176(uint32_t*)WebPSafeMalloc(size, sizeof(*p->offset_length_));177if (p->offset_length_ == NULL) return 0;178p->size_ = size;179180return 1;181}182183void VP8LHashChainClear(VP8LHashChain* const p) {184assert(p != NULL);185WebPSafeFree(p->offset_length_);186187p->size_ = 0;188p->offset_length_ = NULL;189}190191// -----------------------------------------------------------------------------192193#define HASH_MULTIPLIER_HI (0xc6a4a793ULL)194#define HASH_MULTIPLIER_LO (0x5bd1e996ULL)195196static WEBP_INLINE uint32_t GetPixPairHash64(const uint32_t* const argb) {197uint32_t key;198key = (argb[1] * HASH_MULTIPLIER_HI) & 0xffffffffu;199key += (argb[0] * HASH_MULTIPLIER_LO) & 0xffffffffu;200key = key >> (32 - HASH_BITS);201return key;202}203204// Returns the maximum number of hash chain lookups to do for a205// given compression quality. Return value in range [8, 86].206static int GetMaxItersForQuality(int quality) {207return 8 + (quality * quality) / 128;208}209210static int GetWindowSizeForHashChain(int quality, int xsize) {211const int max_window_size = (quality > 75) ? WINDOW_SIZE212: (quality > 50) ? (xsize << 8)213: (quality > 25) ? (xsize << 6)214: (xsize << 4);215assert(xsize > 0);216return (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE : max_window_size;217}218219static WEBP_INLINE int MaxFindCopyLength(int len) {220return (len < MAX_LENGTH) ? len : MAX_LENGTH;221}222223int VP8LHashChainFill(VP8LHashChain* const p, int quality,224const uint32_t* const argb, int xsize, int ysize,225int low_effort) {226const int size = xsize * ysize;227const int iter_max = GetMaxItersForQuality(quality);228const uint32_t window_size = GetWindowSizeForHashChain(quality, xsize);229int pos;230int argb_comp;231uint32_t base_position;232int32_t* hash_to_first_index;233// Temporarily use the p->offset_length_ as a hash chain.234int32_t* chain = (int32_t*)p->offset_length_;235assert(size > 0);236assert(p->size_ != 0);237assert(p->offset_length_ != NULL);238239if (size <= 2) {240p->offset_length_[0] = p->offset_length_[size - 1] = 0;241return 1;242}243244hash_to_first_index =245(int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index));246if (hash_to_first_index == NULL) return 0;247248// Set the int32_t array to -1.249memset(hash_to_first_index, 0xff, HASH_SIZE * sizeof(*hash_to_first_index));250// Fill the chain linking pixels with the same hash.251argb_comp = (argb[0] == argb[1]);252for (pos = 0; pos < size - 2;) {253uint32_t hash_code;254const int argb_comp_next = (argb[pos + 1] == argb[pos + 2]);255if (argb_comp && argb_comp_next) {256// Consecutive pixels with the same color will share the same hash.257// We therefore use a different hash: the color and its repetition258// length.259uint32_t tmp[2];260uint32_t len = 1;261tmp[0] = argb[pos];262// Figure out how far the pixels are the same.263// The last pixel has a different 64 bit hash, as its next pixel does264// not have the same color, so we just need to get to the last pixel equal265// to its follower.266while (pos + (int)len + 2 < size && argb[pos + len + 2] == argb[pos]) {267++len;268}269if (len > MAX_LENGTH) {270// Skip the pixels that match for distance=1 and length>MAX_LENGTH271// because they are linked to their predecessor and we automatically272// check that in the main for loop below. Skipping means setting no273// predecessor in the chain, hence -1.274memset(chain + pos, 0xff, (len - MAX_LENGTH) * sizeof(*chain));275pos += len - MAX_LENGTH;276len = MAX_LENGTH;277}278// Process the rest of the hash chain.279while (len) {280tmp[1] = len--;281hash_code = GetPixPairHash64(tmp);282chain[pos] = hash_to_first_index[hash_code];283hash_to_first_index[hash_code] = pos++;284}285argb_comp = 0;286} else {287// Just move one pixel forward.288hash_code = GetPixPairHash64(argb + pos);289chain[pos] = hash_to_first_index[hash_code];290hash_to_first_index[hash_code] = pos++;291argb_comp = argb_comp_next;292}293}294// Process the penultimate pixel.295chain[pos] = hash_to_first_index[GetPixPairHash64(argb + pos)];296297WebPSafeFree(hash_to_first_index);298299// Find the best match interval at each pixel, defined by an offset to the300// pixel and a length. The right-most pixel cannot match anything to the right301// (hence a best length of 0) and the left-most pixel nothing to the left302// (hence an offset of 0).303assert(size > 2);304p->offset_length_[0] = p->offset_length_[size - 1] = 0;305for (base_position = size - 2; base_position > 0;) {306const int max_len = MaxFindCopyLength(size - 1 - base_position);307const uint32_t* const argb_start = argb + base_position;308int iter = iter_max;309int best_length = 0;310uint32_t best_distance = 0;311uint32_t best_argb;312const int min_pos =313(base_position > window_size) ? base_position - window_size : 0;314const int length_max = (max_len < 256) ? max_len : 256;315uint32_t max_base_position;316317pos = chain[base_position];318if (!low_effort) {319int curr_length;320// Heuristic: use the comparison with the above line as an initialization.321if (base_position >= (uint32_t)xsize) {322curr_length = FindMatchLength(argb_start - xsize, argb_start,323best_length, max_len);324if (curr_length > best_length) {325best_length = curr_length;326best_distance = xsize;327}328--iter;329}330// Heuristic: compare to the previous pixel.331curr_length =332FindMatchLength(argb_start - 1, argb_start, best_length, max_len);333if (curr_length > best_length) {334best_length = curr_length;335best_distance = 1;336}337--iter;338// Skip the for loop if we already have the maximum.339if (best_length == MAX_LENGTH) pos = min_pos - 1;340}341best_argb = argb_start[best_length];342343for (; pos >= min_pos && --iter; pos = chain[pos]) {344int curr_length;345assert(base_position > (uint32_t)pos);346347if (argb[pos + best_length] != best_argb) continue;348349curr_length = VP8LVectorMismatch(argb + pos, argb_start, max_len);350if (best_length < curr_length) {351best_length = curr_length;352best_distance = base_position - pos;353best_argb = argb_start[best_length];354// Stop if we have reached a good enough length.355if (best_length >= length_max) break;356}357}358// We have the best match but in case the two intervals continue matching359// to the left, we have the best matches for the left-extended pixels.360max_base_position = base_position;361while (1) {362assert(best_length <= MAX_LENGTH);363assert(best_distance <= WINDOW_SIZE);364p->offset_length_[base_position] =365(best_distance << MAX_LENGTH_BITS) | (uint32_t)best_length;366--base_position;367// Stop if we don't have a match or if we are out of bounds.368if (best_distance == 0 || base_position == 0) break;369// Stop if we cannot extend the matching intervals to the left.370if (base_position < best_distance ||371argb[base_position - best_distance] != argb[base_position]) {372break;373}374// Stop if we are matching at its limit because there could be a closer375// matching interval with the same maximum length. Then again, if the376// matching interval is as close as possible (best_distance == 1), we will377// never find anything better so let's continue.378if (best_length == MAX_LENGTH && best_distance != 1 &&379base_position + MAX_LENGTH < max_base_position) {380break;381}382if (best_length < MAX_LENGTH) {383++best_length;384max_base_position = base_position;385}386}387}388return 1;389}390391static WEBP_INLINE void AddSingleLiteral(uint32_t pixel, int use_color_cache,392VP8LColorCache* const hashers,393VP8LBackwardRefs* const refs) {394PixOrCopy v;395if (use_color_cache) {396const uint32_t key = VP8LColorCacheGetIndex(hashers, pixel);397if (VP8LColorCacheLookup(hashers, key) == pixel) {398v = PixOrCopyCreateCacheIdx(key);399} else {400v = PixOrCopyCreateLiteral(pixel);401VP8LColorCacheSet(hashers, key, pixel);402}403} else {404v = PixOrCopyCreateLiteral(pixel);405}406VP8LBackwardRefsCursorAdd(refs, v);407}408409static int BackwardReferencesRle(int xsize, int ysize,410const uint32_t* const argb,411int cache_bits, VP8LBackwardRefs* const refs) {412const int pix_count = xsize * ysize;413int i, k;414const int use_color_cache = (cache_bits > 0);415VP8LColorCache hashers;416417if (use_color_cache && !VP8LColorCacheInit(&hashers, cache_bits)) {418return 0;419}420VP8LClearBackwardRefs(refs);421// Add first pixel as literal.422AddSingleLiteral(argb[0], use_color_cache, &hashers, refs);423i = 1;424while (i < pix_count) {425const int max_len = MaxFindCopyLength(pix_count - i);426const int rle_len = FindMatchLength(argb + i, argb + i - 1, 0, max_len);427const int prev_row_len = (i < xsize) ? 0 :428FindMatchLength(argb + i, argb + i - xsize, 0, max_len);429if (rle_len >= prev_row_len && rle_len >= MIN_LENGTH) {430VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(1, rle_len));431// We don't need to update the color cache here since it is always the432// same pixel being copied, and that does not change the color cache433// state.434i += rle_len;435} else if (prev_row_len >= MIN_LENGTH) {436VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(xsize, prev_row_len));437if (use_color_cache) {438for (k = 0; k < prev_row_len; ++k) {439VP8LColorCacheInsert(&hashers, argb[i + k]);440}441}442i += prev_row_len;443} else {444AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);445i++;446}447}448if (use_color_cache) VP8LColorCacheClear(&hashers);449return !refs->error_;450}451452static int BackwardReferencesLz77(int xsize, int ysize,453const uint32_t* const argb, int cache_bits,454const VP8LHashChain* const hash_chain,455VP8LBackwardRefs* const refs) {456int i;457int i_last_check = -1;458int ok = 0;459int cc_init = 0;460const int use_color_cache = (cache_bits > 0);461const int pix_count = xsize * ysize;462VP8LColorCache hashers;463464if (use_color_cache) {465cc_init = VP8LColorCacheInit(&hashers, cache_bits);466if (!cc_init) goto Error;467}468VP8LClearBackwardRefs(refs);469for (i = 0; i < pix_count;) {470// Alternative#1: Code the pixels starting at 'i' using backward reference.471int offset = 0;472int len = 0;473int j;474VP8LHashChainFindCopy(hash_chain, i, &offset, &len);475if (len >= MIN_LENGTH) {476const int len_ini = len;477int max_reach = 0;478const int j_max =479(i + len_ini >= pix_count) ? pix_count - 1 : i + len_ini;480// Only start from what we have not checked already.481i_last_check = (i > i_last_check) ? i : i_last_check;482// We know the best match for the current pixel but we try to find the483// best matches for the current pixel AND the next one combined.484// The naive method would use the intervals:485// [i,i+len) + [i+len, length of best match at i+len)486// while we check if we can use:487// [i,j) (where j<=i+len) + [j, length of best match at j)488for (j = i_last_check + 1; j <= j_max; ++j) {489const int len_j = VP8LHashChainFindLength(hash_chain, j);490const int reach =491j + (len_j >= MIN_LENGTH ? len_j : 1); // 1 for single literal.492if (reach > max_reach) {493len = j - i;494max_reach = reach;495if (max_reach >= pix_count) break;496}497}498} else {499len = 1;500}501// Go with literal or backward reference.502assert(len > 0);503if (len == 1) {504AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);505} else {506VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));507if (use_color_cache) {508for (j = i; j < i + len; ++j) VP8LColorCacheInsert(&hashers, argb[j]);509}510}511i += len;512}513514ok = !refs->error_;515Error:516if (cc_init) VP8LColorCacheClear(&hashers);517return ok;518}519520// Compute an LZ77 by forcing matches to happen within a given distance cost.521// We therefore limit the algorithm to the lowest 32 values in the PlaneCode522// definition.523#define WINDOW_OFFSETS_SIZE_MAX 32524static int BackwardReferencesLz77Box(int xsize, int ysize,525const uint32_t* const argb, int cache_bits,526const VP8LHashChain* const hash_chain_best,527VP8LHashChain* hash_chain,528VP8LBackwardRefs* const refs) {529int i;530const int pix_count = xsize * ysize;531uint16_t* counts;532int window_offsets[WINDOW_OFFSETS_SIZE_MAX] = {0};533int window_offsets_new[WINDOW_OFFSETS_SIZE_MAX] = {0};534int window_offsets_size = 0;535int window_offsets_new_size = 0;536uint16_t* const counts_ini =537(uint16_t*)WebPSafeMalloc(xsize * ysize, sizeof(*counts_ini));538int best_offset_prev = -1, best_length_prev = -1;539if (counts_ini == NULL) return 0;540541// counts[i] counts how many times a pixel is repeated starting at position i.542i = pix_count - 2;543counts = counts_ini + i;544counts[1] = 1;545for (; i >= 0; --i, --counts) {546if (argb[i] == argb[i + 1]) {547// Max out the counts to MAX_LENGTH.548counts[0] = counts[1] + (counts[1] != MAX_LENGTH);549} else {550counts[0] = 1;551}552}553554// Figure out the window offsets around a pixel. They are stored in a555// spiraling order around the pixel as defined by VP8LDistanceToPlaneCode.556{557int x, y;558for (y = 0; y <= 6; ++y) {559for (x = -6; x <= 6; ++x) {560const int offset = y * xsize + x;561int plane_code;562// Ignore offsets that bring us after the pixel.563if (offset <= 0) continue;564plane_code = VP8LDistanceToPlaneCode(xsize, offset) - 1;565if (plane_code >= WINDOW_OFFSETS_SIZE_MAX) continue;566window_offsets[plane_code] = offset;567}568}569// For narrow images, not all plane codes are reached, so remove those.570for (i = 0; i < WINDOW_OFFSETS_SIZE_MAX; ++i) {571if (window_offsets[i] == 0) continue;572window_offsets[window_offsets_size++] = window_offsets[i];573}574// Given a pixel P, find the offsets that reach pixels unreachable from P-1575// with any of the offsets in window_offsets[].576for (i = 0; i < window_offsets_size; ++i) {577int j;578int is_reachable = 0;579for (j = 0; j < window_offsets_size && !is_reachable; ++j) {580is_reachable |= (window_offsets[i] == window_offsets[j] + 1);581}582if (!is_reachable) {583window_offsets_new[window_offsets_new_size] = window_offsets[i];584++window_offsets_new_size;585}586}587}588589hash_chain->offset_length_[0] = 0;590for (i = 1; i < pix_count; ++i) {591int ind;592int best_length = VP8LHashChainFindLength(hash_chain_best, i);593int best_offset;594int do_compute = 1;595596if (best_length >= MAX_LENGTH) {597// Do not recompute the best match if we already have a maximal one in the598// window.599best_offset = VP8LHashChainFindOffset(hash_chain_best, i);600for (ind = 0; ind < window_offsets_size; ++ind) {601if (best_offset == window_offsets[ind]) {602do_compute = 0;603break;604}605}606}607if (do_compute) {608// Figure out if we should use the offset/length from the previous pixel609// as an initial guess and therefore only inspect the offsets in610// window_offsets_new[].611const int use_prev =612(best_length_prev > 1) && (best_length_prev < MAX_LENGTH);613const int num_ind =614use_prev ? window_offsets_new_size : window_offsets_size;615best_length = use_prev ? best_length_prev - 1 : 0;616best_offset = use_prev ? best_offset_prev : 0;617// Find the longest match in a window around the pixel.618for (ind = 0; ind < num_ind; ++ind) {619int curr_length = 0;620int j = i;621int j_offset =622use_prev ? i - window_offsets_new[ind] : i - window_offsets[ind];623if (j_offset < 0 || argb[j_offset] != argb[i]) continue;624// The longest match is the sum of how many times each pixel is625// repeated.626do {627const int counts_j_offset = counts_ini[j_offset];628const int counts_j = counts_ini[j];629if (counts_j_offset != counts_j) {630curr_length +=631(counts_j_offset < counts_j) ? counts_j_offset : counts_j;632break;633}634// The same color is repeated counts_pos times at j_offset and j.635curr_length += counts_j_offset;636j_offset += counts_j_offset;637j += counts_j_offset;638} while (curr_length <= MAX_LENGTH && j < pix_count &&639argb[j_offset] == argb[j]);640if (best_length < curr_length) {641best_offset =642use_prev ? window_offsets_new[ind] : window_offsets[ind];643if (curr_length >= MAX_LENGTH) {644best_length = MAX_LENGTH;645break;646} else {647best_length = curr_length;648}649}650}651}652653assert(i + best_length <= pix_count);654assert(best_length <= MAX_LENGTH);655if (best_length <= MIN_LENGTH) {656hash_chain->offset_length_[i] = 0;657best_offset_prev = 0;658best_length_prev = 0;659} else {660hash_chain->offset_length_[i] =661(best_offset << MAX_LENGTH_BITS) | (uint32_t)best_length;662best_offset_prev = best_offset;663best_length_prev = best_length;664}665}666hash_chain->offset_length_[0] = 0;667WebPSafeFree(counts_ini);668669return BackwardReferencesLz77(xsize, ysize, argb, cache_bits, hash_chain,670refs);671}672673// -----------------------------------------------------------------------------674675static void BackwardReferences2DLocality(int xsize,676const VP8LBackwardRefs* const refs) {677VP8LRefsCursor c = VP8LRefsCursorInit(refs);678while (VP8LRefsCursorOk(&c)) {679if (PixOrCopyIsCopy(c.cur_pos)) {680const int dist = c.cur_pos->argb_or_distance;681const int transformed_dist = VP8LDistanceToPlaneCode(xsize, dist);682c.cur_pos->argb_or_distance = transformed_dist;683}684VP8LRefsCursorNext(&c);685}686}687688// Evaluate optimal cache bits for the local color cache.689// The input *best_cache_bits sets the maximum cache bits to use (passing 0690// implies disabling the local color cache). The local color cache is also691// disabled for the lower (<= 25) quality.692// Returns 0 in case of memory error.693static int CalculateBestCacheSize(const uint32_t* argb, int quality,694const VP8LBackwardRefs* const refs,695int* const best_cache_bits) {696int i;697const int cache_bits_max = (quality <= 25) ? 0 : *best_cache_bits;698double entropy_min = MAX_ENTROPY;699int cc_init[MAX_COLOR_CACHE_BITS + 1] = { 0 };700VP8LColorCache hashers[MAX_COLOR_CACHE_BITS + 1];701VP8LRefsCursor c = VP8LRefsCursorInit(refs);702VP8LHistogram* histos[MAX_COLOR_CACHE_BITS + 1] = { NULL };703int ok = 0;704705assert(cache_bits_max >= 0 && cache_bits_max <= MAX_COLOR_CACHE_BITS);706707if (cache_bits_max == 0) {708*best_cache_bits = 0;709// Local color cache is disabled.710return 1;711}712713// Allocate data.714for (i = 0; i <= cache_bits_max; ++i) {715histos[i] = VP8LAllocateHistogram(i);716if (histos[i] == NULL) goto Error;717if (i == 0) continue;718cc_init[i] = VP8LColorCacheInit(&hashers[i], i);719if (!cc_init[i]) goto Error;720}721722// Find the cache_bits giving the lowest entropy. The search is done in a723// brute-force way as the function (entropy w.r.t cache_bits) can be724// anything in practice.725while (VP8LRefsCursorOk(&c)) {726const PixOrCopy* const v = c.cur_pos;727if (PixOrCopyIsLiteral(v)) {728const uint32_t pix = *argb++;729const uint32_t a = (pix >> 24) & 0xff;730const uint32_t r = (pix >> 16) & 0xff;731const uint32_t g = (pix >> 8) & 0xff;732const uint32_t b = (pix >> 0) & 0xff;733// The keys of the caches can be derived from the longest one.734int key = VP8LHashPix(pix, 32 - cache_bits_max);735// Do not use the color cache for cache_bits = 0.736++histos[0]->blue_[b];737++histos[0]->literal_[g];738++histos[0]->red_[r];739++histos[0]->alpha_[a];740// Deal with cache_bits > 0.741for (i = cache_bits_max; i >= 1; --i, key >>= 1) {742if (VP8LColorCacheLookup(&hashers[i], key) == pix) {743++histos[i]->literal_[NUM_LITERAL_CODES + NUM_LENGTH_CODES + key];744} else {745VP8LColorCacheSet(&hashers[i], key, pix);746++histos[i]->blue_[b];747++histos[i]->literal_[g];748++histos[i]->red_[r];749++histos[i]->alpha_[a];750}751}752} else {753// We should compute the contribution of the (distance,length)754// histograms but those are the same independently from the cache size.755// As those constant contributions are in the end added to the other756// histogram contributions, we can safely ignore them.757int len = PixOrCopyLength(v);758uint32_t argb_prev = *argb ^ 0xffffffffu;759// Update the color caches.760do {761if (*argb != argb_prev) {762// Efficiency: insert only if the color changes.763int key = VP8LHashPix(*argb, 32 - cache_bits_max);764for (i = cache_bits_max; i >= 1; --i, key >>= 1) {765hashers[i].colors_[key] = *argb;766}767argb_prev = *argb;768}769argb++;770} while (--len != 0);771}772VP8LRefsCursorNext(&c);773}774775for (i = 0; i <= cache_bits_max; ++i) {776const double entropy = VP8LHistogramEstimateBits(histos[i]);777if (i == 0 || entropy < entropy_min) {778entropy_min = entropy;779*best_cache_bits = i;780}781}782ok = 1;783Error:784for (i = 0; i <= cache_bits_max; ++i) {785if (cc_init[i]) VP8LColorCacheClear(&hashers[i]);786VP8LFreeHistogram(histos[i]);787}788return ok;789}790791// Update (in-place) backward references for specified cache_bits.792static int BackwardRefsWithLocalCache(const uint32_t* const argb,793int cache_bits,794VP8LBackwardRefs* const refs) {795int pixel_index = 0;796VP8LColorCache hashers;797VP8LRefsCursor c = VP8LRefsCursorInit(refs);798if (!VP8LColorCacheInit(&hashers, cache_bits)) return 0;799800while (VP8LRefsCursorOk(&c)) {801PixOrCopy* const v = c.cur_pos;802if (PixOrCopyIsLiteral(v)) {803const uint32_t argb_literal = v->argb_or_distance;804const int ix = VP8LColorCacheContains(&hashers, argb_literal);805if (ix >= 0) {806// hashers contains argb_literal807*v = PixOrCopyCreateCacheIdx(ix);808} else {809VP8LColorCacheInsert(&hashers, argb_literal);810}811++pixel_index;812} else {813// refs was created without local cache, so it can not have cache indexes.814int k;815assert(PixOrCopyIsCopy(v));816for (k = 0; k < v->len; ++k) {817VP8LColorCacheInsert(&hashers, argb[pixel_index++]);818}819}820VP8LRefsCursorNext(&c);821}822VP8LColorCacheClear(&hashers);823return 1;824}825826static VP8LBackwardRefs* GetBackwardReferencesLowEffort(827int width, int height, const uint32_t* const argb,828int* const cache_bits, const VP8LHashChain* const hash_chain,829VP8LBackwardRefs* const refs_lz77) {830*cache_bits = 0;831if (!BackwardReferencesLz77(width, height, argb, 0, hash_chain, refs_lz77)) {832return NULL;833}834BackwardReferences2DLocality(width, refs_lz77);835return refs_lz77;836}837838extern int VP8LBackwardReferencesTraceBackwards(839int xsize, int ysize, const uint32_t* const argb, int cache_bits,840const VP8LHashChain* const hash_chain,841const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst);842static VP8LBackwardRefs* GetBackwardReferences(843int width, int height, const uint32_t* const argb, int quality,844int lz77_types_to_try, int* const cache_bits,845const VP8LHashChain* const hash_chain, VP8LBackwardRefs* best,846VP8LBackwardRefs* worst) {847const int cache_bits_initial = *cache_bits;848double bit_cost_best = -1;849VP8LHistogram* histo = NULL;850int lz77_type, lz77_type_best = 0;851VP8LHashChain hash_chain_box;852memset(&hash_chain_box, 0, sizeof(hash_chain_box));853854histo = VP8LAllocateHistogram(MAX_COLOR_CACHE_BITS);855if (histo == NULL) goto Error;856857for (lz77_type = 1; lz77_types_to_try;858lz77_types_to_try &= ~lz77_type, lz77_type <<= 1) {859int res = 0;860double bit_cost;861int cache_bits_tmp = cache_bits_initial;862if ((lz77_types_to_try & lz77_type) == 0) continue;863switch (lz77_type) {864case kLZ77RLE:865res = BackwardReferencesRle(width, height, argb, 0, worst);866break;867case kLZ77Standard:868// Compute LZ77 with no cache (0 bits), as the ideal LZ77 with a color869// cache is not that different in practice.870res = BackwardReferencesLz77(width, height, argb, 0, hash_chain, worst);871break;872case kLZ77Box:873if (!VP8LHashChainInit(&hash_chain_box, width * height)) goto Error;874res = BackwardReferencesLz77Box(width, height, argb, 0, hash_chain,875&hash_chain_box, worst);876break;877default:878assert(0);879}880if (!res) goto Error;881882// Next, try with a color cache and update the references.883if (!CalculateBestCacheSize(argb, quality, worst, &cache_bits_tmp)) {884goto Error;885}886if (cache_bits_tmp > 0) {887if (!BackwardRefsWithLocalCache(argb, cache_bits_tmp, worst)) {888goto Error;889}890}891892// Keep the best backward references.893VP8LHistogramCreate(histo, worst, cache_bits_tmp);894bit_cost = VP8LHistogramEstimateBits(histo);895if (lz77_type_best == 0 || bit_cost < bit_cost_best) {896VP8LBackwardRefs* const tmp = worst;897worst = best;898best = tmp;899bit_cost_best = bit_cost;900*cache_bits = cache_bits_tmp;901lz77_type_best = lz77_type;902}903}904assert(lz77_type_best > 0);905906// Improve on simple LZ77 but only for high quality (TraceBackwards is907// costly).908if ((lz77_type_best == kLZ77Standard || lz77_type_best == kLZ77Box) &&909quality >= 25) {910const VP8LHashChain* const hash_chain_tmp =911(lz77_type_best == kLZ77Standard) ? hash_chain : &hash_chain_box;912if (VP8LBackwardReferencesTraceBackwards(width, height, argb, *cache_bits,913hash_chain_tmp, best, worst)) {914double bit_cost_trace;915VP8LHistogramCreate(histo, worst, *cache_bits);916bit_cost_trace = VP8LHistogramEstimateBits(histo);917if (bit_cost_trace < bit_cost_best) best = worst;918}919}920921BackwardReferences2DLocality(width, best);922923Error:924VP8LHashChainClear(&hash_chain_box);925VP8LFreeHistogram(histo);926return best;927}928929VP8LBackwardRefs* VP8LGetBackwardReferences(930int width, int height, const uint32_t* const argb, int quality,931int low_effort, int lz77_types_to_try, int* const cache_bits,932const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1,933VP8LBackwardRefs* const refs_tmp2) {934if (low_effort) {935return GetBackwardReferencesLowEffort(width, height, argb, cache_bits,936hash_chain, refs_tmp1);937} else {938return GetBackwardReferences(width, height, argb, quality,939lz77_types_to_try, cache_bits, hash_chain,940refs_tmp1, refs_tmp2);941}942}943944945