• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 Google Inc. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the COPYING file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 // -----------------------------------------------------------------------------
9 //
10 // Improves a given set of backward references by analyzing its bit cost.
11 // The algorithm is similar to the Zopfli compression algorithm but tailored to
12 // images.
13 //
14 // Author: Vincent Rabaud (vrabaud@google.com)
15 //
16 
17 #include <assert.h>
18 #include <string.h>
19 
20 #include "src/dsp/lossless_common.h"
21 #include "src/enc/backward_references_enc.h"
22 #include "src/enc/histogram_enc.h"
23 #include "src/utils/color_cache_utils.h"
24 #include "src/utils/utils.h"
25 
26 #define VALUES_IN_BYTE 256
27 
28 extern void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs);
29 extern int VP8LDistanceToPlaneCode(int xsize, int dist);
30 extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,
31                                       const PixOrCopy v);
32 
33 typedef struct {
34   uint32_t alpha_[VALUES_IN_BYTE];
35   uint32_t red_[VALUES_IN_BYTE];
36   uint32_t blue_[VALUES_IN_BYTE];
37   uint32_t distance_[NUM_DISTANCE_CODES];
38   uint32_t* literal_;
39 } CostModel;
40 
ConvertPopulationCountTableToBitEstimates(int num_symbols,const uint32_t population_counts[],uint32_t output[])41 static void ConvertPopulationCountTableToBitEstimates(
42     int num_symbols, const uint32_t population_counts[], uint32_t output[]) {
43   uint32_t sum = 0;
44   int nonzeros = 0;
45   int i;
46   for (i = 0; i < num_symbols; ++i) {
47     sum += population_counts[i];
48     if (population_counts[i] > 0) {
49       ++nonzeros;
50     }
51   }
52   if (nonzeros <= 1) {
53     memset(output, 0, num_symbols * sizeof(*output));
54   } else {
55     const uint32_t logsum = VP8LFastLog2(sum);
56     for (i = 0; i < num_symbols; ++i) {
57       output[i] = logsum - VP8LFastLog2(population_counts[i]);
58     }
59   }
60 }
61 
CostModelBuild(CostModel * const m,int xsize,int cache_bits,const VP8LBackwardRefs * const refs)62 static int CostModelBuild(CostModel* const m, int xsize, int cache_bits,
63                           const VP8LBackwardRefs* const refs) {
64   int ok = 0;
65   VP8LRefsCursor c = VP8LRefsCursorInit(refs);
66   VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits);
67   if (histo == NULL) goto Error;
68 
69   // The following code is similar to VP8LHistogramCreate but converts the
70   // distance to plane code.
71   VP8LHistogramInit(histo, cache_bits, /*init_arrays=*/ 1);
72   while (VP8LRefsCursorOk(&c)) {
73     VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos, VP8LDistanceToPlaneCode,
74                                     xsize);
75     VP8LRefsCursorNext(&c);
76   }
77 
78   ConvertPopulationCountTableToBitEstimates(
79       VP8LHistogramNumCodes(histo->palette_code_bits_), histo->literal_,
80       m->literal_);
81   ConvertPopulationCountTableToBitEstimates(
82       VALUES_IN_BYTE, histo->red_, m->red_);
83   ConvertPopulationCountTableToBitEstimates(
84       VALUES_IN_BYTE, histo->blue_, m->blue_);
85   ConvertPopulationCountTableToBitEstimates(
86       VALUES_IN_BYTE, histo->alpha_, m->alpha_);
87   ConvertPopulationCountTableToBitEstimates(
88       NUM_DISTANCE_CODES, histo->distance_, m->distance_);
89   ok = 1;
90 
91  Error:
92   VP8LFreeHistogram(histo);
93   return ok;
94 }
95 
GetLiteralCost(const CostModel * const m,uint32_t v)96 static WEBP_INLINE int64_t GetLiteralCost(const CostModel* const m,
97                                           uint32_t v) {
98   return (int64_t)m->alpha_[v >> 24] + m->red_[(v >> 16) & 0xff] +
99          m->literal_[(v >> 8) & 0xff] + m->blue_[v & 0xff];
100 }
101 
GetCacheCost(const CostModel * const m,uint32_t idx)102 static WEBP_INLINE int64_t GetCacheCost(const CostModel* const m,
103                                         uint32_t idx) {
104   const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx;
105   return (int64_t)m->literal_[literal_idx];
106 }
107 
GetLengthCost(const CostModel * const m,uint32_t length)108 static WEBP_INLINE int64_t GetLengthCost(const CostModel* const m,
109                                          uint32_t length) {
110   int code, extra_bits;
111   VP8LPrefixEncodeBits(length, &code, &extra_bits);
112   return (int64_t)m->literal_[VALUES_IN_BYTE + code] +
113          ((int64_t)extra_bits << LOG_2_PRECISION_BITS);
114 }
115 
GetDistanceCost(const CostModel * const m,uint32_t distance)116 static WEBP_INLINE int64_t GetDistanceCost(const CostModel* const m,
117                                            uint32_t distance) {
118   int code, extra_bits;
119   VP8LPrefixEncodeBits(distance, &code, &extra_bits);
120   return (int64_t)m->distance_[code] +
121          ((int64_t)extra_bits << LOG_2_PRECISION_BITS);
122 }
123 
AddSingleLiteralWithCostModel(const uint32_t * const argb,VP8LColorCache * const hashers,const CostModel * const cost_model,int idx,int use_color_cache,int64_t prev_cost,int64_t * const cost,uint16_t * const dist_array)124 static WEBP_INLINE void AddSingleLiteralWithCostModel(
125     const uint32_t* const argb, VP8LColorCache* const hashers,
126     const CostModel* const cost_model, int idx, int use_color_cache,
127     int64_t prev_cost, int64_t* const cost, uint16_t* const dist_array) {
128   int64_t cost_val = prev_cost;
129   const uint32_t color = argb[idx];
130   const int ix = use_color_cache ? VP8LColorCacheContains(hashers, color) : -1;
131   if (ix >= 0) {
132     // use_color_cache is true and hashers contains color
133     cost_val += DivRound(GetCacheCost(cost_model, ix) * 68, 100);
134   } else {
135     if (use_color_cache) VP8LColorCacheInsert(hashers, color);
136     cost_val += DivRound(GetLiteralCost(cost_model, color) * 82, 100);
137   }
138   if (cost[idx] > cost_val) {
139     cost[idx] = cost_val;
140     dist_array[idx] = 1;  // only one is inserted.
141   }
142 }
143 
144 // -----------------------------------------------------------------------------
145 // CostManager and interval handling
146 
147 // Empirical value to avoid high memory consumption but good for performance.
148 #define COST_CACHE_INTERVAL_SIZE_MAX 500
149 
150 // To perform backward reference every pixel at index index_ is considered and
151 // the cost for the MAX_LENGTH following pixels computed. Those following pixels
152 // at index index_ + k (k from 0 to MAX_LENGTH) have a cost of:
153 //     cost_ = distance cost at index + GetLengthCost(cost_model, k)
154 // and the minimum value is kept. GetLengthCost(cost_model, k) is cached in an
155 // array of size MAX_LENGTH.
156 // Instead of performing MAX_LENGTH comparisons per pixel, we keep track of the
157 // minimal values using intervals of constant cost.
158 // An interval is defined by the index_ of the pixel that generated it and
159 // is only useful in a range of indices from start_ to end_ (exclusive), i.e.
160 // it contains the minimum value for pixels between start_ and end_.
161 // Intervals are stored in a linked list and ordered by start_. When a new
162 // interval has a better value, old intervals are split or removed. There are
163 // therefore no overlapping intervals.
164 typedef struct CostInterval CostInterval;
165 struct CostInterval {
166   int64_t cost_;
167   int start_;
168   int end_;
169   int index_;
170   CostInterval* previous_;
171   CostInterval* next_;
172 };
173 
174 // The GetLengthCost(cost_model, k) are cached in a CostCacheInterval.
175 typedef struct {
176   int64_t cost_;
177   int start_;
178   int end_;       // Exclusive.
179 } CostCacheInterval;
180 
181 // This structure is in charge of managing intervals and costs.
182 // It caches the different CostCacheInterval, caches the different
183 // GetLengthCost(cost_model, k) in cost_cache_ and the CostInterval's (whose
184 // count_ is limited by COST_CACHE_INTERVAL_SIZE_MAX).
185 #define COST_MANAGER_MAX_FREE_LIST 10
186 typedef struct {
187   CostInterval* head_;
188   int count_;  // The number of stored intervals.
189   CostCacheInterval* cache_intervals_;
190   size_t cache_intervals_size_;
191   // Contains the GetLengthCost(cost_model, k).
192   int64_t cost_cache_[MAX_LENGTH];
193   int64_t* costs_;
194   uint16_t* dist_array_;
195   // Most of the time, we only need few intervals -> use a free-list, to avoid
196   // fragmentation with small allocs in most common cases.
197   CostInterval intervals_[COST_MANAGER_MAX_FREE_LIST];
198   CostInterval* free_intervals_;
199   // These are regularly malloc'd remains. This list can't grow larger than than
200   // size COST_CACHE_INTERVAL_SIZE_MAX - COST_MANAGER_MAX_FREE_LIST, note.
201   CostInterval* recycled_intervals_;
202 } CostManager;
203 
CostIntervalAddToFreeList(CostManager * const manager,CostInterval * const interval)204 static void CostIntervalAddToFreeList(CostManager* const manager,
205                                       CostInterval* const interval) {
206   interval->next_ = manager->free_intervals_;
207   manager->free_intervals_ = interval;
208 }
209 
CostIntervalIsInFreeList(const CostManager * const manager,const CostInterval * const interval)210 static int CostIntervalIsInFreeList(const CostManager* const manager,
211                                     const CostInterval* const interval) {
212   return (interval >= &manager->intervals_[0] &&
213           interval <= &manager->intervals_[COST_MANAGER_MAX_FREE_LIST - 1]);
214 }
215 
CostManagerInitFreeList(CostManager * const manager)216 static void CostManagerInitFreeList(CostManager* const manager) {
217   int i;
218   manager->free_intervals_ = NULL;
219   for (i = 0; i < COST_MANAGER_MAX_FREE_LIST; ++i) {
220     CostIntervalAddToFreeList(manager, &manager->intervals_[i]);
221   }
222 }
223 
DeleteIntervalList(CostManager * const manager,const CostInterval * interval)224 static void DeleteIntervalList(CostManager* const manager,
225                                const CostInterval* interval) {
226   while (interval != NULL) {
227     const CostInterval* const next = interval->next_;
228     if (!CostIntervalIsInFreeList(manager, interval)) {
229       WebPSafeFree((void*)interval);
230     }  // else: do nothing
231     interval = next;
232   }
233 }
234 
CostManagerClear(CostManager * const manager)235 static void CostManagerClear(CostManager* const manager) {
236   if (manager == NULL) return;
237 
238   WebPSafeFree(manager->costs_);
239   WebPSafeFree(manager->cache_intervals_);
240 
241   // Clear the interval lists.
242   DeleteIntervalList(manager, manager->head_);
243   manager->head_ = NULL;
244   DeleteIntervalList(manager, manager->recycled_intervals_);
245   manager->recycled_intervals_ = NULL;
246 
247   // Reset pointers, count_ and cache_intervals_size_.
248   memset(manager, 0, sizeof(*manager));
249   CostManagerInitFreeList(manager);
250 }
251 
CostManagerInit(CostManager * const manager,uint16_t * const dist_array,int pix_count,const CostModel * const cost_model)252 static int CostManagerInit(CostManager* const manager,
253                            uint16_t* const dist_array, int pix_count,
254                            const CostModel* const cost_model) {
255   int i;
256   const int cost_cache_size = (pix_count > MAX_LENGTH) ? MAX_LENGTH : pix_count;
257 
258   manager->costs_ = NULL;
259   manager->cache_intervals_ = NULL;
260   manager->head_ = NULL;
261   manager->recycled_intervals_ = NULL;
262   manager->count_ = 0;
263   manager->dist_array_ = dist_array;
264   CostManagerInitFreeList(manager);
265 
266   // Fill in the cost_cache_.
267   // Has to be done in two passes due to a GCC bug on i686
268   // related to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323
269   for (i = 0; i < cost_cache_size; ++i) {
270     manager->cost_cache_[i] = GetLengthCost(cost_model, i);
271   }
272   manager->cache_intervals_size_ = 1;
273   for (i = 1; i < cost_cache_size; ++i) {
274     // Get the number of bound intervals.
275     if (manager->cost_cache_[i] != manager->cost_cache_[i - 1]) {
276       ++manager->cache_intervals_size_;
277     }
278   }
279 
280   // With the current cost model, we usually have below 20 intervals.
281   // The worst case scenario with a cost model would be if every length has a
282   // different cost, hence MAX_LENGTH but that is impossible with the current
283   // implementation that spirals around a pixel.
284   assert(manager->cache_intervals_size_ <= MAX_LENGTH);
285   manager->cache_intervals_ = (CostCacheInterval*)WebPSafeMalloc(
286       manager->cache_intervals_size_, sizeof(*manager->cache_intervals_));
287   if (manager->cache_intervals_ == NULL) {
288     CostManagerClear(manager);
289     return 0;
290   }
291 
292   // Fill in the cache_intervals_.
293   {
294     CostCacheInterval* cur = manager->cache_intervals_;
295 
296     // Consecutive values in cost_cache_ are compared and if a big enough
297     // difference is found, a new interval is created and bounded.
298     cur->start_ = 0;
299     cur->end_ = 1;
300     cur->cost_ = manager->cost_cache_[0];
301     for (i = 1; i < cost_cache_size; ++i) {
302       const int64_t cost_val = manager->cost_cache_[i];
303       if (cost_val != cur->cost_) {
304         ++cur;
305         // Initialize an interval.
306         cur->start_ = i;
307         cur->cost_ = cost_val;
308       }
309       cur->end_ = i + 1;
310     }
311     assert((size_t)(cur - manager->cache_intervals_) + 1 ==
312            manager->cache_intervals_size_);
313   }
314 
315   manager->costs_ =
316       (int64_t*)WebPSafeMalloc(pix_count, sizeof(*manager->costs_));
317   if (manager->costs_ == NULL) {
318     CostManagerClear(manager);
319     return 0;
320   }
321   // Set the initial costs_ to INT64_MAX for every pixel as we will keep the
322   // minimum.
323   for (i = 0; i < pix_count; ++i) manager->costs_[i] = WEBP_INT64_MAX;
324 
325   return 1;
326 }
327 
328 // Given the cost and the position that define an interval, update the cost at
329 // pixel 'i' if it is smaller than the previously computed value.
UpdateCost(CostManager * const manager,int i,int position,int64_t cost)330 static WEBP_INLINE void UpdateCost(CostManager* const manager, int i,
331                                    int position, int64_t cost) {
332   const int k = i - position;
333   assert(k >= 0 && k < MAX_LENGTH);
334 
335   if (manager->costs_[i] > cost) {
336     manager->costs_[i] = cost;
337     manager->dist_array_[i] = k + 1;
338   }
339 }
340 
341 // Given the cost and the position that define an interval, update the cost for
342 // all the pixels between 'start' and 'end' excluded.
UpdateCostPerInterval(CostManager * const manager,int start,int end,int position,int64_t cost)343 static WEBP_INLINE void UpdateCostPerInterval(CostManager* const manager,
344                                               int start, int end, int position,
345                                               int64_t cost) {
346   int i;
347   for (i = start; i < end; ++i) UpdateCost(manager, i, position, cost);
348 }
349 
350 // Given two intervals, make 'prev' be the previous one of 'next' in 'manager'.
ConnectIntervals(CostManager * const manager,CostInterval * const prev,CostInterval * const next)351 static WEBP_INLINE void ConnectIntervals(CostManager* const manager,
352                                          CostInterval* const prev,
353                                          CostInterval* const next) {
354   if (prev != NULL) {
355     prev->next_ = next;
356   } else {
357     manager->head_ = next;
358   }
359 
360   if (next != NULL) next->previous_ = prev;
361 }
362 
363 // Pop an interval in the manager.
PopInterval(CostManager * const manager,CostInterval * const interval)364 static WEBP_INLINE void PopInterval(CostManager* const manager,
365                                     CostInterval* const interval) {
366   if (interval == NULL) return;
367 
368   ConnectIntervals(manager, interval->previous_, interval->next_);
369   if (CostIntervalIsInFreeList(manager, interval)) {
370     CostIntervalAddToFreeList(manager, interval);
371   } else {  // recycle regularly malloc'd intervals too
372     interval->next_ = manager->recycled_intervals_;
373     manager->recycled_intervals_ = interval;
374   }
375   --manager->count_;
376   assert(manager->count_ >= 0);
377 }
378 
379 // Update the cost at index i by going over all the stored intervals that
380 // overlap with i.
381 // If 'do_clean_intervals' is set to something different than 0, intervals that
382 // end before 'i' will be popped.
UpdateCostAtIndex(CostManager * const manager,int i,int do_clean_intervals)383 static WEBP_INLINE void UpdateCostAtIndex(CostManager* const manager, int i,
384                                           int do_clean_intervals) {
385   CostInterval* current = manager->head_;
386 
387   while (current != NULL && current->start_ <= i) {
388     CostInterval* const next = current->next_;
389     if (current->end_ <= i) {
390       if (do_clean_intervals) {
391         // We have an outdated interval, remove it.
392         PopInterval(manager, current);
393       }
394     } else {
395       UpdateCost(manager, i, current->index_, current->cost_);
396     }
397     current = next;
398   }
399 }
400 
401 // Given a current orphan interval and its previous interval, before
402 // it was orphaned (which can be NULL), set it at the right place in the list
403 // of intervals using the start_ ordering and the previous interval as a hint.
PositionOrphanInterval(CostManager * const manager,CostInterval * const current,CostInterval * previous)404 static WEBP_INLINE void PositionOrphanInterval(CostManager* const manager,
405                                                CostInterval* const current,
406                                                CostInterval* previous) {
407   assert(current != NULL);
408 
409   if (previous == NULL) previous = manager->head_;
410   while (previous != NULL && current->start_ < previous->start_) {
411     previous = previous->previous_;
412   }
413   while (previous != NULL && previous->next_ != NULL &&
414          previous->next_->start_ < current->start_) {
415     previous = previous->next_;
416   }
417 
418   if (previous != NULL) {
419     ConnectIntervals(manager, current, previous->next_);
420   } else {
421     ConnectIntervals(manager, current, manager->head_);
422   }
423   ConnectIntervals(manager, previous, current);
424 }
425 
426 // Insert an interval in the list contained in the manager by starting at
427 // interval_in as a hint. The intervals are sorted by start_ value.
InsertInterval(CostManager * const manager,CostInterval * const interval_in,int64_t cost,int position,int start,int end)428 static WEBP_INLINE void InsertInterval(CostManager* const manager,
429                                        CostInterval* const interval_in,
430                                        int64_t cost, int position, int start,
431                                        int end) {
432   CostInterval* interval_new;
433 
434   if (start >= end) return;
435   if (manager->count_ >= COST_CACHE_INTERVAL_SIZE_MAX) {
436     // Serialize the interval if we cannot store it.
437     UpdateCostPerInterval(manager, start, end, position, cost);
438     return;
439   }
440   if (manager->free_intervals_ != NULL) {
441     interval_new = manager->free_intervals_;
442     manager->free_intervals_ = interval_new->next_;
443   } else if (manager->recycled_intervals_ != NULL) {
444     interval_new = manager->recycled_intervals_;
445     manager->recycled_intervals_ = interval_new->next_;
446   } else {  // malloc for good
447     interval_new = (CostInterval*)WebPSafeMalloc(1, sizeof(*interval_new));
448     if (interval_new == NULL) {
449       // Write down the interval if we cannot create it.
450       UpdateCostPerInterval(manager, start, end, position, cost);
451       return;
452     }
453   }
454 
455   interval_new->cost_ = cost;
456   interval_new->index_ = position;
457   interval_new->start_ = start;
458   interval_new->end_ = end;
459   PositionOrphanInterval(manager, interval_new, interval_in);
460 
461   ++manager->count_;
462 }
463 
464 // Given a new cost interval defined by its start at position, its length value
465 // and distance_cost, add its contributions to the previous intervals and costs.
466 // If handling the interval or one of its subintervals becomes to heavy, its
467 // contribution is added to the costs right away.
PushInterval(CostManager * const manager,int64_t distance_cost,int position,int len)468 static WEBP_INLINE void PushInterval(CostManager* const manager,
469                                      int64_t distance_cost, int position,
470                                      int len) {
471   size_t i;
472   CostInterval* interval = manager->head_;
473   CostInterval* interval_next;
474   const CostCacheInterval* const cost_cache_intervals =
475       manager->cache_intervals_;
476   // If the interval is small enough, no need to deal with the heavy
477   // interval logic, just serialize it right away. This constant is empirical.
478   const int kSkipDistance = 10;
479 
480   if (len < kSkipDistance) {
481     int j;
482     for (j = position; j < position + len; ++j) {
483       const int k = j - position;
484       int64_t cost_tmp;
485       assert(k >= 0 && k < MAX_LENGTH);
486       cost_tmp = distance_cost + manager->cost_cache_[k];
487 
488       if (manager->costs_[j] > cost_tmp) {
489         manager->costs_[j] = cost_tmp;
490         manager->dist_array_[j] = k + 1;
491       }
492     }
493     return;
494   }
495 
496   for (i = 0; i < manager->cache_intervals_size_ &&
497               cost_cache_intervals[i].start_ < len;
498        ++i) {
499     // Define the intersection of the ith interval with the new one.
500     int start = position + cost_cache_intervals[i].start_;
501     const int end = position + (cost_cache_intervals[i].end_ > len
502                                  ? len
503                                  : cost_cache_intervals[i].end_);
504     const int64_t cost = distance_cost + cost_cache_intervals[i].cost_;
505 
506     for (; interval != NULL && interval->start_ < end;
507          interval = interval_next) {
508       interval_next = interval->next_;
509 
510       // Make sure we have some overlap
511       if (start >= interval->end_) continue;
512 
513       if (cost >= interval->cost_) {
514         // When intervals are represented, the lower, the better.
515         // [**********************************************************[
516         // start                                                    end
517         //                   [----------------------------------[
518         //                   interval->start_       interval->end_
519         // If we are worse than what we already have, add whatever we have so
520         // far up to interval.
521         const int start_new = interval->end_;
522         InsertInterval(manager, interval, cost, position, start,
523                        interval->start_);
524         start = start_new;
525         if (start >= end) break;
526         continue;
527       }
528 
529       if (start <= interval->start_) {
530         if (interval->end_ <= end) {
531           //                   [----------------------------------[
532           //                   interval->start_       interval->end_
533           // [**************************************************************[
534           // start                                                        end
535           // We can safely remove the old interval as it is fully included.
536           PopInterval(manager, interval);
537         } else {
538           //              [------------------------------------[
539           //              interval->start_        interval->end_
540           // [*****************************[
541           // start                       end
542           interval->start_ = end;
543           break;
544         }
545       } else {
546         if (end < interval->end_) {
547           // [--------------------------------------------------------------[
548           // interval->start_                                  interval->end_
549           //                     [*****************************[
550           //                     start                       end
551           // We have to split the old interval as it fully contains the new one.
552           const int end_original = interval->end_;
553           interval->end_ = start;
554           InsertInterval(manager, interval, interval->cost_, interval->index_,
555                          end, end_original);
556           interval = interval->next_;
557           break;
558         } else {
559           // [------------------------------------[
560           // interval->start_        interval->end_
561           //                     [*****************************[
562           //                     start                       end
563           interval->end_ = start;
564         }
565       }
566     }
567     // Insert the remaining interval from start to end.
568     InsertInterval(manager, interval, cost, position, start, end);
569   }
570 }
571 
BackwardReferencesHashChainDistanceOnly(int xsize,int ysize,const uint32_t * const argb,int cache_bits,const VP8LHashChain * const hash_chain,const VP8LBackwardRefs * const refs,uint16_t * const dist_array)572 static int BackwardReferencesHashChainDistanceOnly(
573     int xsize, int ysize, const uint32_t* const argb, int cache_bits,
574     const VP8LHashChain* const hash_chain, const VP8LBackwardRefs* const refs,
575     uint16_t* const dist_array) {
576   int i;
577   int ok = 0;
578   int cc_init = 0;
579   const int pix_count = xsize * ysize;
580   const int use_color_cache = (cache_bits > 0);
581   const size_t literal_array_size =
582       sizeof(*((CostModel*)NULL)->literal_) * VP8LHistogramNumCodes(cache_bits);
583   const size_t cost_model_size = sizeof(CostModel) + literal_array_size;
584   CostModel* const cost_model =
585       (CostModel*)WebPSafeCalloc(1ULL, cost_model_size);
586   VP8LColorCache hashers;
587   CostManager* cost_manager =
588       (CostManager*)WebPSafeCalloc(1ULL, sizeof(*cost_manager));
589   int offset_prev = -1, len_prev = -1;
590   int64_t offset_cost = -1;
591   int first_offset_is_constant = -1;  // initialized with 'impossible' value
592   int reach = 0;
593 
594   if (cost_model == NULL || cost_manager == NULL) goto Error;
595 
596   cost_model->literal_ = (uint32_t*)(cost_model + 1);
597   if (use_color_cache) {
598     cc_init = VP8LColorCacheInit(&hashers, cache_bits);
599     if (!cc_init) goto Error;
600   }
601 
602   if (!CostModelBuild(cost_model, xsize, cache_bits, refs)) {
603     goto Error;
604   }
605 
606   if (!CostManagerInit(cost_manager, dist_array, pix_count, cost_model)) {
607     goto Error;
608   }
609 
610   // We loop one pixel at a time, but store all currently best points to
611   // non-processed locations from this point.
612   dist_array[0] = 0;
613   // Add first pixel as literal.
614   AddSingleLiteralWithCostModel(argb, &hashers, cost_model, /*idx=*/0,
615                                 use_color_cache, /*prev_cost=*/0,
616                                 cost_manager->costs_, dist_array);
617 
618   for (i = 1; i < pix_count; ++i) {
619     const int64_t prev_cost = cost_manager->costs_[i - 1];
620     int offset, len;
621     VP8LHashChainFindCopy(hash_chain, i, &offset, &len);
622 
623     // Try adding the pixel as a literal.
624     AddSingleLiteralWithCostModel(argb, &hashers, cost_model, i,
625                                   use_color_cache, prev_cost,
626                                   cost_manager->costs_, dist_array);
627 
628     // If we are dealing with a non-literal.
629     if (len >= 2) {
630       if (offset != offset_prev) {
631         const int code = VP8LDistanceToPlaneCode(xsize, offset);
632         offset_cost = GetDistanceCost(cost_model, code);
633         first_offset_is_constant = 1;
634         PushInterval(cost_manager, prev_cost + offset_cost, i, len);
635       } else {
636         assert(offset_cost >= 0);
637         assert(len_prev >= 0);
638         assert(first_offset_is_constant == 0 || first_offset_is_constant == 1);
639         // Instead of considering all contributions from a pixel i by calling:
640         //         PushInterval(cost_manager, prev_cost + offset_cost, i, len);
641         // we optimize these contributions in case offset_cost stays the same
642         // for consecutive pixels. This describes a set of pixels similar to a
643         // previous set (e.g. constant color regions).
644         if (first_offset_is_constant) {
645           reach = i - 1 + len_prev - 1;
646           first_offset_is_constant = 0;
647         }
648 
649         if (i + len - 1 > reach) {
650           // We can only be go further with the same offset if the previous
651           // length was maxed, hence len_prev == len == MAX_LENGTH.
652           // TODO(vrabaud), bump i to the end right away (insert cache and
653           // update cost).
654           // TODO(vrabaud), check if one of the points in between does not have
655           // a lower cost.
656           // Already consider the pixel at "reach" to add intervals that are
657           // better than whatever we add.
658           int offset_j, len_j = 0;
659           int j;
660           assert(len == MAX_LENGTH || len == pix_count - i);
661           // Figure out the last consecutive pixel within [i, reach + 1] with
662           // the same offset.
663           for (j = i; j <= reach; ++j) {
664             VP8LHashChainFindCopy(hash_chain, j + 1, &offset_j, &len_j);
665             if (offset_j != offset) {
666               VP8LHashChainFindCopy(hash_chain, j, &offset_j, &len_j);
667               break;
668             }
669           }
670           // Update the cost at j - 1 and j.
671           UpdateCostAtIndex(cost_manager, j - 1, 0);
672           UpdateCostAtIndex(cost_manager, j, 0);
673 
674           PushInterval(cost_manager, cost_manager->costs_[j - 1] + offset_cost,
675                        j, len_j);
676           reach = j + len_j - 1;
677         }
678       }
679     }
680 
681     UpdateCostAtIndex(cost_manager, i, 1);
682     offset_prev = offset;
683     len_prev = len;
684   }
685 
686   ok = !refs->error_;
687  Error:
688   if (cc_init) VP8LColorCacheClear(&hashers);
689   CostManagerClear(cost_manager);
690   WebPSafeFree(cost_model);
691   WebPSafeFree(cost_manager);
692   return ok;
693 }
694 
695 // We pack the path at the end of *dist_array and return
696 // a pointer to this part of the array. Example:
697 // dist_array = [1x2xx3x2] => packed [1x2x1232], chosen_path = [1232]
TraceBackwards(uint16_t * const dist_array,int dist_array_size,uint16_t ** const chosen_path,int * const chosen_path_size)698 static void TraceBackwards(uint16_t* const dist_array,
699                            int dist_array_size,
700                            uint16_t** const chosen_path,
701                            int* const chosen_path_size) {
702   uint16_t* path = dist_array + dist_array_size;
703   uint16_t* cur = dist_array + dist_array_size - 1;
704   while (cur >= dist_array) {
705     const int k = *cur;
706     --path;
707     *path = k;
708     cur -= k;
709   }
710   *chosen_path = path;
711   *chosen_path_size = (int)(dist_array + dist_array_size - path);
712 }
713 
BackwardReferencesHashChainFollowChosenPath(const uint32_t * const argb,int cache_bits,const uint16_t * const chosen_path,int chosen_path_size,const VP8LHashChain * const hash_chain,VP8LBackwardRefs * const refs)714 static int BackwardReferencesHashChainFollowChosenPath(
715     const uint32_t* const argb, int cache_bits,
716     const uint16_t* const chosen_path, int chosen_path_size,
717     const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs) {
718   const int use_color_cache = (cache_bits > 0);
719   int ix;
720   int i = 0;
721   int ok = 0;
722   int cc_init = 0;
723   VP8LColorCache hashers;
724 
725   if (use_color_cache) {
726     cc_init = VP8LColorCacheInit(&hashers, cache_bits);
727     if (!cc_init) goto Error;
728   }
729 
730   VP8LClearBackwardRefs(refs);
731   for (ix = 0; ix < chosen_path_size; ++ix) {
732     const int len = chosen_path[ix];
733     if (len != 1) {
734       int k;
735       const int offset = VP8LHashChainFindOffset(hash_chain, i);
736       VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));
737       if (use_color_cache) {
738         for (k = 0; k < len; ++k) {
739           VP8LColorCacheInsert(&hashers, argb[i + k]);
740         }
741       }
742       i += len;
743     } else {
744       PixOrCopy v;
745       const int idx =
746           use_color_cache ? VP8LColorCacheContains(&hashers, argb[i]) : -1;
747       if (idx >= 0) {
748         // use_color_cache is true and hashers contains argb[i]
749         // push pixel as a color cache index
750         v = PixOrCopyCreateCacheIdx(idx);
751       } else {
752         if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
753         v = PixOrCopyCreateLiteral(argb[i]);
754       }
755       VP8LBackwardRefsCursorAdd(refs, v);
756       ++i;
757     }
758   }
759   ok = !refs->error_;
760  Error:
761   if (cc_init) VP8LColorCacheClear(&hashers);
762   return ok;
763 }
764 
765 // Returns 1 on success.
766 extern int VP8LBackwardReferencesTraceBackwards(
767     int xsize, int ysize, const uint32_t* const argb, int cache_bits,
768     const VP8LHashChain* const hash_chain,
769     const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst);
VP8LBackwardReferencesTraceBackwards(int xsize,int ysize,const uint32_t * const argb,int cache_bits,const VP8LHashChain * const hash_chain,const VP8LBackwardRefs * const refs_src,VP8LBackwardRefs * const refs_dst)770 int VP8LBackwardReferencesTraceBackwards(int xsize, int ysize,
771                                          const uint32_t* const argb,
772                                          int cache_bits,
773                                          const VP8LHashChain* const hash_chain,
774                                          const VP8LBackwardRefs* const refs_src,
775                                          VP8LBackwardRefs* const refs_dst) {
776   int ok = 0;
777   const int dist_array_size = xsize * ysize;
778   uint16_t* chosen_path = NULL;
779   int chosen_path_size = 0;
780   uint16_t* dist_array =
781       (uint16_t*)WebPSafeMalloc(dist_array_size, sizeof(*dist_array));
782 
783   if (dist_array == NULL) goto Error;
784 
785   if (!BackwardReferencesHashChainDistanceOnly(
786           xsize, ysize, argb, cache_bits, hash_chain, refs_src, dist_array)) {
787     goto Error;
788   }
789   TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size);
790   if (!BackwardReferencesHashChainFollowChosenPath(
791           argb, cache_bits, chosen_path, chosen_path_size, hash_chain,
792           refs_dst)) {
793     goto Error;
794   }
795   ok = 1;
796  Error:
797   WebPSafeFree(dist_array);
798   return ok;
799 }
800