Path: blob/master/3rdparty/libwebp/src/enc/backward_references_cost_enc.c
16344 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>1718#include "src/enc/backward_references_enc.h"19#include "src/enc/histogram_enc.h"20#include "src/dsp/lossless_common.h"21#include "src/utils/color_cache_utils.h"22#include "src/utils/utils.h"2324#define VALUES_IN_BYTE 2562526extern void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs);27extern int VP8LDistanceToPlaneCode(int xsize, int dist);28extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,29const PixOrCopy v);3031typedef struct {32double alpha_[VALUES_IN_BYTE];33double red_[VALUES_IN_BYTE];34double blue_[VALUES_IN_BYTE];35double distance_[NUM_DISTANCE_CODES];36double* literal_;37} CostModel;3839static void ConvertPopulationCountTableToBitEstimates(40int num_symbols, const uint32_t population_counts[], double output[]) {41uint32_t sum = 0;42int nonzeros = 0;43int i;44for (i = 0; i < num_symbols; ++i) {45sum += population_counts[i];46if (population_counts[i] > 0) {47++nonzeros;48}49}50if (nonzeros <= 1) {51memset(output, 0, num_symbols * sizeof(*output));52} else {53const double logsum = VP8LFastLog2(sum);54for (i = 0; i < num_symbols; ++i) {55output[i] = logsum - VP8LFastLog2(population_counts[i]);56}57}58}5960static int CostModelBuild(CostModel* const m, int xsize, int cache_bits,61const VP8LBackwardRefs* const refs) {62int ok = 0;63VP8LRefsCursor c = VP8LRefsCursorInit(refs);64VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits);65if (histo == NULL) goto Error;6667// The following code is similar to VP8LHistogramCreate but converts the68// distance to plane code.69VP8LHistogramInit(histo, cache_bits);70while (VP8LRefsCursorOk(&c)) {71VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos, VP8LDistanceToPlaneCode,72xsize);73VP8LRefsCursorNext(&c);74}7576ConvertPopulationCountTableToBitEstimates(77VP8LHistogramNumCodes(histo->palette_code_bits_),78histo->literal_, m->literal_);79ConvertPopulationCountTableToBitEstimates(80VALUES_IN_BYTE, histo->red_, m->red_);81ConvertPopulationCountTableToBitEstimates(82VALUES_IN_BYTE, histo->blue_, m->blue_);83ConvertPopulationCountTableToBitEstimates(84VALUES_IN_BYTE, histo->alpha_, m->alpha_);85ConvertPopulationCountTableToBitEstimates(86NUM_DISTANCE_CODES, histo->distance_, m->distance_);87ok = 1;8889Error:90VP8LFreeHistogram(histo);91return ok;92}9394static WEBP_INLINE double GetLiteralCost(const CostModel* const m, uint32_t v) {95return m->alpha_[v >> 24] +96m->red_[(v >> 16) & 0xff] +97m->literal_[(v >> 8) & 0xff] +98m->blue_[v & 0xff];99}100101static WEBP_INLINE double GetCacheCost(const CostModel* const m, uint32_t idx) {102const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx;103return m->literal_[literal_idx];104}105106static WEBP_INLINE double GetLengthCost(const CostModel* const m,107uint32_t length) {108int code, extra_bits;109VP8LPrefixEncodeBits(length, &code, &extra_bits);110return m->literal_[VALUES_IN_BYTE + code] + extra_bits;111}112113static WEBP_INLINE double GetDistanceCost(const CostModel* const m,114uint32_t distance) {115int code, extra_bits;116VP8LPrefixEncodeBits(distance, &code, &extra_bits);117return m->distance_[code] + extra_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,123float prev_cost, float* const cost, uint16_t* const dist_array) {124double 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 color129const double mul0 = 0.68;130cost_val += GetCacheCost(cost_model, ix) * mul0;131} else {132const double mul1 = 0.82;133if (use_color_cache) VP8LColorCacheInsert(hashers, color);134cost_val += GetLiteralCost(cost_model, color) * mul1;135}136if (cost[idx] > cost_val) {137cost[idx] = (float)cost_val;138dist_array[idx] = 1; // only one is inserted.139}140}141142// -----------------------------------------------------------------------------143// CostManager and interval handling144145// Empirical value to avoid high memory consumption but good for performance.146#define COST_CACHE_INTERVAL_SIZE_MAX 500147148// To perform backward reference every pixel at index index_ is considered and149// the cost for the MAX_LENGTH following pixels computed. Those following pixels150// at index index_ + k (k from 0 to MAX_LENGTH) have a cost of:151// cost_ = distance cost at index + GetLengthCost(cost_model, k)152// and the minimum value is kept. GetLengthCost(cost_model, k) is cached in an153// array of size MAX_LENGTH.154// Instead of performing MAX_LENGTH comparisons per pixel, we keep track of the155// minimal values using intervals of constant cost.156// An interval is defined by the index_ of the pixel that generated it and157// is only useful in a range of indices from start_ to end_ (exclusive), i.e.158// it contains the minimum value for pixels between start_ and end_.159// Intervals are stored in a linked list and ordered by start_. When a new160// interval has a better value, old intervals are split or removed. There are161// therefore no overlapping intervals.162typedef struct CostInterval CostInterval;163struct CostInterval {164float cost_;165int start_;166int end_;167int index_;168CostInterval* previous_;169CostInterval* next_;170};171172// The GetLengthCost(cost_model, k) are cached in a CostCacheInterval.173typedef struct {174double cost_;175int start_;176int end_; // Exclusive.177} CostCacheInterval;178179// This structure is in charge of managing intervals and costs.180// It caches the different CostCacheInterval, caches the different181// GetLengthCost(cost_model, k) in cost_cache_ and the CostInterval's (whose182// count_ is limited by COST_CACHE_INTERVAL_SIZE_MAX).183#define COST_MANAGER_MAX_FREE_LIST 10184typedef struct {185CostInterval* head_;186int count_; // The number of stored intervals.187CostCacheInterval* cache_intervals_;188size_t cache_intervals_size_;189double cost_cache_[MAX_LENGTH]; // Contains the GetLengthCost(cost_model, k).190float* costs_;191uint16_t* dist_array_;192// Most of the time, we only need few intervals -> use a free-list, to avoid193// fragmentation with small allocs in most common cases.194CostInterval intervals_[COST_MANAGER_MAX_FREE_LIST];195CostInterval* free_intervals_;196// These are regularly malloc'd remains. This list can't grow larger than than197// size COST_CACHE_INTERVAL_SIZE_MAX - COST_MANAGER_MAX_FREE_LIST, note.198CostInterval* recycled_intervals_;199} CostManager;200201static void CostIntervalAddToFreeList(CostManager* const manager,202CostInterval* const interval) {203interval->next_ = manager->free_intervals_;204manager->free_intervals_ = interval;205}206207static int CostIntervalIsInFreeList(const CostManager* const manager,208const CostInterval* const interval) {209return (interval >= &manager->intervals_[0] &&210interval <= &manager->intervals_[COST_MANAGER_MAX_FREE_LIST - 1]);211}212213static void CostManagerInitFreeList(CostManager* const manager) {214int i;215manager->free_intervals_ = NULL;216for (i = 0; i < COST_MANAGER_MAX_FREE_LIST; ++i) {217CostIntervalAddToFreeList(manager, &manager->intervals_[i]);218}219}220221static void DeleteIntervalList(CostManager* const manager,222const CostInterval* interval) {223while (interval != NULL) {224const CostInterval* const next = interval->next_;225if (!CostIntervalIsInFreeList(manager, interval)) {226WebPSafeFree((void*)interval);227} // else: do nothing228interval = next;229}230}231232static void CostManagerClear(CostManager* const manager) {233if (manager == NULL) return;234235WebPSafeFree(manager->costs_);236WebPSafeFree(manager->cache_intervals_);237238// Clear the interval lists.239DeleteIntervalList(manager, manager->head_);240manager->head_ = NULL;241DeleteIntervalList(manager, manager->recycled_intervals_);242manager->recycled_intervals_ = NULL;243244// Reset pointers, count_ and cache_intervals_size_.245memset(manager, 0, sizeof(*manager));246CostManagerInitFreeList(manager);247}248249static int CostManagerInit(CostManager* const manager,250uint16_t* const dist_array, int pix_count,251const CostModel* const cost_model) {252int i;253const int cost_cache_size = (pix_count > MAX_LENGTH) ? MAX_LENGTH : pix_count;254255manager->costs_ = NULL;256manager->cache_intervals_ = NULL;257manager->head_ = NULL;258manager->recycled_intervals_ = NULL;259manager->count_ = 0;260manager->dist_array_ = dist_array;261CostManagerInitFreeList(manager);262263// Fill in the cost_cache_.264manager->cache_intervals_size_ = 1;265manager->cost_cache_[0] = GetLengthCost(cost_model, 0);266for (i = 1; i < cost_cache_size; ++i) {267manager->cost_cache_[i] = GetLengthCost(cost_model, i);268// Get the number of bound intervals.269if (manager->cost_cache_[i] != manager->cost_cache_[i - 1]) {270++manager->cache_intervals_size_;271}272}273274// With the current cost model, we usually have below 20 intervals.275// The worst case scenario with a cost model would be if every length has a276// different cost, hence MAX_LENGTH but that is impossible with the current277// implementation that spirals around a pixel.278assert(manager->cache_intervals_size_ <= MAX_LENGTH);279manager->cache_intervals_ = (CostCacheInterval*)WebPSafeMalloc(280manager->cache_intervals_size_, sizeof(*manager->cache_intervals_));281if (manager->cache_intervals_ == NULL) {282CostManagerClear(manager);283return 0;284}285286// Fill in the cache_intervals_.287{288CostCacheInterval* cur = manager->cache_intervals_;289290// Consecutive values in cost_cache_ are compared and if a big enough291// difference is found, a new interval is created and bounded.292cur->start_ = 0;293cur->end_ = 1;294cur->cost_ = manager->cost_cache_[0];295for (i = 1; i < cost_cache_size; ++i) {296const double cost_val = manager->cost_cache_[i];297if (cost_val != cur->cost_) {298++cur;299// Initialize an interval.300cur->start_ = i;301cur->cost_ = cost_val;302}303cur->end_ = i + 1;304}305}306307manager->costs_ = (float*)WebPSafeMalloc(pix_count, sizeof(*manager->costs_));308if (manager->costs_ == NULL) {309CostManagerClear(manager);310return 0;311}312// Set the initial costs_ high for every pixel as we will keep the minimum.313for (i = 0; i < pix_count; ++i) manager->costs_[i] = 1e38f;314315return 1;316}317318// Given the cost and the position that define an interval, update the cost at319// pixel 'i' if it is smaller than the previously computed value.320static WEBP_INLINE void UpdateCost(CostManager* const manager, int i,321int position, float cost) {322const int k = i - position;323assert(k >= 0 && k < MAX_LENGTH);324325if (manager->costs_[i] > cost) {326manager->costs_[i] = cost;327manager->dist_array_[i] = k + 1;328}329}330331// Given the cost and the position that define an interval, update the cost for332// all the pixels between 'start' and 'end' excluded.333static WEBP_INLINE void UpdateCostPerInterval(CostManager* const manager,334int start, int end, int position,335float cost) {336int i;337for (i = start; i < end; ++i) UpdateCost(manager, i, position, cost);338}339340// Given two intervals, make 'prev' be the previous one of 'next' in 'manager'.341static WEBP_INLINE void ConnectIntervals(CostManager* const manager,342CostInterval* const prev,343CostInterval* const next) {344if (prev != NULL) {345prev->next_ = next;346} else {347manager->head_ = next;348}349350if (next != NULL) next->previous_ = prev;351}352353// Pop an interval in the manager.354static WEBP_INLINE void PopInterval(CostManager* const manager,355CostInterval* const interval) {356if (interval == NULL) return;357358ConnectIntervals(manager, interval->previous_, interval->next_);359if (CostIntervalIsInFreeList(manager, interval)) {360CostIntervalAddToFreeList(manager, interval);361} else { // recycle regularly malloc'd intervals too362interval->next_ = manager->recycled_intervals_;363manager->recycled_intervals_ = interval;364}365--manager->count_;366assert(manager->count_ >= 0);367}368369// Update the cost at index i by going over all the stored intervals that370// overlap with i.371// If 'do_clean_intervals' is set to something different than 0, intervals that372// end before 'i' will be popped.373static WEBP_INLINE void UpdateCostAtIndex(CostManager* const manager, int i,374int do_clean_intervals) {375CostInterval* current = manager->head_;376377while (current != NULL && current->start_ <= i) {378CostInterval* const next = current->next_;379if (current->end_ <= i) {380if (do_clean_intervals) {381// We have an outdated interval, remove it.382PopInterval(manager, current);383}384} else {385UpdateCost(manager, i, current->index_, current->cost_);386}387current = next;388}389}390391// Given a current orphan interval and its previous interval, before392// it was orphaned (which can be NULL), set it at the right place in the list393// of intervals using the start_ ordering and the previous interval as a hint.394static WEBP_INLINE void PositionOrphanInterval(CostManager* const manager,395CostInterval* const current,396CostInterval* previous) {397assert(current != NULL);398399if (previous == NULL) previous = manager->head_;400while (previous != NULL && current->start_ < previous->start_) {401previous = previous->previous_;402}403while (previous != NULL && previous->next_ != NULL &&404previous->next_->start_ < current->start_) {405previous = previous->next_;406}407408if (previous != NULL) {409ConnectIntervals(manager, current, previous->next_);410} else {411ConnectIntervals(manager, current, manager->head_);412}413ConnectIntervals(manager, previous, current);414}415416// Insert an interval in the list contained in the manager by starting at417// interval_in as a hint. The intervals are sorted by start_ value.418static WEBP_INLINE void InsertInterval(CostManager* const manager,419CostInterval* const interval_in,420float cost, int position, int start,421int end) {422CostInterval* interval_new;423424if (start >= end) return;425if (manager->count_ >= COST_CACHE_INTERVAL_SIZE_MAX) {426// Serialize the interval if we cannot store it.427UpdateCostPerInterval(manager, start, end, position, cost);428return;429}430if (manager->free_intervals_ != NULL) {431interval_new = manager->free_intervals_;432manager->free_intervals_ = interval_new->next_;433} else if (manager->recycled_intervals_ != NULL) {434interval_new = manager->recycled_intervals_;435manager->recycled_intervals_ = interval_new->next_;436} else { // malloc for good437interval_new = (CostInterval*)WebPSafeMalloc(1, sizeof(*interval_new));438if (interval_new == NULL) {439// Write down the interval if we cannot create it.440UpdateCostPerInterval(manager, start, end, position, cost);441return;442}443}444445interval_new->cost_ = cost;446interval_new->index_ = position;447interval_new->start_ = start;448interval_new->end_ = end;449PositionOrphanInterval(manager, interval_new, interval_in);450451++manager->count_;452}453454// Given a new cost interval defined by its start at position, its length value455// and distance_cost, add its contributions to the previous intervals and costs.456// If handling the interval or one of its subintervals becomes to heavy, its457// contribution is added to the costs right away.458static WEBP_INLINE void PushInterval(CostManager* const manager,459double distance_cost, int position,460int len) {461size_t i;462CostInterval* interval = manager->head_;463CostInterval* interval_next;464const CostCacheInterval* const cost_cache_intervals =465manager->cache_intervals_;466// If the interval is small enough, no need to deal with the heavy467// interval logic, just serialize it right away. This constant is empirical.468const int kSkipDistance = 10;469470if (len < kSkipDistance) {471int j;472for (j = position; j < position + len; ++j) {473const int k = j - position;474float cost_tmp;475assert(k >= 0 && k < MAX_LENGTH);476cost_tmp = (float)(distance_cost + manager->cost_cache_[k]);477478if (manager->costs_[j] > cost_tmp) {479manager->costs_[j] = cost_tmp;480manager->dist_array_[j] = k + 1;481}482}483return;484}485486for (i = 0; i < manager->cache_intervals_size_ &&487cost_cache_intervals[i].start_ < len;488++i) {489// Define the intersection of the ith interval with the new one.490int start = position + cost_cache_intervals[i].start_;491const int end = position + (cost_cache_intervals[i].end_ > len492? len493: cost_cache_intervals[i].end_);494const float cost = (float)(distance_cost + cost_cache_intervals[i].cost_);495496for (; interval != NULL && interval->start_ < end;497interval = interval_next) {498interval_next = interval->next_;499500// Make sure we have some overlap501if (start >= interval->end_) continue;502503if (cost >= interval->cost_) {504// When intervals are represented, the lower, the better.505// [**********************************************************[506// start end507// [----------------------------------[508// interval->start_ interval->end_509// If we are worse than what we already have, add whatever we have so510// far up to interval.511const int start_new = interval->end_;512InsertInterval(manager, interval, cost, position, start,513interval->start_);514start = start_new;515if (start >= end) break;516continue;517}518519if (start <= interval->start_) {520if (interval->end_ <= end) {521// [----------------------------------[522// interval->start_ interval->end_523// [**************************************************************[524// start end525// We can safely remove the old interval as it is fully included.526PopInterval(manager, interval);527} else {528// [------------------------------------[529// interval->start_ interval->end_530// [*****************************[531// start end532interval->start_ = end;533break;534}535} else {536if (end < interval->end_) {537// [--------------------------------------------------------------[538// interval->start_ interval->end_539// [*****************************[540// start end541// We have to split the old interval as it fully contains the new one.542const int end_original = interval->end_;543interval->end_ = start;544InsertInterval(manager, interval, interval->cost_, interval->index_,545end, end_original);546interval = interval->next_;547break;548} else {549// [------------------------------------[550// interval->start_ interval->end_551// [*****************************[552// start end553interval->end_ = start;554}555}556}557// Insert the remaining interval from start to end.558InsertInterval(manager, interval, cost, position, start, end);559}560}561562static int BackwardReferencesHashChainDistanceOnly(563int xsize, int ysize, const uint32_t* const argb, int cache_bits,564const VP8LHashChain* const hash_chain, const VP8LBackwardRefs* const refs,565uint16_t* const dist_array) {566int i;567int ok = 0;568int cc_init = 0;569const int pix_count = xsize * ysize;570const int use_color_cache = (cache_bits > 0);571const size_t literal_array_size =572sizeof(double) * (NUM_LITERAL_CODES + NUM_LENGTH_CODES +573((cache_bits > 0) ? (1 << cache_bits) : 0));574const size_t cost_model_size = sizeof(CostModel) + literal_array_size;575CostModel* const cost_model =576(CostModel*)WebPSafeCalloc(1ULL, cost_model_size);577VP8LColorCache hashers;578CostManager* cost_manager =579(CostManager*)WebPSafeMalloc(1ULL, sizeof(*cost_manager));580int offset_prev = -1, len_prev = -1;581double offset_cost = -1;582int first_offset_is_constant = -1; // initialized with 'impossible' value583int reach = 0;584585if (cost_model == NULL || cost_manager == NULL) goto Error;586587cost_model->literal_ = (double*)(cost_model + 1);588if (use_color_cache) {589cc_init = VP8LColorCacheInit(&hashers, cache_bits);590if (!cc_init) goto Error;591}592593if (!CostModelBuild(cost_model, xsize, cache_bits, refs)) {594goto Error;595}596597if (!CostManagerInit(cost_manager, dist_array, pix_count, cost_model)) {598goto Error;599}600601// We loop one pixel at a time, but store all currently best points to602// non-processed locations from this point.603dist_array[0] = 0;604// Add first pixel as literal.605AddSingleLiteralWithCostModel(argb, &hashers, cost_model, 0, use_color_cache,6060.f, cost_manager->costs_, dist_array);607608for (i = 1; i < pix_count; ++i) {609const float prev_cost = cost_manager->costs_[i - 1];610int offset, len;611VP8LHashChainFindCopy(hash_chain, i, &offset, &len);612613// Try adding the pixel as a literal.614AddSingleLiteralWithCostModel(argb, &hashers, cost_model, i,615use_color_cache, prev_cost,616cost_manager->costs_, dist_array);617618// If we are dealing with a non-literal.619if (len >= 2) {620if (offset != offset_prev) {621const int code = VP8LDistanceToPlaneCode(xsize, offset);622offset_cost = GetDistanceCost(cost_model, code);623first_offset_is_constant = 1;624PushInterval(cost_manager, prev_cost + offset_cost, i, len);625} else {626assert(offset_cost >= 0);627assert(len_prev >= 0);628assert(first_offset_is_constant == 0 || first_offset_is_constant == 1);629// Instead of considering all contributions from a pixel i by calling:630// PushInterval(cost_manager, prev_cost + offset_cost, i, len);631// we optimize these contributions in case offset_cost stays the same632// for consecutive pixels. This describes a set of pixels similar to a633// previous set (e.g. constant color regions).634if (first_offset_is_constant) {635reach = i - 1 + len_prev - 1;636first_offset_is_constant = 0;637}638639if (i + len - 1 > reach) {640// We can only be go further with the same offset if the previous641// length was maxed, hence len_prev == len == MAX_LENGTH.642// TODO(vrabaud), bump i to the end right away (insert cache and643// update cost).644// TODO(vrabaud), check if one of the points in between does not have645// a lower cost.646// Already consider the pixel at "reach" to add intervals that are647// better than whatever we add.648int offset_j, len_j = 0;649int j;650assert(len == MAX_LENGTH || len == pix_count - i);651// Figure out the last consecutive pixel within [i, reach + 1] with652// the same offset.653for (j = i; j <= reach; ++j) {654VP8LHashChainFindCopy(hash_chain, j + 1, &offset_j, &len_j);655if (offset_j != offset) {656VP8LHashChainFindCopy(hash_chain, j, &offset_j, &len_j);657break;658}659}660// Update the cost at j - 1 and j.661UpdateCostAtIndex(cost_manager, j - 1, 0);662UpdateCostAtIndex(cost_manager, j, 0);663664PushInterval(cost_manager, cost_manager->costs_[j - 1] + offset_cost,665j, len_j);666reach = j + len_j - 1;667}668}669}670671UpdateCostAtIndex(cost_manager, i, 1);672offset_prev = offset;673len_prev = len;674}675676ok = !refs->error_;677Error:678if (cc_init) VP8LColorCacheClear(&hashers);679CostManagerClear(cost_manager);680WebPSafeFree(cost_model);681WebPSafeFree(cost_manager);682return ok;683}684685// We pack the path at the end of *dist_array and return686// a pointer to this part of the array. Example:687// dist_array = [1x2xx3x2] => packed [1x2x1232], chosen_path = [1232]688static void TraceBackwards(uint16_t* const dist_array,689int dist_array_size,690uint16_t** const chosen_path,691int* const chosen_path_size) {692uint16_t* path = dist_array + dist_array_size;693uint16_t* cur = dist_array + dist_array_size - 1;694while (cur >= dist_array) {695const int k = *cur;696--path;697*path = k;698cur -= k;699}700*chosen_path = path;701*chosen_path_size = (int)(dist_array + dist_array_size - path);702}703704static int BackwardReferencesHashChainFollowChosenPath(705const uint32_t* const argb, int cache_bits,706const uint16_t* const chosen_path, int chosen_path_size,707const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs) {708const int use_color_cache = (cache_bits > 0);709int ix;710int i = 0;711int ok = 0;712int cc_init = 0;713VP8LColorCache hashers;714715if (use_color_cache) {716cc_init = VP8LColorCacheInit(&hashers, cache_bits);717if (!cc_init) goto Error;718}719720VP8LClearBackwardRefs(refs);721for (ix = 0; ix < chosen_path_size; ++ix) {722const int len = chosen_path[ix];723if (len != 1) {724int k;725const int offset = VP8LHashChainFindOffset(hash_chain, i);726VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));727if (use_color_cache) {728for (k = 0; k < len; ++k) {729VP8LColorCacheInsert(&hashers, argb[i + k]);730}731}732i += len;733} else {734PixOrCopy v;735const int idx =736use_color_cache ? VP8LColorCacheContains(&hashers, argb[i]) : -1;737if (idx >= 0) {738// use_color_cache is true and hashers contains argb[i]739// push pixel as a color cache index740v = PixOrCopyCreateCacheIdx(idx);741} else {742if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);743v = PixOrCopyCreateLiteral(argb[i]);744}745VP8LBackwardRefsCursorAdd(refs, v);746++i;747}748}749ok = !refs->error_;750Error:751if (cc_init) VP8LColorCacheClear(&hashers);752return ok;753}754755// Returns 1 on success.756extern int VP8LBackwardReferencesTraceBackwards(757int xsize, int ysize, const uint32_t* const argb, int cache_bits,758const VP8LHashChain* const hash_chain,759const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst);760int VP8LBackwardReferencesTraceBackwards(int xsize, int ysize,761const uint32_t* const argb,762int cache_bits,763const VP8LHashChain* const hash_chain,764const VP8LBackwardRefs* const refs_src,765VP8LBackwardRefs* const refs_dst) {766int ok = 0;767const int dist_array_size = xsize * ysize;768uint16_t* chosen_path = NULL;769int chosen_path_size = 0;770uint16_t* dist_array =771(uint16_t*)WebPSafeMalloc(dist_array_size, sizeof(*dist_array));772773if (dist_array == NULL) goto Error;774775if (!BackwardReferencesHashChainDistanceOnly(776xsize, ysize, argb, cache_bits, hash_chain, refs_src, dist_array)) {777goto Error;778}779TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size);780if (!BackwardReferencesHashChainFollowChosenPath(781argb, cache_bits, chosen_path, chosen_path_size, hash_chain,782refs_dst)) {783goto Error;784}785ok = 1;786Error:787WebPSafeFree(dist_array);788return ok;789}790791792