Coverage Report

Created: 2026-02-14 07:11

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