Path: blob/master/thirdparty/libwebp/src/enc/backward_references_cost_enc.c
21724 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"24#include "src/webp/format_constants.h"25#include "src/webp/types.h"2627#define VALUES_IN_BYTE 2562829extern void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs);30extern int VP8LDistanceToPlaneCode(int xsize, int dist);31extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,32const PixOrCopy v);3334typedef struct {35uint32_t alpha[VALUES_IN_BYTE];36uint32_t red[VALUES_IN_BYTE];37uint32_t blue[VALUES_IN_BYTE];38uint32_t distance[NUM_DISTANCE_CODES];39uint32_t* literal;40} CostModel;4142static void ConvertPopulationCountTableToBitEstimates(43int num_symbols, const uint32_t population_counts[], uint32_t output[]) {44uint32_t sum = 0;45int nonzeros = 0;46int i;47for (i = 0; i < num_symbols; ++i) {48sum += population_counts[i];49if (population_counts[i] > 0) {50++nonzeros;51}52}53if (nonzeros <= 1) {54memset(output, 0, num_symbols * sizeof(*output));55} else {56const uint32_t logsum = VP8LFastLog2(sum);57for (i = 0; i < num_symbols; ++i) {58output[i] = logsum - VP8LFastLog2(population_counts[i]);59}60}61}6263static int CostModelBuild(CostModel* const m, int xsize, int cache_bits,64const VP8LBackwardRefs* const refs) {65int ok = 0;66VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits);67if (histo == NULL) goto Error;6869// The following code is similar to VP8LHistogramCreate but converts the70// distance to plane code.71VP8LHistogramInit(histo, cache_bits, /*init_arrays=*/ 1);72VP8LHistogramStoreRefs(refs, VP8LDistanceToPlaneCode, xsize, histo);7374ConvertPopulationCountTableToBitEstimates(75VP8LHistogramNumCodes(histo->palette_code_bits), histo->literal,76m->literal);77ConvertPopulationCountTableToBitEstimates(78VALUES_IN_BYTE, histo->red, m->red);79ConvertPopulationCountTableToBitEstimates(80VALUES_IN_BYTE, histo->blue, m->blue);81ConvertPopulationCountTableToBitEstimates(82VALUES_IN_BYTE, histo->alpha, m->alpha);83ConvertPopulationCountTableToBitEstimates(84NUM_DISTANCE_CODES, histo->distance, m->distance);85ok = 1;8687Error:88VP8LFreeHistogram(histo);89return ok;90}9192static WEBP_INLINE int64_t GetLiteralCost(const CostModel* const m,93uint32_t v) {94return (int64_t)m->alpha[v >> 24] + m->red[(v >> 16) & 0xff] +95m->literal[(v >> 8) & 0xff] + m->blue[v & 0xff];96}9798static WEBP_INLINE int64_t GetCacheCost(const CostModel* const m,99uint32_t idx) {100const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx;101return (int64_t)m->literal[literal_idx];102}103104static WEBP_INLINE int64_t GetLengthCost(const CostModel* const m,105uint32_t length) {106int code, extra_bits;107VP8LPrefixEncodeBits(length, &code, &extra_bits);108return (int64_t)m->literal[VALUES_IN_BYTE + code] +109((int64_t)extra_bits << LOG_2_PRECISION_BITS);110}111112static WEBP_INLINE int64_t GetDistanceCost(const CostModel* const m,113uint32_t distance) {114int code, extra_bits;115VP8LPrefixEncodeBits(distance, &code, &extra_bits);116return (int64_t)m->distance[code] +117((int64_t)extra_bits << LOG_2_PRECISION_BITS);118}119120static WEBP_INLINE void AddSingleLiteralWithCostModel(121const uint32_t* const argb, VP8LColorCache* const hashers,122const CostModel* const cost_model, int idx, int use_color_cache,123int64_t prev_cost, int64_t* const cost, uint16_t* const dist_array) {124int64_t cost_val = prev_cost;125const uint32_t color = argb[idx];126const int ix = use_color_cache ? VP8LColorCacheContains(hashers, color) : -1;127if (ix >= 0) {128// use_color_cache is true and hashers contains color129cost_val += DivRound(GetCacheCost(cost_model, ix) * 68, 100);130} else {131if (use_color_cache) VP8LColorCacheInsert(hashers, color);132cost_val += DivRound(GetLiteralCost(cost_model, color) * 82, 100);133}134if (cost[idx] > cost_val) {135cost[idx] = cost_val;136dist_array[idx] = 1; // only one is inserted.137}138}139140// -----------------------------------------------------------------------------141// CostManager and interval handling142143// Empirical value to avoid high memory consumption but good for performance.144#define COST_CACHE_INTERVAL_SIZE_MAX 500145146// To perform backward reference every pixel at index 'index' is considered and147// the cost for the MAX_LENGTH following pixels computed. Those following pixels148// at index 'index' + k (k from 0 to MAX_LENGTH) have a cost of:149// cost = distance cost at index + GetLengthCost(cost_model, k)150// and the minimum value is kept. GetLengthCost(cost_model, k) is cached in an151// array of size MAX_LENGTH.152// Instead of performing MAX_LENGTH comparisons per pixel, we keep track of the153// minimal values using intervals of constant cost.154// An interval is defined by the 'index' of the pixel that generated it and155// is only useful in a range of indices from 'start' to 'end' (exclusive), i.e.156// it contains the minimum value for pixels between start and end.157// Intervals are stored in a linked list and ordered by 'start'. When a new158// interval has a better value, old intervals are split or removed. There are159// therefore no overlapping intervals.160typedef struct CostInterval CostInterval;161struct CostInterval {162int64_t cost;163int start;164int end;165int index;166CostInterval* previous;167CostInterval* next;168};169170// The GetLengthCost(cost_model, k) are cached in a CostCacheInterval.171typedef struct {172int64_t cost;173int start;174int end; // Exclusive.175} CostCacheInterval;176177// This structure is in charge of managing intervals and costs.178// It caches the different CostCacheInterval, caches the different179// GetLengthCost(cost_model, k) in cost_cache and the CostInterval's (whose180// 'count' is limited by COST_CACHE_INTERVAL_SIZE_MAX).181#define COST_MANAGER_MAX_FREE_LIST 10182typedef struct {183CostInterval* head;184int count; // The number of stored intervals.185CostCacheInterval* cache_intervals;186size_t cache_intervals_size;187// Contains the GetLengthCost(cost_model, k).188int64_t cost_cache[MAX_LENGTH];189int64_t* costs;190uint16_t* dist_array;191// Most of the time, we only need few intervals -> use a free-list, to avoid192// fragmentation with small allocs in most common cases.193CostInterval intervals[COST_MANAGER_MAX_FREE_LIST];194CostInterval* free_intervals;195// These are regularly malloc'd remains. This list can't grow larger than than196// size COST_CACHE_INTERVAL_SIZE_MAX - COST_MANAGER_MAX_FREE_LIST, note.197CostInterval* recycled_intervals;198} CostManager;199200static void CostIntervalAddToFreeList(CostManager* const manager,201CostInterval* const interval) {202interval->next = manager->free_intervals;203manager->free_intervals = interval;204}205206static int CostIntervalIsInFreeList(const CostManager* const manager,207const CostInterval* const interval) {208return (interval >= &manager->intervals[0] &&209interval <= &manager->intervals[COST_MANAGER_MAX_FREE_LIST - 1]);210}211212static void CostManagerInitFreeList(CostManager* const manager) {213int i;214manager->free_intervals = NULL;215for (i = 0; i < COST_MANAGER_MAX_FREE_LIST; ++i) {216CostIntervalAddToFreeList(manager, &manager->intervals[i]);217}218}219220static void DeleteIntervalList(CostManager* const manager,221const CostInterval* interval) {222while (interval != NULL) {223const CostInterval* const next = interval->next;224if (!CostIntervalIsInFreeList(manager, interval)) {225WebPSafeFree((void*)interval);226} // else: do nothing227interval = next;228}229}230231static void CostManagerClear(CostManager* const manager) {232if (manager == NULL) return;233234WebPSafeFree(manager->costs);235WebPSafeFree(manager->cache_intervals);236237// Clear the interval lists.238DeleteIntervalList(manager, manager->head);239manager->head = NULL;240DeleteIntervalList(manager, manager->recycled_intervals);241manager->recycled_intervals = NULL;242243// Reset pointers, 'count' and 'cache_intervals_size'.244memset(manager, 0, sizeof(*manager));245CostManagerInitFreeList(manager);246}247248static int CostManagerInit(CostManager* const manager,249uint16_t* const dist_array, int pix_count,250const CostModel* const cost_model) {251int i;252const int cost_cache_size = (pix_count > MAX_LENGTH) ? MAX_LENGTH : pix_count;253254manager->costs = NULL;255manager->cache_intervals = NULL;256manager->head = NULL;257manager->recycled_intervals = NULL;258manager->count = 0;259manager->dist_array = dist_array;260CostManagerInitFreeList(manager);261262// Fill in the 'cost_cache'.263// Has to be done in two passes due to a GCC bug on i686264// related to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323265for (i = 0; i < cost_cache_size; ++i) {266manager->cost_cache[i] = GetLengthCost(cost_model, i);267}268manager->cache_intervals_size = 1;269for (i = 1; i < cost_cache_size; ++i) {270// Get the number of bound intervals.271if (manager->cost_cache[i] != manager->cost_cache[i - 1]) {272++manager->cache_intervals_size;273}274}275276// With the current cost model, we usually have below 20 intervals.277// The worst case scenario with a cost model would be if every length has a278// different cost, hence MAX_LENGTH but that is impossible with the current279// implementation that spirals around a pixel.280assert(manager->cache_intervals_size <= MAX_LENGTH);281manager->cache_intervals = (CostCacheInterval*)WebPSafeMalloc(282manager->cache_intervals_size, sizeof(*manager->cache_intervals));283if (manager->cache_intervals == NULL) {284CostManagerClear(manager);285return 0;286}287288// Fill in the 'cache_intervals'.289{290CostCacheInterval* cur = manager->cache_intervals;291292// Consecutive values in 'cost_cache' are compared and if a big enough293// difference is found, a new interval is created and bounded.294cur->start = 0;295cur->end = 1;296cur->cost = manager->cost_cache[0];297for (i = 1; i < cost_cache_size; ++i) {298const int64_t cost_val = manager->cost_cache[i];299if (cost_val != cur->cost) {300++cur;301// Initialize an interval.302cur->start = i;303cur->cost = cost_val;304}305cur->end = i + 1;306}307assert((size_t)(cur - manager->cache_intervals) + 1 ==308manager->cache_intervals_size);309}310311manager->costs =312(int64_t*)WebPSafeMalloc(pix_count, sizeof(*manager->costs));313if (manager->costs == NULL) {314CostManagerClear(manager);315return 0;316}317// Set the initial 'costs' to INT64_MAX for every pixel as we will keep the318// minimum.319for (i = 0; i < pix_count; ++i) manager->costs[i] = WEBP_INT64_MAX;320321return 1;322}323324// Given the cost and the position that define an interval, update the cost at325// pixel 'i' if it is smaller than the previously computed value.326static WEBP_INLINE void UpdateCost(CostManager* const manager, int i,327int position, int64_t cost) {328const int k = i - position;329assert(k >= 0 && k < MAX_LENGTH);330331if (manager->costs[i] > cost) {332manager->costs[i] = cost;333manager->dist_array[i] = k + 1;334}335}336337// Given the cost and the position that define an interval, update the cost for338// all the pixels between 'start' and 'end' excluded.339static WEBP_INLINE void UpdateCostPerInterval(CostManager* const manager,340int start, int end, int position,341int64_t cost) {342int i;343for (i = start; i < end; ++i) UpdateCost(manager, i, position, cost);344}345346// Given two intervals, make 'prev' be the previous one of 'next' in 'manager'.347static WEBP_INLINE void ConnectIntervals(CostManager* const manager,348CostInterval* const prev,349CostInterval* const next) {350if (prev != NULL) {351prev->next = next;352} else {353manager->head = next;354}355356if (next != NULL) next->previous = prev;357}358359// Pop an interval in the manager.360static WEBP_INLINE void PopInterval(CostManager* const manager,361CostInterval* const interval) {362if (interval == NULL) return;363364ConnectIntervals(manager, interval->previous, interval->next);365if (CostIntervalIsInFreeList(manager, interval)) {366CostIntervalAddToFreeList(manager, interval);367} else { // recycle regularly malloc'd intervals too368interval->next = manager->recycled_intervals;369manager->recycled_intervals = interval;370}371--manager->count;372assert(manager->count >= 0);373}374375// Update the cost at index i by going over all the stored intervals that376// overlap with i.377// If 'do_clean_intervals' is set to something different than 0, intervals that378// end before 'i' will be popped.379static WEBP_INLINE void UpdateCostAtIndex(CostManager* const manager, int i,380int do_clean_intervals) {381CostInterval* current = manager->head;382383while (current != NULL && current->start <= i) {384CostInterval* const next = current->next;385if (current->end <= i) {386if (do_clean_intervals) {387// We have an outdated interval, remove it.388PopInterval(manager, current);389}390} else {391UpdateCost(manager, i, current->index, current->cost);392}393current = next;394}395}396397// Given a current orphan interval and its previous interval, before398// it was orphaned (which can be NULL), set it at the right place in the list399// of intervals using the 'start' ordering and the previous interval as a hint.400static WEBP_INLINE void PositionOrphanInterval(CostManager* const manager,401CostInterval* const current,402CostInterval* previous) {403assert(current != NULL);404405if (previous == NULL) previous = manager->head;406while (previous != NULL && current->start < previous->start) {407previous = previous->previous;408}409while (previous != NULL && previous->next != NULL &&410previous->next->start < current->start) {411previous = previous->next;412}413414if (previous != NULL) {415ConnectIntervals(manager, current, previous->next);416} else {417ConnectIntervals(manager, current, manager->head);418}419ConnectIntervals(manager, previous, current);420}421422// Insert an interval in the list contained in the manager by starting at423// 'interval_in' as a hint. The intervals are sorted by 'start' value.424static WEBP_INLINE void InsertInterval(CostManager* const manager,425CostInterval* const interval_in,426int64_t cost, int position, int start,427int end) {428CostInterval* interval_new;429430if (start >= end) return;431if (manager->count >= COST_CACHE_INTERVAL_SIZE_MAX) {432// Serialize the interval if we cannot store it.433UpdateCostPerInterval(manager, start, end, position, cost);434return;435}436if (manager->free_intervals != NULL) {437interval_new = manager->free_intervals;438manager->free_intervals = interval_new->next;439} else if (manager->recycled_intervals != NULL) {440interval_new = manager->recycled_intervals;441manager->recycled_intervals = interval_new->next;442} else { // malloc for good443interval_new = (CostInterval*)WebPSafeMalloc(1, sizeof(*interval_new));444if (interval_new == NULL) {445// Write down the interval if we cannot create it.446UpdateCostPerInterval(manager, start, end, position, cost);447return;448}449}450451interval_new->cost = cost;452interval_new->index = position;453interval_new->start = start;454interval_new->end = end;455PositionOrphanInterval(manager, interval_new, interval_in);456457++manager->count;458}459460// Given a new cost interval defined by its start at position, its length value461// and distance_cost, add its contributions to the previous intervals and costs.462// If handling the interval or one of its subintervals becomes to heavy, its463// contribution is added to the costs right away.464static WEBP_INLINE void PushInterval(CostManager* const manager,465int64_t distance_cost, int position,466int len) {467size_t i;468CostInterval* interval = manager->head;469CostInterval* interval_next;470const CostCacheInterval* const cost_cache_intervals =471manager->cache_intervals;472// If the interval is small enough, no need to deal with the heavy473// interval logic, just serialize it right away. This constant is empirical.474const int kSkipDistance = 10;475476if (len < kSkipDistance) {477int j;478for (j = position; j < position + len; ++j) {479const int k = j - position;480int64_t cost_tmp;481assert(k >= 0 && k < MAX_LENGTH);482cost_tmp = distance_cost + manager->cost_cache[k];483484if (manager->costs[j] > cost_tmp) {485manager->costs[j] = cost_tmp;486manager->dist_array[j] = k + 1;487}488}489return;490}491492for (i = 0; i < manager->cache_intervals_size &&493cost_cache_intervals[i].start < len;494++i) {495// Define the intersection of the ith interval with the new one.496int start = position + cost_cache_intervals[i].start;497const int end = position + (cost_cache_intervals[i].end > len498? len499: cost_cache_intervals[i].end);500const int64_t cost = distance_cost + cost_cache_intervals[i].cost;501502for (; interval != NULL && interval->start < end;503interval = interval_next) {504interval_next = interval->next;505506// Make sure we have some overlap507if (start >= interval->end) continue;508509if (cost >= interval->cost) {510// When intervals are represented, the lower, the better.511// [**********************************************************[512// start end513// [----------------------------------[514// interval->start interval->end515// If we are worse than what we already have, add whatever we have so516// far up to interval.517const int start_new = interval->end;518InsertInterval(manager, interval, cost, position, start,519interval->start);520start = start_new;521if (start >= end) break;522continue;523}524525if (start <= interval->start) {526if (interval->end <= end) {527// [----------------------------------[528// interval->start interval->end529// [**************************************************************[530// start end531// We can safely remove the old interval as it is fully included.532PopInterval(manager, interval);533} else {534// [------------------------------------[535// interval->start interval->end536// [*****************************[537// start end538interval->start = end;539break;540}541} else {542if (end < interval->end) {543// [--------------------------------------------------------------[544// interval->start interval->end545// [*****************************[546// start end547// We have to split the old interval as it fully contains the new one.548const int end_original = interval->end;549interval->end = start;550InsertInterval(manager, interval, interval->cost, interval->index,551end, end_original);552interval = interval->next;553break;554} else {555// [------------------------------------[556// interval->start interval->end557// [*****************************[558// start end559interval->end = start;560}561}562}563// Insert the remaining interval from start to end.564InsertInterval(manager, interval, cost, position, start, end);565}566}567568static int BackwardReferencesHashChainDistanceOnly(569int xsize, int ysize, const uint32_t* const argb, int cache_bits,570const VP8LHashChain* const hash_chain, const VP8LBackwardRefs* const refs,571uint16_t* const dist_array) {572int i;573int ok = 0;574int cc_init = 0;575const int pix_count = xsize * ysize;576const int use_color_cache = (cache_bits > 0);577const size_t literal_array_size =578sizeof(*((CostModel*)NULL)->literal) * VP8LHistogramNumCodes(cache_bits);579const size_t cost_model_size = sizeof(CostModel) + literal_array_size;580CostModel* const cost_model =581(CostModel*)WebPSafeCalloc(1ULL, cost_model_size);582VP8LColorCache hashers;583CostManager* cost_manager =584(CostManager*)WebPSafeCalloc(1ULL, sizeof(*cost_manager));585int offset_prev = -1, len_prev = -1;586int64_t offset_cost = -1;587int first_offset_is_constant = -1; // initialized with 'impossible' value588int reach = 0;589590if (cost_model == NULL || cost_manager == NULL) goto Error;591592cost_model->literal = (uint32_t*)(cost_model + 1);593if (use_color_cache) {594cc_init = VP8LColorCacheInit(&hashers, cache_bits);595if (!cc_init) goto Error;596}597598if (!CostModelBuild(cost_model, xsize, cache_bits, refs)) {599goto Error;600}601602if (!CostManagerInit(cost_manager, dist_array, pix_count, cost_model)) {603goto Error;604}605606// We loop one pixel at a time, but store all currently best points to607// non-processed locations from this point.608dist_array[0] = 0;609// Add first pixel as literal.610AddSingleLiteralWithCostModel(argb, &hashers, cost_model, /*idx=*/0,611use_color_cache, /*prev_cost=*/0,612cost_manager->costs, dist_array);613614for (i = 1; i < pix_count; ++i) {615const int64_t prev_cost = cost_manager->costs[i - 1];616int offset, len;617VP8LHashChainFindCopy(hash_chain, i, &offset, &len);618619// Try adding the pixel as a literal.620AddSingleLiteralWithCostModel(argb, &hashers, cost_model, i,621use_color_cache, prev_cost,622cost_manager->costs, dist_array);623624// If we are dealing with a non-literal.625if (len >= 2) {626if (offset != offset_prev) {627const int code = VP8LDistanceToPlaneCode(xsize, offset);628offset_cost = GetDistanceCost(cost_model, code);629first_offset_is_constant = 1;630PushInterval(cost_manager, prev_cost + offset_cost, i, len);631} else {632assert(offset_cost >= 0);633assert(len_prev >= 0);634assert(first_offset_is_constant == 0 || first_offset_is_constant == 1);635// Instead of considering all contributions from a pixel i by calling:636// PushInterval(cost_manager, prev_cost + offset_cost, i, len);637// we optimize these contributions in case offset_cost stays the same638// for consecutive pixels. This describes a set of pixels similar to a639// previous set (e.g. constant color regions).640if (first_offset_is_constant) {641reach = i - 1 + len_prev - 1;642first_offset_is_constant = 0;643}644645if (i + len - 1 > reach) {646// We can only be go further with the same offset if the previous647// length was maxed, hence len_prev == len == MAX_LENGTH.648// TODO(vrabaud), bump i to the end right away (insert cache and649// update cost).650// TODO(vrabaud), check if one of the points in between does not have651// a lower cost.652// Already consider the pixel at "reach" to add intervals that are653// better than whatever we add.654int offset_j, len_j = 0;655int j;656assert(len == MAX_LENGTH || len == pix_count - i);657// Figure out the last consecutive pixel within [i, reach + 1] with658// the same offset.659for (j = i; j <= reach; ++j) {660VP8LHashChainFindCopy(hash_chain, j + 1, &offset_j, &len_j);661if (offset_j != offset) {662VP8LHashChainFindCopy(hash_chain, j, &offset_j, &len_j);663break;664}665}666// Update the cost at j - 1 and j.667UpdateCostAtIndex(cost_manager, j - 1, 0);668UpdateCostAtIndex(cost_manager, j, 0);669670PushInterval(cost_manager, cost_manager->costs[j - 1] + offset_cost,671j, len_j);672reach = j + len_j - 1;673}674}675}676677UpdateCostAtIndex(cost_manager, i, 1);678offset_prev = offset;679len_prev = len;680}681682ok = !refs->error;683Error:684if (cc_init) VP8LColorCacheClear(&hashers);685CostManagerClear(cost_manager);686WebPSafeFree(cost_model);687WebPSafeFree(cost_manager);688return ok;689}690691// We pack the path at the end of *dist_array and return692// a pointer to this part of the array. Example:693// dist_array = [1x2xx3x2] => packed [1x2x1232], chosen_path = [1232]694static void TraceBackwards(uint16_t* const dist_array,695int dist_array_size,696uint16_t** const chosen_path,697int* const chosen_path_size) {698uint16_t* path = dist_array + dist_array_size;699uint16_t* cur = dist_array + dist_array_size - 1;700while (cur >= dist_array) {701const int k = *cur;702--path;703*path = k;704cur -= k;705}706*chosen_path = path;707*chosen_path_size = (int)(dist_array + dist_array_size - path);708}709710static int BackwardReferencesHashChainFollowChosenPath(711const uint32_t* const argb, int cache_bits,712const uint16_t* const chosen_path, int chosen_path_size,713const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs) {714const int use_color_cache = (cache_bits > 0);715int ix;716int i = 0;717int ok = 0;718int cc_init = 0;719VP8LColorCache hashers;720721if (use_color_cache) {722cc_init = VP8LColorCacheInit(&hashers, cache_bits);723if (!cc_init) goto Error;724}725726VP8LClearBackwardRefs(refs);727for (ix = 0; ix < chosen_path_size; ++ix) {728const int len = chosen_path[ix];729if (len != 1) {730int k;731const int offset = VP8LHashChainFindOffset(hash_chain, i);732VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));733if (use_color_cache) {734for (k = 0; k < len; ++k) {735VP8LColorCacheInsert(&hashers, argb[i + k]);736}737}738i += len;739} else {740PixOrCopy v;741const int idx =742use_color_cache ? VP8LColorCacheContains(&hashers, argb[i]) : -1;743if (idx >= 0) {744// use_color_cache is true and hashers contains argb[i]745// push pixel as a color cache index746v = PixOrCopyCreateCacheIdx(idx);747} else {748if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);749v = PixOrCopyCreateLiteral(argb[i]);750}751VP8LBackwardRefsCursorAdd(refs, v);752++i;753}754}755ok = !refs->error;756Error:757if (cc_init) VP8LColorCacheClear(&hashers);758return ok;759}760761// Returns 1 on success.762extern int VP8LBackwardReferencesTraceBackwards(763int xsize, int ysize, const uint32_t* const argb, int cache_bits,764const VP8LHashChain* const hash_chain,765const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst);766int VP8LBackwardReferencesTraceBackwards(int xsize, int ysize,767const uint32_t* const argb,768int cache_bits,769const VP8LHashChain* const hash_chain,770const VP8LBackwardRefs* const refs_src,771VP8LBackwardRefs* const refs_dst) {772int ok = 0;773const int dist_array_size = xsize * ysize;774uint16_t* chosen_path = NULL;775int chosen_path_size = 0;776uint16_t* dist_array =777(uint16_t*)WebPSafeMalloc(dist_array_size, sizeof(*dist_array));778779if (dist_array == NULL) goto Error;780781if (!BackwardReferencesHashChainDistanceOnly(782xsize, ysize, argb, cache_bits, hash_chain, refs_src, dist_array)) {783goto Error;784}785TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size);786if (!BackwardReferencesHashChainFollowChosenPath(787argb, cache_bits, chosen_path, chosen_path_size, hash_chain,788refs_dst)) {789goto Error;790}791ok = 1;792Error:793WebPSafeFree(dist_array);794return ok;795}796797798