Blame src/enc/backward_references_enc.c

Packit 9c6abc
// Copyright 2012 Google Inc. All Rights Reserved.
Packit 9c6abc
//
Packit 9c6abc
// Use of this source code is governed by a BSD-style license
Packit 9c6abc
// that can be found in the COPYING file in the root of the source
Packit 9c6abc
// tree. An additional intellectual property rights grant can be found
Packit 9c6abc
// in the file PATENTS. All contributing project authors may
Packit 9c6abc
// be found in the AUTHORS file in the root of the source tree.
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
//
Packit 9c6abc
// Author: Jyrki Alakuijala (jyrki@google.com)
Packit 9c6abc
//
Packit 9c6abc
Packit 9c6abc
#include <assert.h>
Packit 9c6abc
#include <math.h>
Packit 9c6abc
Packit 9c6abc
#include "src/enc/backward_references_enc.h"
Packit 9c6abc
#include "src/enc/histogram_enc.h"
Packit 9c6abc
#include "src/dsp/lossless.h"
Packit 9c6abc
#include "src/dsp/lossless_common.h"
Packit 9c6abc
#include "src/dsp/dsp.h"
Packit 9c6abc
#include "src/utils/color_cache_utils.h"
Packit 9c6abc
#include "src/utils/utils.h"
Packit 9c6abc
Packit 9c6abc
#define MIN_BLOCK_SIZE 256  // minimum block size for backward references
Packit 9c6abc
Packit 9c6abc
#define MAX_ENTROPY    (1e30f)
Packit 9c6abc
Packit 9c6abc
// 1M window (4M bytes) minus 120 special codes for short distances.
Packit 9c6abc
#define WINDOW_SIZE ((1 << WINDOW_SIZE_BITS) - 120)
Packit 9c6abc
Packit 9c6abc
// Minimum number of pixels for which it is cheaper to encode a
Packit 9c6abc
// distance + length instead of each pixel as a literal.
Packit 9c6abc
#define MIN_LENGTH 4
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
Packit 9c6abc
static const uint8_t plane_to_code_lut[128] = {
Packit 9c6abc
 96,   73,  55,  39,  23,  13,   5,  1,  255, 255, 255, 255, 255, 255, 255, 255,
Packit 9c6abc
 101,  78,  58,  42,  26,  16,   8,  2,    0,   3,  9,   17,  27,  43,  59,  79,
Packit 9c6abc
 102,  86,  62,  46,  32,  20,  10,  6,    4,   7,  11,  21,  33,  47,  63,  87,
Packit 9c6abc
 105,  90,  70,  52,  37,  28,  18,  14,  12,  15,  19,  29,  38,  53,  71,  91,
Packit 9c6abc
 110,  99,  82,  66,  48,  35,  30,  24,  22,  25,  31,  36,  49,  67,  83, 100,
Packit 9c6abc
 115, 108,  94,  76,  64,  50,  44,  40,  34,  41,  45,  51,  65,  77,  95, 109,
Packit 9c6abc
 118, 113, 103,  92,  80,  68,  60,  56,  54,  57,  61,  69,  81,  93, 104, 114,
Packit 9c6abc
 119, 116, 111, 106,  97,  88,  84,  74,  72,  75,  85,  89,  98, 107, 112, 117
Packit 9c6abc
};
Packit 9c6abc
Packit 9c6abc
extern int VP8LDistanceToPlaneCode(int xsize, int dist);
Packit 9c6abc
int VP8LDistanceToPlaneCode(int xsize, int dist) {
Packit 9c6abc
  const int yoffset = dist / xsize;
Packit 9c6abc
  const int xoffset = dist - yoffset * xsize;
Packit 9c6abc
  if (xoffset <= 8 && yoffset < 8) {
Packit 9c6abc
    return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1;
Packit 9c6abc
  } else if (xoffset > xsize - 8 && yoffset < 7) {
Packit 9c6abc
    return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1;
Packit 9c6abc
  }
Packit 9c6abc
  return dist + 120;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Returns the exact index where array1 and array2 are different. For an index
Packit 9c6abc
// inferior or equal to best_len_match, the return value just has to be strictly
Packit 9c6abc
// inferior to best_len_match. The current behavior is to return 0 if this index
Packit 9c6abc
// is best_len_match, and the index itself otherwise.
Packit 9c6abc
// If no two elements are the same, it returns max_limit.
Packit 9c6abc
static WEBP_INLINE int FindMatchLength(const uint32_t* const array1,
Packit 9c6abc
                                       const uint32_t* const array2,
Packit 9c6abc
                                       int best_len_match, int max_limit) {
Packit 9c6abc
  // Before 'expensive' linear match, check if the two arrays match at the
Packit 9c6abc
  // current best length index.
Packit 9c6abc
  if (array1[best_len_match] != array2[best_len_match]) return 0;
Packit 9c6abc
Packit 9c6abc
  return VP8LVectorMismatch(array1, array2, max_limit);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
//  VP8LBackwardRefs
Packit 9c6abc
Packit 9c6abc
struct PixOrCopyBlock {
Packit 9c6abc
  PixOrCopyBlock* next_;   // next block (or NULL)
Packit 9c6abc
  PixOrCopy* start_;       // data start
Packit 9c6abc
  int size_;               // currently used size
Packit 9c6abc
};
Packit 9c6abc
Packit 9c6abc
extern void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs);
Packit 9c6abc
void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs) {
Packit 9c6abc
  assert(refs != NULL);
Packit 9c6abc
  if (refs->tail_ != NULL) {
Packit 9c6abc
    *refs->tail_ = refs->free_blocks_;  // recycle all blocks at once
Packit 9c6abc
  }
Packit 9c6abc
  refs->free_blocks_ = refs->refs_;
Packit 9c6abc
  refs->tail_ = &refs->refs_;
Packit 9c6abc
  refs->last_block_ = NULL;
Packit 9c6abc
  refs->refs_ = NULL;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs) {
Packit 9c6abc
  assert(refs != NULL);
Packit 9c6abc
  VP8LClearBackwardRefs(refs);
Packit 9c6abc
  while (refs->free_blocks_ != NULL) {
Packit 9c6abc
    PixOrCopyBlock* const next = refs->free_blocks_->next_;
Packit 9c6abc
    WebPSafeFree(refs->free_blocks_);
Packit 9c6abc
    refs->free_blocks_ = next;
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size) {
Packit 9c6abc
  assert(refs != NULL);
Packit 9c6abc
  memset(refs, 0, sizeof(*refs));
Packit 9c6abc
  refs->tail_ = &refs->refs_;
Packit 9c6abc
  refs->block_size_ =
Packit 9c6abc
      (block_size < MIN_BLOCK_SIZE) ? MIN_BLOCK_SIZE : block_size;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
VP8LRefsCursor VP8LRefsCursorInit(const VP8LBackwardRefs* const refs) {
Packit 9c6abc
  VP8LRefsCursor c;
Packit 9c6abc
  c.cur_block_ = refs->refs_;
Packit 9c6abc
  if (refs->refs_ != NULL) {
Packit 9c6abc
    c.cur_pos = c.cur_block_->start_;
Packit 9c6abc
    c.last_pos_ = c.cur_pos + c.cur_block_->size_;
Packit 9c6abc
  } else {
Packit 9c6abc
    c.cur_pos = NULL;
Packit 9c6abc
    c.last_pos_ = NULL;
Packit 9c6abc
  }
Packit 9c6abc
  return c;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
void VP8LRefsCursorNextBlock(VP8LRefsCursor* const c) {
Packit 9c6abc
  PixOrCopyBlock* const b = c->cur_block_->next_;
Packit 9c6abc
  c->cur_pos = (b == NULL) ? NULL : b->start_;
Packit 9c6abc
  c->last_pos_ = (b == NULL) ? NULL : b->start_ + b->size_;
Packit 9c6abc
  c->cur_block_ = b;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Create a new block, either from the free list or allocated
Packit 9c6abc
static PixOrCopyBlock* BackwardRefsNewBlock(VP8LBackwardRefs* const refs) {
Packit 9c6abc
  PixOrCopyBlock* b = refs->free_blocks_;
Packit 9c6abc
  if (b == NULL) {   // allocate new memory chunk
Packit 9c6abc
    const size_t total_size =
Packit 9c6abc
        sizeof(*b) + refs->block_size_ * sizeof(*b->start_);
Packit 9c6abc
    b = (PixOrCopyBlock*)WebPSafeMalloc(1ULL, total_size);
Packit 9c6abc
    if (b == NULL) {
Packit 9c6abc
      refs->error_ |= 1;
Packit 9c6abc
      return NULL;
Packit 9c6abc
    }
Packit 9c6abc
    b->start_ = (PixOrCopy*)((uint8_t*)b + sizeof(*b));  // not always aligned
Packit 9c6abc
  } else {  // recycle from free-list
Packit 9c6abc
    refs->free_blocks_ = b->next_;
Packit 9c6abc
  }
Packit 9c6abc
  *refs->tail_ = b;
Packit 9c6abc
  refs->tail_ = &b->next_;
Packit 9c6abc
  refs->last_block_ = b;
Packit 9c6abc
  b->next_ = NULL;
Packit 9c6abc
  b->size_ = 0;
Packit 9c6abc
  return b;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,
Packit 9c6abc
                                      const PixOrCopy v);
Packit 9c6abc
void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,
Packit 9c6abc
                               const PixOrCopy v) {
Packit 9c6abc
  PixOrCopyBlock* b = refs->last_block_;
Packit 9c6abc
  if (b == NULL || b->size_ == refs->block_size_) {
Packit 9c6abc
    b = BackwardRefsNewBlock(refs);
Packit 9c6abc
    if (b == NULL) return;   // refs->error_ is set
Packit 9c6abc
  }
Packit 9c6abc
  b->start_[b->size_++] = v;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
// Hash chains
Packit 9c6abc
Packit 9c6abc
int VP8LHashChainInit(VP8LHashChain* const p, int size) {
Packit 9c6abc
  assert(p->size_ == 0);
Packit 9c6abc
  assert(p->offset_length_ == NULL);
Packit 9c6abc
  assert(size > 0);
Packit 9c6abc
  p->offset_length_ =
Packit 9c6abc
      (uint32_t*)WebPSafeMalloc(size, sizeof(*p->offset_length_));
Packit 9c6abc
  if (p->offset_length_ == NULL) return 0;
Packit 9c6abc
  p->size_ = size;
Packit 9c6abc
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
void VP8LHashChainClear(VP8LHashChain* const p) {
Packit 9c6abc
  assert(p != NULL);
Packit 9c6abc
  WebPSafeFree(p->offset_length_);
Packit 9c6abc
Packit 9c6abc
  p->size_ = 0;
Packit 9c6abc
  p->offset_length_ = NULL;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
Packit 9c6abc
#define HASH_MULTIPLIER_HI (0xc6a4a793ULL)
Packit 9c6abc
#define HASH_MULTIPLIER_LO (0x5bd1e996ULL)
Packit 9c6abc
Packit 9c6abc
static WEBP_INLINE uint32_t GetPixPairHash64(const uint32_t* const argb) {
Packit 9c6abc
  uint32_t key;
Packit 9c6abc
  key  = (argb[1] * HASH_MULTIPLIER_HI) & 0xffffffffu;
Packit 9c6abc
  key += (argb[0] * HASH_MULTIPLIER_LO) & 0xffffffffu;
Packit 9c6abc
  key = key >> (32 - HASH_BITS);
Packit 9c6abc
  return key;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Returns the maximum number of hash chain lookups to do for a
Packit 9c6abc
// given compression quality. Return value in range [8, 86].
Packit 9c6abc
static int GetMaxItersForQuality(int quality) {
Packit 9c6abc
  return 8 + (quality * quality) / 128;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int GetWindowSizeForHashChain(int quality, int xsize) {
Packit 9c6abc
  const int max_window_size = (quality > 75) ? WINDOW_SIZE
Packit 9c6abc
                            : (quality > 50) ? (xsize << 8)
Packit 9c6abc
                            : (quality > 25) ? (xsize << 6)
Packit 9c6abc
                            : (xsize << 4);
Packit 9c6abc
  assert(xsize > 0);
Packit 9c6abc
  return (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE : max_window_size;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WEBP_INLINE int MaxFindCopyLength(int len) {
Packit 9c6abc
  return (len < MAX_LENGTH) ? len : MAX_LENGTH;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
int VP8LHashChainFill(VP8LHashChain* const p, int quality,
Packit 9c6abc
                      const uint32_t* const argb, int xsize, int ysize,
Packit 9c6abc
                      int low_effort) {
Packit 9c6abc
  const int size = xsize * ysize;
Packit 9c6abc
  const int iter_max = GetMaxItersForQuality(quality);
Packit 9c6abc
  const uint32_t window_size = GetWindowSizeForHashChain(quality, xsize);
Packit 9c6abc
  int pos;
Packit 9c6abc
  int argb_comp;
Packit 9c6abc
  uint32_t base_position;
Packit 9c6abc
  int32_t* hash_to_first_index;
Packit 9c6abc
  // Temporarily use the p->offset_length_ as a hash chain.
Packit 9c6abc
  int32_t* chain = (int32_t*)p->offset_length_;
Packit 9c6abc
  assert(size > 0);
Packit 9c6abc
  assert(p->size_ != 0);
Packit 9c6abc
  assert(p->offset_length_ != NULL);
Packit 9c6abc
Packit 9c6abc
  if (size <= 2) {
Packit 9c6abc
    p->offset_length_[0] = p->offset_length_[size - 1] = 0;
Packit 9c6abc
    return 1;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  hash_to_first_index =
Packit 9c6abc
      (int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index));
Packit 9c6abc
  if (hash_to_first_index == NULL) return 0;
Packit 9c6abc
Packit 9c6abc
  // Set the int32_t array to -1.
Packit 9c6abc
  memset(hash_to_first_index, 0xff, HASH_SIZE * sizeof(*hash_to_first_index));
Packit 9c6abc
  // Fill the chain linking pixels with the same hash.
Packit 9c6abc
  argb_comp = (argb[0] == argb[1]);
Packit 9c6abc
  for (pos = 0; pos < size - 2;) {
Packit 9c6abc
    uint32_t hash_code;
Packit 9c6abc
    const int argb_comp_next = (argb[pos + 1] == argb[pos + 2]);
Packit 9c6abc
    if (argb_comp && argb_comp_next) {
Packit 9c6abc
      // Consecutive pixels with the same color will share the same hash.
Packit 9c6abc
      // We therefore use a different hash: the color and its repetition
Packit 9c6abc
      // length.
Packit 9c6abc
      uint32_t tmp[2];
Packit 9c6abc
      uint32_t len = 1;
Packit 9c6abc
      tmp[0] = argb[pos];
Packit 9c6abc
      // Figure out how far the pixels are the same.
Packit 9c6abc
      // The last pixel has a different 64 bit hash, as its next pixel does
Packit 9c6abc
      // not have the same color, so we just need to get to the last pixel equal
Packit 9c6abc
      // to its follower.
Packit 9c6abc
      while (pos + (int)len + 2 < size && argb[pos + len + 2] == argb[pos]) {
Packit 9c6abc
        ++len;
Packit 9c6abc
      }
Packit 9c6abc
      if (len > MAX_LENGTH) {
Packit 9c6abc
        // Skip the pixels that match for distance=1 and length>MAX_LENGTH
Packit 9c6abc
        // because they are linked to their predecessor and we automatically
Packit 9c6abc
        // check that in the main for loop below. Skipping means setting no
Packit 9c6abc
        // predecessor in the chain, hence -1.
Packit 9c6abc
        memset(chain + pos, 0xff, (len - MAX_LENGTH) * sizeof(*chain));
Packit 9c6abc
        pos += len - MAX_LENGTH;
Packit 9c6abc
        len = MAX_LENGTH;
Packit 9c6abc
      }
Packit 9c6abc
      // Process the rest of the hash chain.
Packit 9c6abc
      while (len) {
Packit 9c6abc
        tmp[1] = len--;
Packit 9c6abc
        hash_code = GetPixPairHash64(tmp);
Packit 9c6abc
        chain[pos] = hash_to_first_index[hash_code];
Packit 9c6abc
        hash_to_first_index[hash_code] = pos++;
Packit 9c6abc
      }
Packit 9c6abc
      argb_comp = 0;
Packit 9c6abc
    } else {
Packit 9c6abc
      // Just move one pixel forward.
Packit 9c6abc
      hash_code = GetPixPairHash64(argb + pos);
Packit 9c6abc
      chain[pos] = hash_to_first_index[hash_code];
Packit 9c6abc
      hash_to_first_index[hash_code] = pos++;
Packit 9c6abc
      argb_comp = argb_comp_next;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  // Process the penultimate pixel.
Packit 9c6abc
  chain[pos] = hash_to_first_index[GetPixPairHash64(argb + pos)];
Packit 9c6abc
Packit 9c6abc
  WebPSafeFree(hash_to_first_index);
Packit 9c6abc
Packit 9c6abc
  // Find the best match interval at each pixel, defined by an offset to the
Packit 9c6abc
  // pixel and a length. The right-most pixel cannot match anything to the right
Packit 9c6abc
  // (hence a best length of 0) and the left-most pixel nothing to the left
Packit 9c6abc
  // (hence an offset of 0).
Packit 9c6abc
  assert(size > 2);
Packit 9c6abc
  p->offset_length_[0] = p->offset_length_[size - 1] = 0;
Packit 9c6abc
  for (base_position = size - 2; base_position > 0;) {
Packit 9c6abc
    const int max_len = MaxFindCopyLength(size - 1 - base_position);
Packit 9c6abc
    const uint32_t* const argb_start = argb + base_position;
Packit 9c6abc
    int iter = iter_max;
Packit 9c6abc
    int best_length = 0;
Packit 9c6abc
    uint32_t best_distance = 0;
Packit 9c6abc
    uint32_t best_argb;
Packit 9c6abc
    const int min_pos =
Packit 9c6abc
        (base_position > window_size) ? base_position - window_size : 0;
Packit 9c6abc
    const int length_max = (max_len < 256) ? max_len : 256;
Packit 9c6abc
    uint32_t max_base_position;
Packit 9c6abc
Packit 9c6abc
    pos = chain[base_position];
Packit 9c6abc
    if (!low_effort) {
Packit 9c6abc
      int curr_length;
Packit 9c6abc
      // Heuristic: use the comparison with the above line as an initialization.
Packit 9c6abc
      if (base_position >= (uint32_t)xsize) {
Packit 9c6abc
        curr_length = FindMatchLength(argb_start - xsize, argb_start,
Packit 9c6abc
                                      best_length, max_len);
Packit 9c6abc
        if (curr_length > best_length) {
Packit 9c6abc
          best_length = curr_length;
Packit 9c6abc
          best_distance = xsize;
Packit 9c6abc
        }
Packit 9c6abc
        --iter;
Packit 9c6abc
      }
Packit 9c6abc
      // Heuristic: compare to the previous pixel.
Packit 9c6abc
      curr_length =
Packit 9c6abc
          FindMatchLength(argb_start - 1, argb_start, best_length, max_len);
Packit 9c6abc
      if (curr_length > best_length) {
Packit 9c6abc
        best_length = curr_length;
Packit 9c6abc
        best_distance = 1;
Packit 9c6abc
      }
Packit 9c6abc
      --iter;
Packit 9c6abc
      // Skip the for loop if we already have the maximum.
Packit 9c6abc
      if (best_length == MAX_LENGTH) pos = min_pos - 1;
Packit 9c6abc
    }
Packit 9c6abc
    best_argb = argb_start[best_length];
Packit 9c6abc
Packit 9c6abc
    for (; pos >= min_pos && --iter; pos = chain[pos]) {
Packit 9c6abc
      int curr_length;
Packit 9c6abc
      assert(base_position > (uint32_t)pos);
Packit 9c6abc
Packit 9c6abc
      if (argb[pos + best_length] != best_argb) continue;
Packit 9c6abc
Packit 9c6abc
      curr_length = VP8LVectorMismatch(argb + pos, argb_start, max_len);
Packit 9c6abc
      if (best_length < curr_length) {
Packit 9c6abc
        best_length = curr_length;
Packit 9c6abc
        best_distance = base_position - pos;
Packit 9c6abc
        best_argb = argb_start[best_length];
Packit 9c6abc
        // Stop if we have reached a good enough length.
Packit 9c6abc
        if (best_length >= length_max) break;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    // We have the best match but in case the two intervals continue matching
Packit 9c6abc
    // to the left, we have the best matches for the left-extended pixels.
Packit 9c6abc
    max_base_position = base_position;
Packit 9c6abc
    while (1) {
Packit 9c6abc
      assert(best_length <= MAX_LENGTH);
Packit 9c6abc
      assert(best_distance <= WINDOW_SIZE);
Packit 9c6abc
      p->offset_length_[base_position] =
Packit 9c6abc
          (best_distance << MAX_LENGTH_BITS) | (uint32_t)best_length;
Packit 9c6abc
      --base_position;
Packit 9c6abc
      // Stop if we don't have a match or if we are out of bounds.
Packit 9c6abc
      if (best_distance == 0 || base_position == 0) break;
Packit 9c6abc
      // Stop if we cannot extend the matching intervals to the left.
Packit 9c6abc
      if (base_position < best_distance ||
Packit 9c6abc
          argb[base_position - best_distance] != argb[base_position]) {
Packit 9c6abc
        break;
Packit 9c6abc
      }
Packit 9c6abc
      // Stop if we are matching at its limit because there could be a closer
Packit 9c6abc
      // matching interval with the same maximum length. Then again, if the
Packit 9c6abc
      // matching interval is as close as possible (best_distance == 1), we will
Packit 9c6abc
      // never find anything better so let's continue.
Packit 9c6abc
      if (best_length == MAX_LENGTH && best_distance != 1 &&
Packit 9c6abc
          base_position + MAX_LENGTH < max_base_position) {
Packit 9c6abc
        break;
Packit 9c6abc
      }
Packit 9c6abc
      if (best_length < MAX_LENGTH) {
Packit 9c6abc
        ++best_length;
Packit 9c6abc
        max_base_position = base_position;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WEBP_INLINE void AddSingleLiteral(uint32_t pixel, int use_color_cache,
Packit 9c6abc
                                         VP8LColorCache* const hashers,
Packit 9c6abc
                                         VP8LBackwardRefs* const refs) {
Packit 9c6abc
  PixOrCopy v;
Packit 9c6abc
  if (use_color_cache) {
Packit 9c6abc
    const uint32_t key = VP8LColorCacheGetIndex(hashers, pixel);
Packit 9c6abc
    if (VP8LColorCacheLookup(hashers, key) == pixel) {
Packit 9c6abc
      v = PixOrCopyCreateCacheIdx(key);
Packit 9c6abc
    } else {
Packit 9c6abc
      v = PixOrCopyCreateLiteral(pixel);
Packit 9c6abc
      VP8LColorCacheSet(hashers, key, pixel);
Packit 9c6abc
    }
Packit 9c6abc
  } else {
Packit 9c6abc
    v = PixOrCopyCreateLiteral(pixel);
Packit 9c6abc
  }
Packit 9c6abc
  VP8LBackwardRefsCursorAdd(refs, v);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int BackwardReferencesRle(int xsize, int ysize,
Packit 9c6abc
                                 const uint32_t* const argb,
Packit 9c6abc
                                 int cache_bits, VP8LBackwardRefs* const refs) {
Packit 9c6abc
  const int pix_count = xsize * ysize;
Packit 9c6abc
  int i, k;
Packit 9c6abc
  const int use_color_cache = (cache_bits > 0);
Packit 9c6abc
  VP8LColorCache hashers;
Packit 9c6abc
Packit 9c6abc
  if (use_color_cache && !VP8LColorCacheInit(&hashers, cache_bits)) {
Packit 9c6abc
    return 0;
Packit 9c6abc
  }
Packit 9c6abc
  VP8LClearBackwardRefs(refs);
Packit 9c6abc
  // Add first pixel as literal.
Packit 9c6abc
  AddSingleLiteral(argb[0], use_color_cache, &hashers, refs);
Packit 9c6abc
  i = 1;
Packit 9c6abc
  while (i < pix_count) {
Packit 9c6abc
    const int max_len = MaxFindCopyLength(pix_count - i);
Packit 9c6abc
    const int rle_len = FindMatchLength(argb + i, argb + i - 1, 0, max_len);
Packit 9c6abc
    const int prev_row_len = (i < xsize) ? 0 :
Packit 9c6abc
        FindMatchLength(argb + i, argb + i - xsize, 0, max_len);
Packit 9c6abc
    if (rle_len >= prev_row_len && rle_len >= MIN_LENGTH) {
Packit 9c6abc
      VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(1, rle_len));
Packit 9c6abc
      // We don't need to update the color cache here since it is always the
Packit 9c6abc
      // same pixel being copied, and that does not change the color cache
Packit 9c6abc
      // state.
Packit 9c6abc
      i += rle_len;
Packit 9c6abc
    } else if (prev_row_len >= MIN_LENGTH) {
Packit 9c6abc
      VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(xsize, prev_row_len));
Packit 9c6abc
      if (use_color_cache) {
Packit 9c6abc
        for (k = 0; k < prev_row_len; ++k) {
Packit 9c6abc
          VP8LColorCacheInsert(&hashers, argb[i + k]);
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
      i += prev_row_len;
Packit 9c6abc
    } else {
Packit 9c6abc
      AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
Packit 9c6abc
      i++;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  if (use_color_cache) VP8LColorCacheClear(&hashers);
Packit 9c6abc
  return !refs->error_;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int BackwardReferencesLz77(int xsize, int ysize,
Packit 9c6abc
                                  const uint32_t* const argb, int cache_bits,
Packit 9c6abc
                                  const VP8LHashChain* const hash_chain,
Packit 9c6abc
                                  VP8LBackwardRefs* const refs) {
Packit 9c6abc
  int i;
Packit 9c6abc
  int i_last_check = -1;
Packit 9c6abc
  int ok = 0;
Packit 9c6abc
  int cc_init = 0;
Packit 9c6abc
  const int use_color_cache = (cache_bits > 0);
Packit 9c6abc
  const int pix_count = xsize * ysize;
Packit 9c6abc
  VP8LColorCache hashers;
Packit 9c6abc
Packit 9c6abc
  if (use_color_cache) {
Packit 9c6abc
    cc_init = VP8LColorCacheInit(&hashers, cache_bits);
Packit 9c6abc
    if (!cc_init) goto Error;
Packit 9c6abc
  }
Packit 9c6abc
  VP8LClearBackwardRefs(refs);
Packit 9c6abc
  for (i = 0; i < pix_count;) {
Packit 9c6abc
    // Alternative#1: Code the pixels starting at 'i' using backward reference.
Packit 9c6abc
    int offset = 0;
Packit 9c6abc
    int len = 0;
Packit 9c6abc
    int j;
Packit 9c6abc
    VP8LHashChainFindCopy(hash_chain, i, &offset, &len;;
Packit 9c6abc
    if (len >= MIN_LENGTH) {
Packit 9c6abc
      const int len_ini = len;
Packit 9c6abc
      int max_reach = 0;
Packit 9c6abc
      const int j_max =
Packit 9c6abc
          (i + len_ini >= pix_count) ? pix_count - 1 : i + len_ini;
Packit 9c6abc
      // Only start from what we have not checked already.
Packit 9c6abc
      i_last_check = (i > i_last_check) ? i : i_last_check;
Packit 9c6abc
      // We know the best match for the current pixel but we try to find the
Packit 9c6abc
      // best matches for the current pixel AND the next one combined.
Packit 9c6abc
      // The naive method would use the intervals:
Packit 9c6abc
      // [i,i+len) + [i+len, length of best match at i+len)
Packit 9c6abc
      // while we check if we can use:
Packit 9c6abc
      // [i,j) (where j<=i+len) + [j, length of best match at j)
Packit 9c6abc
      for (j = i_last_check + 1; j <= j_max; ++j) {
Packit 9c6abc
        const int len_j = VP8LHashChainFindLength(hash_chain, j);
Packit 9c6abc
        const int reach =
Packit 9c6abc
            j + (len_j >= MIN_LENGTH ? len_j : 1);  // 1 for single literal.
Packit 9c6abc
        if (reach > max_reach) {
Packit 9c6abc
          len = j - i;
Packit 9c6abc
          max_reach = reach;
Packit 9c6abc
          if (max_reach >= pix_count) break;
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
    } else {
Packit 9c6abc
      len = 1;
Packit 9c6abc
    }
Packit 9c6abc
    // Go with literal or backward reference.
Packit 9c6abc
    assert(len > 0);
Packit 9c6abc
    if (len == 1) {
Packit 9c6abc
      AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
Packit 9c6abc
    } else {
Packit 9c6abc
      VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));
Packit 9c6abc
      if (use_color_cache) {
Packit 9c6abc
        for (j = i; j < i + len; ++j) VP8LColorCacheInsert(&hashers, argb[j]);
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    i += len;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  ok = !refs->error_;
Packit 9c6abc
 Error:
Packit 9c6abc
  if (cc_init) VP8LColorCacheClear(&hashers);
Packit 9c6abc
  return ok;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Compute an LZ77 by forcing matches to happen within a given distance cost.
Packit 9c6abc
// We therefore limit the algorithm to the lowest 32 values in the PlaneCode
Packit 9c6abc
// definition.
Packit 9c6abc
#define WINDOW_OFFSETS_SIZE_MAX 32
Packit 9c6abc
static int BackwardReferencesLz77Box(int xsize, int ysize,
Packit 9c6abc
                                     const uint32_t* const argb, int cache_bits,
Packit 9c6abc
                                     const VP8LHashChain* const hash_chain_best,
Packit 9c6abc
                                     VP8LHashChain* hash_chain,
Packit 9c6abc
                                     VP8LBackwardRefs* const refs) {
Packit 9c6abc
  int i;
Packit 9c6abc
  const int pix_count = xsize * ysize;
Packit 9c6abc
  uint16_t* counts;
Packit 9c6abc
  int window_offsets[WINDOW_OFFSETS_SIZE_MAX] = {0};
Packit 9c6abc
  int window_offsets_new[WINDOW_OFFSETS_SIZE_MAX] = {0};
Packit 9c6abc
  int window_offsets_size = 0;
Packit 9c6abc
  int window_offsets_new_size = 0;
Packit 9c6abc
  uint16_t* const counts_ini =
Packit 9c6abc
      (uint16_t*)WebPSafeMalloc(xsize * ysize, sizeof(*counts_ini));
Packit 9c6abc
  int best_offset_prev = -1, best_length_prev = -1;
Packit 9c6abc
  if (counts_ini == NULL) return 0;
Packit 9c6abc
Packit 9c6abc
  // counts[i] counts how many times a pixel is repeated starting at position i.
Packit 9c6abc
  i = pix_count - 2;
Packit 9c6abc
  counts = counts_ini + i;
Packit 9c6abc
  counts[1] = 1;
Packit 9c6abc
  for (; i >= 0; --i, --counts) {
Packit 9c6abc
    if (argb[i] == argb[i + 1]) {
Packit 9c6abc
      // Max out the counts to MAX_LENGTH.
Packit 9c6abc
      counts[0] = counts[1] + (counts[1] != MAX_LENGTH);
Packit 9c6abc
    } else {
Packit 9c6abc
      counts[0] = 1;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Figure out the window offsets around a pixel. They are stored in a
Packit 9c6abc
  // spiraling order around the pixel as defined by VP8LDistanceToPlaneCode.
Packit 9c6abc
  {
Packit 9c6abc
    int x, y;
Packit 9c6abc
    for (y = 0; y <= 6; ++y) {
Packit 9c6abc
      for (x = -6; x <= 6; ++x) {
Packit 9c6abc
        const int offset = y * xsize + x;
Packit 9c6abc
        int plane_code;
Packit 9c6abc
        // Ignore offsets that bring us after the pixel.
Packit 9c6abc
        if (offset <= 0) continue;
Packit 9c6abc
        plane_code = VP8LDistanceToPlaneCode(xsize, offset) - 1;
Packit 9c6abc
        if (plane_code >= WINDOW_OFFSETS_SIZE_MAX) continue;
Packit 9c6abc
        window_offsets[plane_code] = offset;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    // For narrow images, not all plane codes are reached, so remove those.
Packit 9c6abc
    for (i = 0; i < WINDOW_OFFSETS_SIZE_MAX; ++i) {
Packit 9c6abc
      if (window_offsets[i] == 0) continue;
Packit 9c6abc
      window_offsets[window_offsets_size++] = window_offsets[i];
Packit 9c6abc
    }
Packit 9c6abc
    // Given a pixel P, find the offsets that reach pixels unreachable from P-1
Packit 9c6abc
    // with any of the offsets in window_offsets[].
Packit 9c6abc
    for (i = 0; i < window_offsets_size; ++i) {
Packit 9c6abc
      int j;
Packit 9c6abc
      int is_reachable = 0;
Packit 9c6abc
      for (j = 0; j < window_offsets_size && !is_reachable; ++j) {
Packit 9c6abc
        is_reachable |= (window_offsets[i] == window_offsets[j] + 1);
Packit 9c6abc
      }
Packit 9c6abc
      if (!is_reachable) {
Packit 9c6abc
        window_offsets_new[window_offsets_new_size] = window_offsets[i];
Packit 9c6abc
        ++window_offsets_new_size;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  hash_chain->offset_length_[0] = 0;
Packit 9c6abc
  for (i = 1; i < pix_count; ++i) {
Packit 9c6abc
    int ind;
Packit 9c6abc
    int best_length = VP8LHashChainFindLength(hash_chain_best, i);
Packit 9c6abc
    int best_offset;
Packit 9c6abc
    int do_compute = 1;
Packit 9c6abc
Packit 9c6abc
    if (best_length >= MAX_LENGTH) {
Packit 9c6abc
      // Do not recompute the best match if we already have a maximal one in the
Packit 9c6abc
      // window.
Packit 9c6abc
      best_offset = VP8LHashChainFindOffset(hash_chain_best, i);
Packit 9c6abc
      for (ind = 0; ind < window_offsets_size; ++ind) {
Packit 9c6abc
        if (best_offset == window_offsets[ind]) {
Packit 9c6abc
          do_compute = 0;
Packit 9c6abc
          break;
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    if (do_compute) {
Packit 9c6abc
      // Figure out if we should use the offset/length from the previous pixel
Packit 9c6abc
      // as an initial guess and therefore only inspect the offsets in
Packit 9c6abc
      // window_offsets_new[].
Packit 9c6abc
      const int use_prev =
Packit 9c6abc
          (best_length_prev > 1) && (best_length_prev < MAX_LENGTH);
Packit 9c6abc
      const int num_ind =
Packit 9c6abc
          use_prev ? window_offsets_new_size : window_offsets_size;
Packit 9c6abc
      best_length = use_prev ? best_length_prev - 1 : 0;
Packit 9c6abc
      best_offset = use_prev ? best_offset_prev : 0;
Packit 9c6abc
      // Find the longest match in a window around the pixel.
Packit 9c6abc
      for (ind = 0; ind < num_ind; ++ind) {
Packit 9c6abc
        int curr_length = 0;
Packit 9c6abc
        int j = i;
Packit 9c6abc
        int j_offset =
Packit 9c6abc
            use_prev ? i - window_offsets_new[ind] : i - window_offsets[ind];
Packit 9c6abc
        if (j_offset < 0 || argb[j_offset] != argb[i]) continue;
Packit 9c6abc
        // The longest match is the sum of how many times each pixel is
Packit 9c6abc
        // repeated.
Packit 9c6abc
        do {
Packit 9c6abc
          const int counts_j_offset = counts_ini[j_offset];
Packit 9c6abc
          const int counts_j = counts_ini[j];
Packit 9c6abc
          if (counts_j_offset != counts_j) {
Packit 9c6abc
            curr_length +=
Packit 9c6abc
                (counts_j_offset < counts_j) ? counts_j_offset : counts_j;
Packit 9c6abc
            break;
Packit 9c6abc
          }
Packit 9c6abc
          // The same color is repeated counts_pos times at j_offset and j.
Packit 9c6abc
          curr_length += counts_j_offset;
Packit 9c6abc
          j_offset += counts_j_offset;
Packit 9c6abc
          j += counts_j_offset;
Packit 9c6abc
        } while (curr_length <= MAX_LENGTH && j < pix_count &&
Packit 9c6abc
                 argb[j_offset] == argb[j]);
Packit 9c6abc
        if (best_length < curr_length) {
Packit 9c6abc
          best_offset =
Packit 9c6abc
              use_prev ? window_offsets_new[ind] : window_offsets[ind];
Packit 9c6abc
          if (curr_length >= MAX_LENGTH) {
Packit 9c6abc
            best_length = MAX_LENGTH;
Packit 9c6abc
            break;
Packit 9c6abc
          } else {
Packit 9c6abc
            best_length = curr_length;
Packit 9c6abc
          }
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
Packit 9c6abc
    assert(i + best_length <= pix_count);
Packit 9c6abc
    assert(best_length <= MAX_LENGTH);
Packit 9c6abc
    if (best_length <= MIN_LENGTH) {
Packit 9c6abc
      hash_chain->offset_length_[i] = 0;
Packit 9c6abc
      best_offset_prev = 0;
Packit 9c6abc
      best_length_prev = 0;
Packit 9c6abc
    } else {
Packit 9c6abc
      hash_chain->offset_length_[i] =
Packit 9c6abc
          (best_offset << MAX_LENGTH_BITS) | (uint32_t)best_length;
Packit 9c6abc
      best_offset_prev = best_offset;
Packit 9c6abc
      best_length_prev = best_length;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  hash_chain->offset_length_[0] = 0;
Packit 9c6abc
  WebPSafeFree(counts_ini);
Packit 9c6abc
Packit 9c6abc
  return BackwardReferencesLz77(xsize, ysize, argb, cache_bits, hash_chain,
Packit 9c6abc
                                refs);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
Packit 9c6abc
static void BackwardReferences2DLocality(int xsize,
Packit 9c6abc
                                         const VP8LBackwardRefs* const refs) {
Packit 9c6abc
  VP8LRefsCursor c = VP8LRefsCursorInit(refs);
Packit 9c6abc
  while (VP8LRefsCursorOk(&c)) {
Packit 9c6abc
    if (PixOrCopyIsCopy(c.cur_pos)) {
Packit 9c6abc
      const int dist = c.cur_pos->argb_or_distance;
Packit 9c6abc
      const int transformed_dist = VP8LDistanceToPlaneCode(xsize, dist);
Packit 9c6abc
      c.cur_pos->argb_or_distance = transformed_dist;
Packit 9c6abc
    }
Packit 9c6abc
    VP8LRefsCursorNext(&c);
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Evaluate optimal cache bits for the local color cache.
Packit 9c6abc
// The input *best_cache_bits sets the maximum cache bits to use (passing 0
Packit 9c6abc
// implies disabling the local color cache). The local color cache is also
Packit 9c6abc
// disabled for the lower (<= 25) quality.
Packit 9c6abc
// Returns 0 in case of memory error.
Packit 9c6abc
static int CalculateBestCacheSize(const uint32_t* argb, int quality,
Packit 9c6abc
                                  const VP8LBackwardRefs* const refs,
Packit 9c6abc
                                  int* const best_cache_bits) {
Packit 9c6abc
  int i;
Packit 9c6abc
  const int cache_bits_max = (quality <= 25) ? 0 : *best_cache_bits;
Packit 9c6abc
  double entropy_min = MAX_ENTROPY;
Packit 9c6abc
  int cc_init[MAX_COLOR_CACHE_BITS + 1] = { 0 };
Packit 9c6abc
  VP8LColorCache hashers[MAX_COLOR_CACHE_BITS + 1];
Packit 9c6abc
  VP8LRefsCursor c = VP8LRefsCursorInit(refs);
Packit 9c6abc
  VP8LHistogram* histos[MAX_COLOR_CACHE_BITS + 1] = { NULL };
Packit 9c6abc
  int ok = 0;
Packit 9c6abc
Packit 9c6abc
  assert(cache_bits_max >= 0 && cache_bits_max <= MAX_COLOR_CACHE_BITS);
Packit 9c6abc
Packit 9c6abc
  if (cache_bits_max == 0) {
Packit 9c6abc
    *best_cache_bits = 0;
Packit 9c6abc
    // Local color cache is disabled.
Packit 9c6abc
    return 1;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Allocate data.
Packit 9c6abc
  for (i = 0; i <= cache_bits_max; ++i) {
Packit 9c6abc
    histos[i] = VP8LAllocateHistogram(i);
Packit 9c6abc
    if (histos[i] == NULL) goto Error;
Packit 9c6abc
    if (i == 0) continue;
Packit 9c6abc
    cc_init[i] = VP8LColorCacheInit(&hashers[i], i);
Packit 9c6abc
    if (!cc_init[i]) goto Error;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Find the cache_bits giving the lowest entropy. The search is done in a
Packit 9c6abc
  // brute-force way as the function (entropy w.r.t cache_bits) can be
Packit 9c6abc
  // anything in practice.
Packit 9c6abc
  while (VP8LRefsCursorOk(&c)) {
Packit 9c6abc
    const PixOrCopy* const v = c.cur_pos;
Packit 9c6abc
    if (PixOrCopyIsLiteral(v)) {
Packit 9c6abc
      const uint32_t pix = *argb++;
Packit 9c6abc
      const uint32_t a = (pix >> 24) & 0xff;
Packit 9c6abc
      const uint32_t r = (pix >> 16) & 0xff;
Packit 9c6abc
      const uint32_t g = (pix >>  8) & 0xff;
Packit 9c6abc
      const uint32_t b = (pix >>  0) & 0xff;
Packit 9c6abc
      // The keys of the caches can be derived from the longest one.
Packit 9c6abc
      int key = VP8LHashPix(pix, 32 - cache_bits_max);
Packit 9c6abc
      // Do not use the color cache for cache_bits = 0.
Packit 9c6abc
      ++histos[0]->blue_[b];
Packit 9c6abc
      ++histos[0]->literal_[g];
Packit 9c6abc
      ++histos[0]->red_[r];
Packit 9c6abc
      ++histos[0]->alpha_[a];
Packit 9c6abc
      // Deal with cache_bits > 0.
Packit 9c6abc
      for (i = cache_bits_max; i >= 1; --i, key >>= 1) {
Packit 9c6abc
        if (VP8LColorCacheLookup(&hashers[i], key) == pix) {
Packit 9c6abc
          ++histos[i]->literal_[NUM_LITERAL_CODES + NUM_LENGTH_CODES + key];
Packit 9c6abc
        } else {
Packit 9c6abc
          VP8LColorCacheSet(&hashers[i], key, pix);
Packit 9c6abc
          ++histos[i]->blue_[b];
Packit 9c6abc
          ++histos[i]->literal_[g];
Packit 9c6abc
          ++histos[i]->red_[r];
Packit 9c6abc
          ++histos[i]->alpha_[a];
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
    } else {
Packit 9c6abc
      // We should compute the contribution of the (distance,length)
Packit 9c6abc
      // histograms but those are the same independently from the cache size.
Packit 9c6abc
      // As those constant contributions are in the end added to the other
Packit 9c6abc
      // histogram contributions, we can safely ignore them.
Packit 9c6abc
      int len = PixOrCopyLength(v);
Packit 9c6abc
      uint32_t argb_prev = *argb ^ 0xffffffffu;
Packit 9c6abc
      // Update the color caches.
Packit 9c6abc
      do {
Packit 9c6abc
        if (*argb != argb_prev) {
Packit 9c6abc
          // Efficiency: insert only if the color changes.
Packit 9c6abc
          int key = VP8LHashPix(*argb, 32 - cache_bits_max);
Packit 9c6abc
          for (i = cache_bits_max; i >= 1; --i, key >>= 1) {
Packit 9c6abc
            hashers[i].colors_[key] = *argb;
Packit 9c6abc
          }
Packit 9c6abc
          argb_prev = *argb;
Packit 9c6abc
        }
Packit 9c6abc
        argb++;
Packit 9c6abc
      } while (--len != 0);
Packit 9c6abc
    }
Packit 9c6abc
    VP8LRefsCursorNext(&c);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  for (i = 0; i <= cache_bits_max; ++i) {
Packit 9c6abc
    const double entropy = VP8LHistogramEstimateBits(histos[i]);
Packit 9c6abc
    if (i == 0 || entropy < entropy_min) {
Packit 9c6abc
      entropy_min = entropy;
Packit 9c6abc
      *best_cache_bits = i;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  ok = 1;
Packit 9c6abc
Error:
Packit 9c6abc
  for (i = 0; i <= cache_bits_max; ++i) {
Packit 9c6abc
    if (cc_init[i]) VP8LColorCacheClear(&hashers[i]);
Packit 9c6abc
    VP8LFreeHistogram(histos[i]);
Packit 9c6abc
  }
Packit 9c6abc
  return ok;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Update (in-place) backward references for specified cache_bits.
Packit 9c6abc
static int BackwardRefsWithLocalCache(const uint32_t* const argb,
Packit 9c6abc
                                      int cache_bits,
Packit 9c6abc
                                      VP8LBackwardRefs* const refs) {
Packit 9c6abc
  int pixel_index = 0;
Packit 9c6abc
  VP8LColorCache hashers;
Packit 9c6abc
  VP8LRefsCursor c = VP8LRefsCursorInit(refs);
Packit 9c6abc
  if (!VP8LColorCacheInit(&hashers, cache_bits)) return 0;
Packit 9c6abc
Packit 9c6abc
  while (VP8LRefsCursorOk(&c)) {
Packit 9c6abc
    PixOrCopy* const v = c.cur_pos;
Packit 9c6abc
    if (PixOrCopyIsLiteral(v)) {
Packit 9c6abc
      const uint32_t argb_literal = v->argb_or_distance;
Packit 9c6abc
      const int ix = VP8LColorCacheContains(&hashers, argb_literal);
Packit 9c6abc
      if (ix >= 0) {
Packit 9c6abc
        // hashers contains argb_literal
Packit 9c6abc
        *v = PixOrCopyCreateCacheIdx(ix);
Packit 9c6abc
      } else {
Packit 9c6abc
        VP8LColorCacheInsert(&hashers, argb_literal);
Packit 9c6abc
      }
Packit 9c6abc
      ++pixel_index;
Packit 9c6abc
    } else {
Packit 9c6abc
      // refs was created without local cache, so it can not have cache indexes.
Packit 9c6abc
      int k;
Packit 9c6abc
      assert(PixOrCopyIsCopy(v));
Packit 9c6abc
      for (k = 0; k < v->len; ++k) {
Packit 9c6abc
        VP8LColorCacheInsert(&hashers, argb[pixel_index++]);
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    VP8LRefsCursorNext(&c);
Packit 9c6abc
  }
Packit 9c6abc
  VP8LColorCacheClear(&hashers);
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static VP8LBackwardRefs* GetBackwardReferencesLowEffort(
Packit 9c6abc
    int width, int height, const uint32_t* const argb,
Packit 9c6abc
    int* const cache_bits, const VP8LHashChain* const hash_chain,
Packit 9c6abc
    VP8LBackwardRefs* const refs_lz77) {
Packit 9c6abc
  *cache_bits = 0;
Packit 9c6abc
  if (!BackwardReferencesLz77(width, height, argb, 0, hash_chain, refs_lz77)) {
Packit 9c6abc
    return NULL;
Packit 9c6abc
  }
Packit 9c6abc
  BackwardReferences2DLocality(width, refs_lz77);
Packit 9c6abc
  return refs_lz77;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
extern int VP8LBackwardReferencesTraceBackwards(
Packit 9c6abc
    int xsize, int ysize, const uint32_t* const argb, int cache_bits,
Packit 9c6abc
    const VP8LHashChain* const hash_chain,
Packit 9c6abc
    const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst);
Packit 9c6abc
static VP8LBackwardRefs* GetBackwardReferences(
Packit 9c6abc
    int width, int height, const uint32_t* const argb, int quality,
Packit 9c6abc
    int lz77_types_to_try, int* const cache_bits,
Packit 9c6abc
    const VP8LHashChain* const hash_chain, VP8LBackwardRefs* best,
Packit 9c6abc
    VP8LBackwardRefs* worst) {
Packit 9c6abc
  const int cache_bits_initial = *cache_bits;
Packit 9c6abc
  double bit_cost_best = -1;
Packit 9c6abc
  VP8LHistogram* histo = NULL;
Packit 9c6abc
  int lz77_type, lz77_type_best = 0;
Packit 9c6abc
  VP8LHashChain hash_chain_box;
Packit 9c6abc
  memset(&hash_chain_box, 0, sizeof(hash_chain_box));
Packit 9c6abc
Packit 9c6abc
  histo = VP8LAllocateHistogram(MAX_COLOR_CACHE_BITS);
Packit 9c6abc
  if (histo == NULL) goto Error;
Packit 9c6abc
Packit 9c6abc
  for (lz77_type = 1; lz77_types_to_try;
Packit 9c6abc
       lz77_types_to_try &= ~lz77_type, lz77_type <<= 1) {
Packit 9c6abc
    int res = 0;
Packit 9c6abc
    double bit_cost;
Packit 9c6abc
    int cache_bits_tmp = cache_bits_initial;
Packit 9c6abc
    if ((lz77_types_to_try & lz77_type) == 0) continue;
Packit 9c6abc
    switch (lz77_type) {
Packit 9c6abc
      case kLZ77RLE:
Packit 9c6abc
        res = BackwardReferencesRle(width, height, argb, 0, worst);
Packit 9c6abc
        break;
Packit 9c6abc
      case kLZ77Standard:
Packit 9c6abc
        // Compute LZ77 with no cache (0 bits), as the ideal LZ77 with a color
Packit 9c6abc
        // cache is not that different in practice.
Packit 9c6abc
        res = BackwardReferencesLz77(width, height, argb, 0, hash_chain, worst);
Packit 9c6abc
        break;
Packit 9c6abc
      case kLZ77Box:
Packit 9c6abc
        if (!VP8LHashChainInit(&hash_chain_box, width * height)) goto Error;
Packit 9c6abc
        res = BackwardReferencesLz77Box(width, height, argb, 0, hash_chain,
Packit 9c6abc
                                        &hash_chain_box, worst);
Packit 9c6abc
        break;
Packit 9c6abc
      default:
Packit 9c6abc
        assert(0);
Packit 9c6abc
    }
Packit 9c6abc
    if (!res) goto Error;
Packit 9c6abc
Packit 9c6abc
    // Next, try with a color cache and update the references.
Packit 9c6abc
    if (!CalculateBestCacheSize(argb, quality, worst, &cache_bits_tmp)) {
Packit 9c6abc
      goto Error;
Packit 9c6abc
    }
Packit 9c6abc
    if (cache_bits_tmp > 0) {
Packit 9c6abc
      if (!BackwardRefsWithLocalCache(argb, cache_bits_tmp, worst)) {
Packit 9c6abc
        goto Error;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
Packit 9c6abc
    // Keep the best backward references.
Packit 9c6abc
    VP8LHistogramCreate(histo, worst, cache_bits_tmp);
Packit 9c6abc
    bit_cost = VP8LHistogramEstimateBits(histo);
Packit 9c6abc
    if (lz77_type_best == 0 || bit_cost < bit_cost_best) {
Packit 9c6abc
      VP8LBackwardRefs* const tmp = worst;
Packit 9c6abc
      worst = best;
Packit 9c6abc
      best = tmp;
Packit 9c6abc
      bit_cost_best = bit_cost;
Packit 9c6abc
      *cache_bits = cache_bits_tmp;
Packit 9c6abc
      lz77_type_best = lz77_type;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  assert(lz77_type_best > 0);
Packit 9c6abc
Packit 9c6abc
  // Improve on simple LZ77 but only for high quality (TraceBackwards is
Packit 9c6abc
  // costly).
Packit 9c6abc
  if ((lz77_type_best == kLZ77Standard || lz77_type_best == kLZ77Box) &&
Packit 9c6abc
      quality >= 25) {
Packit 9c6abc
    const VP8LHashChain* const hash_chain_tmp =
Packit 9c6abc
        (lz77_type_best == kLZ77Standard) ? hash_chain : &hash_chain_box;
Packit 9c6abc
    if (VP8LBackwardReferencesTraceBackwards(width, height, argb, *cache_bits,
Packit 9c6abc
                                             hash_chain_tmp, best, worst)) {
Packit 9c6abc
      double bit_cost_trace;
Packit 9c6abc
      VP8LHistogramCreate(histo, worst, *cache_bits);
Packit 9c6abc
      bit_cost_trace = VP8LHistogramEstimateBits(histo);
Packit 9c6abc
      if (bit_cost_trace < bit_cost_best) best = worst;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  BackwardReferences2DLocality(width, best);
Packit 9c6abc
Packit 9c6abc
Error:
Packit 9c6abc
  VP8LHashChainClear(&hash_chain_box);
Packit 9c6abc
  VP8LFreeHistogram(histo);
Packit 9c6abc
  return best;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
VP8LBackwardRefs* VP8LGetBackwardReferences(
Packit 9c6abc
    int width, int height, const uint32_t* const argb, int quality,
Packit 9c6abc
    int low_effort, int lz77_types_to_try, int* const cache_bits,
Packit 9c6abc
    const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1,
Packit 9c6abc
    VP8LBackwardRefs* const refs_tmp2) {
Packit 9c6abc
  if (low_effort) {
Packit 9c6abc
    return GetBackwardReferencesLowEffort(width, height, argb, cache_bits,
Packit 9c6abc
                                          hash_chain, refs_tmp1);
Packit 9c6abc
  } else {
Packit 9c6abc
    return GetBackwardReferences(width, height, argb, quality,
Packit 9c6abc
                                 lz77_types_to_try, cache_bits, hash_chain,
Packit 9c6abc
                                 refs_tmp1, refs_tmp2);
Packit 9c6abc
  }
Packit 9c6abc
}