Path: blob/master/thirdparty/libwebp/src/enc/backward_references_cost_enc.c
9913 views
// Copyright 2017 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// Improves a given set of backward references by analyzing its bit cost.10// The algorithm is similar to the Zopfli compression algorithm but tailored to11// images.12//13// Author: Vincent Rabaud ([email protected])14//1516#include <assert.h>17#include <string.h>1819#include "src/dsp/lossless_common.h"20#include "src/enc/backward_references_enc.h"21#include "src/enc/histogram_enc.h"22#include "src/utils/color_cache_utils.h"23#include "src/utils/utils.h"2425#define VALUES_IN_BYTE 2562627extern void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs);28extern int VP8LDistanceToPlaneCode(int xsize, int dist);29extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,30const PixOrCopy v);3132typedef struct {33uint32_t alpha_[VALUES_IN_BYTE];34uint32_t red_[VALUES_IN_BYTE];35uint32_t blue_[VALUES_IN_BYTE];36uint32_t distance_[NUM_DISTANCE_CODES];37uint32_t* literal_;38} CostModel;3940static void ConvertPopulationCountTableToBitEstimates(41int num_symbols, const uint32_t population_counts[], uint32_t output[]) {42uint32_t sum = 0;43int nonzeros = 0;44int i;45for (i = 0; i < num_symbols; ++i) {46sum += population_counts[i];47if (population_counts[i] > 0) {48++nonzeros;49}50}51if (nonzeros <= 1) {52memset(output, 0, num_symbols * sizeof(*output));53} else {54const uint32_t logsum = VP8LFastLog2(sum);55for (i = 0; i < num_symbols; ++i) {56output[i] = logsum - VP8LFastLog2(population_counts[i]);57}58}59}6061static int CostModelBuild(CostModel* const m, int xsize, int cache_bits,62const VP8LBackwardRefs* const refs) {63int ok = 0;64VP8LRefsCursor c = VP8LRefsCursorInit(refs);65VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits);66if (histo == NULL) goto Error;6768// The following code is similar to VP8LHistogramCreate but converts the69// distance to plane code.70VP8LHistogramInit(histo, cache_bits, /*init_arrays=*/ 1);71while (VP8LRefsCursorOk(&c)) {72VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos, VP8LDistanceToPlaneCode,73xsize);74VP8LRefsCursorNext(&c);75}7677ConvertPopulationCountTableToBitEstimates(78VP8LHistogramNumCodes(histo->palette_code_bits_), histo->literal_,79m->literal_);80ConvertPopulationCountTableToBitEstimates(81VALUES_IN_BYTE, histo->red_, m->red_);82ConvertPopulationCountTableToBitEstimates(83VALUES_IN_BYTE, histo->blue_, m->blue_);84ConvertPopulationCountTableToBitEstimates(85VALUES_IN_BYTE, histo->alpha_, m->alpha_);86ConvertPopulationCountTableToBitEstimates(87NUM_DISTANCE_CODES, histo->distance_, m->distance_);88ok = 1;8990Error:91VP8LFreeHistogram(histo);92return ok;93}9495static WEBP_INLINE int64_t GetLiteralCost(const CostModel* const m,96uint32_t v) {97return (int64_t)m->alpha_[v >> 24] + m->red_[(v >> 16) & 0xff] +98m->literal_[(v >> 8) & 0xff] + m->blue_[v & 0xff];99}100101static WEBP_INLINE int64_t GetCacheCost(const CostModel* const m,102uint32_t idx) {103const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx;104return (int64_t)m->literal_[literal_idx];105}106107static WEBP_INLINE int64_t GetLengthCost(const CostModel* const m,108uint32_t length) {109int code, extra_bits;110VP8LPrefixEncodeBits(length, &code, &extra_bits);111return (int64_t)m->literal_[VALUES_IN_BYTE + code] +112((int64_t)extra_bits << LOG_2_PRECISION_BITS);113}114115static WEBP_INLINE int64_t GetDistanceCost(const CostModel* const m,116uint32_t distance) {117int code, extra_bits;118VP8LPrefixEncodeBits(distance, &code, &extra_bits);119return (int64_t)m->distance_[code] +120((int64_t)extra_bits << LOG_2_PRECISION_BITS);121}122123static WEBP_INLINE void AddSingleLiteralWithCostModel(124const uint32_t* const argb, VP8LColorCache* const hashers,125const CostModel* const cost_model, int idx, int use_color_cache,126int64_t prev_cost, int64_t* const cost, uint16_t* const dist_array) {127int64_t cost_val = prev_cost;128const uint32_t color = argb[idx];129const int ix = use_color_cache ? VP8LColorCacheContains(hashers, color) : -1;130if (ix >= 0) {131// use_color_cache is true and hashers contains color132cost_val += DivRound(GetCacheCost(cost_model, ix) * 68, 100);133} else {134if (use_color_cache) VP8LColorCacheInsert(hashers, color);135cost_val += DivRound(GetLiteralCost(cost_model, color) * 82, 100);136}137if (cost[idx] > cost_val) {138cost[idx] = cost_val;139dist_array[idx] = 1; // only one is inserted.140}141}142143// -----------------------------------------------------------------------------144// CostManager and interval handling145146// Empirical value to avoid high memory consumption but good for performance.147#define COST_CACHE_INTERVAL_SIZE_MAX 500148149// To perform backward reference every pixel at index index_ is considered and150// the cost for the MAX_LENGTH following pixels computed. Those following pixels151// at index index_ + k (k from 0 to MAX_LENGTH) have a cost of:152// cost_ = distance cost at index + GetLengthCost(cost_model, k)153// and the minimum value is kept. GetLengthCost(cost_model, k) is cached in an154// array of size MAX_LENGTH.155// Instead of performing MAX_LENGTH comparisons per pixel, we keep track of the156// minimal values using intervals of constant cost.157// An interval is defined by the index_ of the pixel that generated it and158// is only useful in a range of indices from start_ to end_ (exclusive), i.e.159// it contains the minimum value for pixels between start_ and end_.160// Intervals are stored in a linked list and ordered by start_. When a new161// interval has a better value, old intervals are split or removed. There are162// therefore no overlapping intervals.163typedef struct CostInterval CostInterval;164struct CostInterval {165int64_t cost_;166int start_;167int end_;168int index_;169CostInterval* previous_;170CostInterval* next_;171};172173// The GetLengthCost(cost_model, k) are cached in a CostCacheInterval.174typedef struct {175int64_t cost_;176int start_;177int end_; // Exclusive.178} CostCacheInterval;179180// This structure is in charge of managing intervals and costs.181// It caches the different CostCacheInterval, caches the different182// GetLengthCost(cost_model, k) in cost_cache_ and the CostInterval's (whose183// count_ is limited by COST_CACHE_INTERVAL_SIZE_MAX).184#define COST_MANAGER_MAX_FREE_LIST 10185typedef struct {186CostInterval* head_;187int count_; // The number of stored intervals.188CostCacheInterval* cache_intervals_;189size_t cache_intervals_size_;190// Contains the GetLengthCost(cost_model, k).191int64_t cost_cache_[MAX_LENGTH];192int64_t* costs_;193uint16_t* dist_array_;194// Most of the time, we only need few intervals -> use a free-list, to avoid195// fragmentation with small allocs in most common cases.196CostInterval intervals_[COST_MANAGER_MAX_FREE_LIST];197CostInterval* free_intervals_;198// These are regularly malloc'd remains. This list can't grow larger than than199// size COST_CACHE_INTERVAL_SIZE_MAX - COST_MANAGER_MAX_FREE_LIST, note.200CostInterval* recycled_intervals_;201} CostManager;202203static void CostIntervalAddToFreeList(CostManager* const manager,204CostInterval* const interval) {205interval->next_ = manager->free_intervals_;206manager->free_intervals_ = interval;207}208209static int CostIntervalIsInFreeList(const CostManager* const manager,210const CostInterval* const interval) {211return (interval >= &manager->intervals_[0] &&212interval <= &manager->intervals_[COST_MANAGER_MAX_FREE_LIST - 1]);213}214215static void CostManagerInitFreeList(CostManager* const manager) {216int i;217manager->free_intervals_ = NULL;218for (i = 0; i < COST_MANAGER_MAX_FREE_LIST; ++i) {219CostIntervalAddToFreeList(manager, &manager->intervals_[i]);220}221}222223static void DeleteIntervalList(CostManager* const manager,224const CostInterval* interval) {225while (interval != NULL) {226const CostInterval* const next = interval->next_;227if (!CostIntervalIsInFreeList(manager, interval)) {228WebPSafeFree((void*)interval);229} // else: do nothing230interval = next;231}232}233234static void CostManagerClear(CostManager* const manager) {235if (manager == NULL) return;236237WebPSafeFree(manager->costs_);238WebPSafeFree(manager->cache_intervals_);239240// Clear the interval lists.241DeleteIntervalList(manager, manager->head_);242manager->head_ = NULL;243DeleteIntervalList(manager, manager->recycled_intervals_);244manager->recycled_intervals_ = NULL;245246// Reset pointers, count_ and cache_intervals_size_.247memset(manager, 0, sizeof(*manager));248CostManagerInitFreeList(manager);249}250251static int CostManagerInit(CostManager* const manager,252uint16_t* const dist_array, int pix_count,253const CostModel* const cost_model) {254int i;255const int cost_cache_size = (pix_count > MAX_LENGTH) ? MAX_LENGTH : pix_count;256257manager->costs_ = NULL;258manager->cache_intervals_ = NULL;259manager->head_ = NULL;260manager->recycled_intervals_ = NULL;261manager->count_ = 0;262manager->dist_array_ = dist_array;263CostManagerInitFreeList(manager);264265// Fill in the cost_cache_.266// Has to be done in two passes due to a GCC bug on i686267// related to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323268for (i = 0; i < cost_cache_size; ++i) {269manager->cost_cache_[i] = GetLengthCost(cost_model, i);270}271manager->cache_intervals_size_ = 1;272for (i = 1; i < cost_cache_size; ++i) {273// Get the number of bound intervals.274if (manager->cost_cache_[i] != manager->cost_cache_[i - 1]) {275++manager->cache_intervals_size_;276}277}278279// With the current cost model, we usually have below 20 intervals.280// The worst case scenario with a cost model would be if every length has a281// different cost, hence MAX_LENGTH but that is impossible with the current282// implementation that spirals around a pixel.283assert(manager->cache_intervals_size_ <= MAX_LENGTH);284manager->cache_intervals_ = (CostCacheInterval*)WebPSafeMalloc(285manager->cache_intervals_size_, sizeof(*manager->cache_intervals_));286if (manager->cache_intervals_ == NULL) {287CostManagerClear(manager);288return 0;289}290291// Fill in the cache_intervals_.292{293CostCacheInterval* cur = manager->cache_intervals_;294295// Consecutive values in cost_cache_ are compared and if a big enough296// difference is found, a new interval is created and bounded.297cur->start_ = 0;298cur->end_ = 1;299cur->cost_ = manager->cost_cache_[0];300for (i = 1; i < cost_cache_size; ++i) {301const int64_t cost_val = manager->cost_cache_[i];302if (cost_val != cur->cost_) {303++cur;304// Initialize an interval.305cur->start_ = i;306cur->cost_ = cost_val;307}308cur->end_ = i + 1;309}310assert((size_t)(cur - manager->cache_intervals_) + 1 ==311manager->cache_intervals_size_);312}313314manager->costs_ =315(int64_t*)WebPSafeMalloc(pix_count, sizeof(*manager->costs_));316if (manager->costs_ == NULL) {317CostManagerClear(manager);318return 0;319}320// Set the initial costs_ to INT64_MAX for every pixel as we will keep the321// minimum.322for (i = 0; i < pix_count; ++i) manager->costs_[i] = WEBP_INT64_MAX;323324return 1;325}326327// Given the cost and the position that define an interval, update the cost at328// pixel 'i' if it is smaller than the previously computed value.329static WEBP_INLINE void UpdateCost(CostManager* const manager, int i,330int position, int64_t cost) {331const int k = i - position;332assert(k >= 0 && k < MAX_LENGTH);333334if (manager->costs_[i] > cost) {335manager->costs_[i] = cost;336manager->dist_array_[i] = k + 1;337}338}339340// Given the cost and the position that define an interval, update the cost for341// all the pixels between 'start' and 'end' excluded.342static WEBP_INLINE void UpdateCostPerInterval(CostManager* const manager,343int start, int end, int position,344int64_t cost) {345int i;346for (i = start; i < end; ++i) UpdateCost(manager, i, position, cost);347}348349// Given two intervals, make 'prev' be the previous one of 'next' in 'manager'.350static WEBP_INLINE void ConnectIntervals(CostManager* const manager,351CostInterval* const prev,352CostInterval* const next) {353if (prev != NULL) {354prev->next_ = next;355} else {356manager->head_ = next;357}358359if (next != NULL) next->previous_ = prev;360}361362// Pop an interval in the manager.363static WEBP_INLINE void PopInterval(CostManager* const manager,364CostInterval* const interval) {365if (interval == NULL) return;366367ConnectIntervals(manager, interval->previous_, interval->next_);368if (CostIntervalIsInFreeList(manager, interval)) {369CostIntervalAddToFreeList(manager, interval);370} else { // recycle regularly malloc'd intervals too371interval->next_ = manager->recycled_intervals_;372manager->recycled_intervals_ = interval;373}374--manager->count_;375assert(manager->count_ >= 0);376}377378// Update the cost at index i by going over all the stored intervals that379// overlap with i.380// If 'do_clean_intervals' is set to something different than 0, intervals that381// end before 'i' will be popped.382static WEBP_INLINE void UpdateCostAtIndex(CostManager* const manager, int i,383int do_clean_intervals) {384CostInterval* current = manager->head_;385386while (current != NULL && current->start_ <= i) {387CostInterval* const next = current->next_;388if (current->end_ <= i) {389if (do_clean_intervals) {390// We have an outdated interval, remove it.391PopInterval(manager, current);392}393} else {394UpdateCost(manager, i, current->index_, current->cost_);395}396current = next;397}398}399400// Given a current orphan interval and its previous interval, before401// it was orphaned (which can be NULL), set it at the right place in the list402// of intervals using the start_ ordering and the previous interval as a hint.403static WEBP_INLINE void PositionOrphanInterval(CostManager* const manager,404CostInterval* const current,405CostInterval* previous) {406assert(current != NULL);407408if (previous == NULL) previous = manager->head_;409while (previous != NULL && current->start_ < previous->start_) {410previous = previous->previous_;411}412while (previous != NULL && previous->next_ != NULL &&413previous->next_->start_ < current->start_) {414previous = previous->next_;415}416417if (previous != NULL) {418ConnectIntervals(manager, current, previous->next_);419} else {420ConnectIntervals(manager, current, manager->head_);421}422ConnectIntervals(manager, previous, current);423}424425// Insert an interval in the list contained in the manager by starting at426// interval_in as a hint. The intervals are sorted by start_ value.427static WEBP_INLINE void InsertInterval(CostManager* const manager,428CostInterval* const interval_in,429int64_t cost, int position, int start,430int end) {431CostInterval* interval_new;432433if (start >= end) return;434if (manager->count_ >= COST_CACHE_INTERVAL_SIZE_MAX) {435// Serialize the interval if we cannot store it.436UpdateCostPerInterval(manager, start, end, position, cost);437return;438}439if (manager->free_intervals_ != NULL) {440interval_new = manager->free_intervals_;441manager->free_intervals_ = interval_new->next_;442} else if (manager->recycled_intervals_ != NULL) {443interval_new = manager->recycled_intervals_;444manager->recycled_intervals_ = interval_new->next_;445} else { // malloc for good446interval_new = (CostInterval*)WebPSafeMalloc(1, sizeof(*interval_new));447if (interval_new == NULL) {448// Write down the interval if we cannot create it.449UpdateCostPerInterval(manager, start, end, position, cost);450return;451}452}453454interval_new->cost_ = cost;455interval_new->index_ = position;456interval_new->start_ = start;457interval_new->end_ = end;458PositionOrphanInterval(manager, interval_new, interval_in);459460++manager->count_;461}462463// Given a new cost interval defined by its start at position, its length value464// and distance_cost, add its contributions to the previous intervals and costs.465// If handling the interval or one of its subintervals becomes to heavy, its466// contribution is added to the costs right away.467static WEBP_INLINE void PushInterval(CostManager* const manager,468int64_t distance_cost, int position,469int len) {470size_t i;471CostInterval* interval = manager->head_;472CostInterval* interval_next;473const CostCacheInterval* const cost_cache_intervals =474manager->cache_intervals_;475// If the interval is small enough, no need to deal with the heavy476// interval logic, just serialize it right away. This constant is empirical.477const int kSkipDistance = 10;478479if (len < kSkipDistance) {480int j;481for (j = position; j < position + len; ++j) {482const int k = j - position;483int64_t cost_tmp;484assert(k >= 0 && k < MAX_LENGTH);485cost_tmp = distance_cost + manager->cost_cache_[k];486487if (manager->costs_[j] > cost_tmp) {488manager->costs_[j] = cost_tmp;489manager->dist_array_[j] = k + 1;490}491}492return;493}494495for (i = 0; i < manager->cache_intervals_size_ &&496cost_cache_intervals[i].start_ < len;497++i) {498// Define the intersection of the ith interval with the new one.499int start = position + cost_cache_intervals[i].start_;500const int end = position + (cost_cache_intervals[i].end_ > len501? len502: cost_cache_intervals[i].end_);503const int64_t cost = distance_cost + cost_cache_intervals[i].cost_;504505for (; interval != NULL && interval->start_ < end;506interval = interval_next) {507interval_next = interval->next_;508509// Make sure we have some overlap510if (start >= interval->end_) continue;511512if (cost >= interval->cost_) {513// When intervals are represented, the lower, the better.514// [**********************************************************[515// start end516// [----------------------------------[517// interval->start_ interval->end_518// If we are worse than what we already have, add whatever we have so519// far up to interval.520const int start_new = interval->end_;521InsertInterval(manager, interval, cost, position, start,522interval->start_);523start = start_new;524if (start >= end) break;525continue;526}527528if (start <= interval->start_) {529if (interval->end_ <= end) {530// [----------------------------------[531// interval->start_ interval->end_532// [**************************************************************[533// start end534// We can safely remove the old interval as it is fully included.535PopInterval(manager, interval);536} else {537// [------------------------------------[538// interval->start_ interval->end_539// [*****************************[540// start end541interval->start_ = end;542break;543}544} else {545if (end < interval->end_) {546// [--------------------------------------------------------------[547// interval->start_ interval->end_548// [*****************************[549// start end550// We have to split the old interval as it fully contains the new one.551const int end_original = interval->end_;552interval->end_ = start;553InsertInterval(manager, interval, interval->cost_, interval->index_,554end, end_original);555interval = interval->next_;556break;557} else {558// [------------------------------------[559// interval->start_ interval->end_560// [*****************************[561// start end562interval->end_ = start;563}564}565}566// Insert the remaining interval from start to end.567InsertInterval(manager, interval, cost, position, start, end);568}569}570571static int BackwardReferencesHashChainDistanceOnly(572int xsize, int ysize, const uint32_t* const argb, int cache_bits,573const VP8LHashChain* const hash_chain, const VP8LBackwardRefs* const refs,574uint16_t* const dist_array) {575int i;576int ok = 0;577int cc_init = 0;578const int pix_count = xsize * ysize;579const int use_color_cache = (cache_bits > 0);580const size_t literal_array_size =581sizeof(*((CostModel*)NULL)->literal_) * VP8LHistogramNumCodes(cache_bits);582const size_t cost_model_size = sizeof(CostModel) + literal_array_size;583CostModel* const cost_model =584(CostModel*)WebPSafeCalloc(1ULL, cost_model_size);585VP8LColorCache hashers;586CostManager* cost_manager =587(CostManager*)WebPSafeCalloc(1ULL, sizeof(*cost_manager));588int offset_prev = -1, len_prev = -1;589int64_t offset_cost = -1;590int first_offset_is_constant = -1; // initialized with 'impossible' value591int reach = 0;592593if (cost_model == NULL || cost_manager == NULL) goto Error;594595cost_model->literal_ = (uint32_t*)(cost_model + 1);596if (use_color_cache) {597cc_init = VP8LColorCacheInit(&hashers, cache_bits);598if (!cc_init) goto Error;599}600601if (!CostModelBuild(cost_model, xsize, cache_bits, refs)) {602goto Error;603}604605if (!CostManagerInit(cost_manager, dist_array, pix_count, cost_model)) {606goto Error;607}608609// We loop one pixel at a time, but store all currently best points to610// non-processed locations from this point.611dist_array[0] = 0;612// Add first pixel as literal.613AddSingleLiteralWithCostModel(argb, &hashers, cost_model, /*idx=*/0,614use_color_cache, /*prev_cost=*/0,615cost_manager->costs_, dist_array);616617for (i = 1; i < pix_count; ++i) {618const int64_t prev_cost = cost_manager->costs_[i - 1];619int offset, len;620VP8LHashChainFindCopy(hash_chain, i, &offset, &len);621622// Try adding the pixel as a literal.623AddSingleLiteralWithCostModel(argb, &hashers, cost_model, i,624use_color_cache, prev_cost,625cost_manager->costs_, dist_array);626627// If we are dealing with a non-literal.628if (len >= 2) {629if (offset != offset_prev) {630const int code = VP8LDistanceToPlaneCode(xsize, offset);631offset_cost = GetDistanceCost(cost_model, code);632first_offset_is_constant = 1;633PushInterval(cost_manager, prev_cost + offset_cost, i, len);634} else {635assert(offset_cost >= 0);636assert(len_prev >= 0);637assert(first_offset_is_constant == 0 || first_offset_is_constant == 1);638// Instead of considering all contributions from a pixel i by calling:639// PushInterval(cost_manager, prev_cost + offset_cost, i, len);640// we optimize these contributions in case offset_cost stays the same641// for consecutive pixels. This describes a set of pixels similar to a642// previous set (e.g. constant color regions).643if (first_offset_is_constant) {644reach = i - 1 + len_prev - 1;645first_offset_is_constant = 0;646}647648if (i + len - 1 > reach) {649// We can only be go further with the same offset if the previous650// length was maxed, hence len_prev == len == MAX_LENGTH.651// TODO(vrabaud), bump i to the end right away (insert cache and652// update cost).653// TODO(vrabaud), check if one of the points in between does not have654// a lower cost.655// Already consider the pixel at "reach" to add intervals that are656// better than whatever we add.657int offset_j, len_j = 0;658int j;659assert(len == MAX_LENGTH || len == pix_count - i);660// Figure out the last consecutive pixel within [i, reach + 1] with661// the same offset.662for (j = i; j <= reach; ++j) {663VP8LHashChainFindCopy(hash_chain, j + 1, &offset_j, &len_j);664if (offset_j != offset) {665VP8LHashChainFindCopy(hash_chain, j, &offset_j, &len_j);666break;667}668}669// Update the cost at j - 1 and j.670UpdateCostAtIndex(cost_manager, j - 1, 0);671UpdateCostAtIndex(cost_manager, j, 0);672673PushInterval(cost_manager, cost_manager->costs_[j - 1] + offset_cost,674j, len_j);675reach = j + len_j - 1;676}677}678}679680UpdateCostAtIndex(cost_manager, i, 1);681offset_prev = offset;682len_prev = len;683}684685ok = !refs->error_;686Error:687if (cc_init) VP8LColorCacheClear(&hashers);688CostManagerClear(cost_manager);689WebPSafeFree(cost_model);690WebPSafeFree(cost_manager);691return ok;692}693694// We pack the path at the end of *dist_array and return695// a pointer to this part of the array. Example:696// dist_array = [1x2xx3x2] => packed [1x2x1232], chosen_path = [1232]697static void TraceBackwards(uint16_t* const dist_array,698int dist_array_size,699uint16_t** const chosen_path,700int* const chosen_path_size) {701uint16_t* path = dist_array + dist_array_size;702uint16_t* cur = dist_array + dist_array_size - 1;703while (cur >= dist_array) {704const int k = *cur;705--path;706*path = k;707cur -= k;708}709*chosen_path = path;710*chosen_path_size = (int)(dist_array + dist_array_size - path);711}712713static int BackwardReferencesHashChainFollowChosenPath(714const uint32_t* const argb, int cache_bits,715const uint16_t* const chosen_path, int chosen_path_size,716const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs) {717const int use_color_cache = (cache_bits > 0);718int ix;719int i = 0;720int ok = 0;721int cc_init = 0;722VP8LColorCache hashers;723724if (use_color_cache) {725cc_init = VP8LColorCacheInit(&hashers, cache_bits);726if (!cc_init) goto Error;727}728729VP8LClearBackwardRefs(refs);730for (ix = 0; ix < chosen_path_size; ++ix) {731const int len = chosen_path[ix];732if (len != 1) {733int k;734const int offset = VP8LHashChainFindOffset(hash_chain, i);735VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));736if (use_color_cache) {737for (k = 0; k < len; ++k) {738VP8LColorCacheInsert(&hashers, argb[i + k]);739}740}741i += len;742} else {743PixOrCopy v;744const int idx =745use_color_cache ? VP8LColorCacheContains(&hashers, argb[i]) : -1;746if (idx >= 0) {747// use_color_cache is true and hashers contains argb[i]748// push pixel as a color cache index749v = PixOrCopyCreateCacheIdx(idx);750} else {751if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);752v = PixOrCopyCreateLiteral(argb[i]);753}754VP8LBackwardRefsCursorAdd(refs, v);755++i;756}757}758ok = !refs->error_;759Error:760if (cc_init) VP8LColorCacheClear(&hashers);761return ok;762}763764// Returns 1 on success.765extern int VP8LBackwardReferencesTraceBackwards(766int xsize, int ysize, const uint32_t* const argb, int cache_bits,767const VP8LHashChain* const hash_chain,768const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst);769int VP8LBackwardReferencesTraceBackwards(int xsize, int ysize,770const uint32_t* const argb,771int cache_bits,772const VP8LHashChain* const hash_chain,773const VP8LBackwardRefs* const refs_src,774VP8LBackwardRefs* const refs_dst) {775int ok = 0;776const int dist_array_size = xsize * ysize;777uint16_t* chosen_path = NULL;778int chosen_path_size = 0;779uint16_t* dist_array =780(uint16_t*)WebPSafeMalloc(dist_array_size, sizeof(*dist_array));781782if (dist_array == NULL) goto Error;783784if (!BackwardReferencesHashChainDistanceOnly(785xsize, ysize, argb, cache_bits, hash_chain, refs_src, dist_array)) {786goto Error;787}788TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size);789if (!BackwardReferencesHashChainFollowChosenPath(790argb, cache_bits, chosen_path, chosen_path_size, hash_chain,791refs_dst)) {792goto Error;793}794ok = 1;795Error:796WebPSafeFree(dist_array);797return ok;798}799800801