Blame src/enc/vp8l_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
// main entry for the lossless encoder.
Packit 9c6abc
//
Packit 9c6abc
// Author: Vikas Arora (vikaas.arora@gmail.com)
Packit 9c6abc
//
Packit 9c6abc
Packit 9c6abc
#include <assert.h>
Packit 9c6abc
#include <stdlib.h>
Packit 9c6abc
Packit 9c6abc
#include "src/enc/backward_references_enc.h"
Packit 9c6abc
#include "src/enc/histogram_enc.h"
Packit 9c6abc
#include "src/enc/vp8i_enc.h"
Packit 9c6abc
#include "src/enc/vp8li_enc.h"
Packit 9c6abc
#include "src/dsp/lossless.h"
Packit 9c6abc
#include "src/dsp/lossless_common.h"
Packit 9c6abc
#include "src/utils/bit_writer_utils.h"
Packit 9c6abc
#include "src/utils/huffman_encode_utils.h"
Packit 9c6abc
#include "src/utils/utils.h"
Packit 9c6abc
#include "src/webp/format_constants.h"
Packit 9c6abc
Packit 9c6abc
// Maximum number of histogram images (sub-blocks).
Packit 9c6abc
#define MAX_HUFF_IMAGE_SIZE       2600
Packit 9c6abc
Packit 9c6abc
// Palette reordering for smaller sum of deltas (and for smaller storage).
Packit 9c6abc
Packit 9c6abc
static int PaletteCompareColorsForQsort(const void* p1, const void* p2) {
Packit 9c6abc
  const uint32_t a = WebPMemToUint32((uint8_t*)p1);
Packit 9c6abc
  const uint32_t b = WebPMemToUint32((uint8_t*)p2);
Packit 9c6abc
  assert(a != b);
Packit 9c6abc
  return (a < b) ? -1 : 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WEBP_INLINE uint32_t PaletteComponentDistance(uint32_t v) {
Packit 9c6abc
  return (v <= 128) ? v : (256 - v);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Computes a value that is related to the entropy created by the
Packit 9c6abc
// palette entry diff.
Packit 9c6abc
//
Packit 9c6abc
// Note that the last & 0xff is a no-operation in the next statement, but
Packit 9c6abc
// removed by most compilers and is here only for regularity of the code.
Packit 9c6abc
static WEBP_INLINE uint32_t PaletteColorDistance(uint32_t col1, uint32_t col2) {
Packit 9c6abc
  const uint32_t diff = VP8LSubPixels(col1, col2);
Packit 9c6abc
  const int kMoreWeightForRGBThanForAlpha = 9;
Packit 9c6abc
  uint32_t score;
Packit 9c6abc
  score =  PaletteComponentDistance((diff >>  0) & 0xff);
Packit 9c6abc
  score += PaletteComponentDistance((diff >>  8) & 0xff);
Packit 9c6abc
  score += PaletteComponentDistance((diff >> 16) & 0xff);
Packit 9c6abc
  score *= kMoreWeightForRGBThanForAlpha;
Packit 9c6abc
  score += PaletteComponentDistance((diff >> 24) & 0xff);
Packit 9c6abc
  return score;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WEBP_INLINE void SwapColor(uint32_t* const col1, uint32_t* const col2) {
Packit 9c6abc
  const uint32_t tmp = *col1;
Packit 9c6abc
  *col1 = *col2;
Packit 9c6abc
  *col2 = tmp;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void GreedyMinimizeDeltas(uint32_t palette[], int num_colors) {
Packit 9c6abc
  // Find greedily always the closest color of the predicted color to minimize
Packit 9c6abc
  // deltas in the palette. This reduces storage needs since the
Packit 9c6abc
  // palette is stored with delta encoding.
Packit 9c6abc
  uint32_t predict = 0x00000000;
Packit 9c6abc
  int i, k;
Packit 9c6abc
  for (i = 0; i < num_colors; ++i) {
Packit 9c6abc
    int best_ix = i;
Packit 9c6abc
    uint32_t best_score = ~0U;
Packit 9c6abc
    for (k = i; k < num_colors; ++k) {
Packit 9c6abc
      const uint32_t cur_score = PaletteColorDistance(palette[k], predict);
Packit 9c6abc
      if (best_score > cur_score) {
Packit 9c6abc
        best_score = cur_score;
Packit 9c6abc
        best_ix = k;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    SwapColor(&palette[best_ix], &palette[i]);
Packit 9c6abc
    predict = palette[i];
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// The palette has been sorted by alpha. This function checks if the other
Packit 9c6abc
// components of the palette have a monotonic development with regards to
Packit 9c6abc
// position in the palette. If all have monotonic development, there is
Packit 9c6abc
// no benefit to re-organize them greedily. A monotonic development
Packit 9c6abc
// would be spotted in green-only situations (like lossy alpha) or gray-scale
Packit 9c6abc
// images.
Packit 9c6abc
static int PaletteHasNonMonotonousDeltas(uint32_t palette[], int num_colors) {
Packit 9c6abc
  uint32_t predict = 0x000000;
Packit 9c6abc
  int i;
Packit 9c6abc
  uint8_t sign_found = 0x00;
Packit 9c6abc
  for (i = 0; i < num_colors; ++i) {
Packit 9c6abc
    const uint32_t diff = VP8LSubPixels(palette[i], predict);
Packit 9c6abc
    const uint8_t rd = (diff >> 16) & 0xff;
Packit 9c6abc
    const uint8_t gd = (diff >>  8) & 0xff;
Packit 9c6abc
    const uint8_t bd = (diff >>  0) & 0xff;
Packit 9c6abc
    if (rd != 0x00) {
Packit 9c6abc
      sign_found |= (rd < 0x80) ? 1 : 2;
Packit 9c6abc
    }
Packit 9c6abc
    if (gd != 0x00) {
Packit 9c6abc
      sign_found |= (gd < 0x80) ? 8 : 16;
Packit 9c6abc
    }
Packit 9c6abc
    if (bd != 0x00) {
Packit 9c6abc
      sign_found |= (bd < 0x80) ? 64 : 128;
Packit 9c6abc
    }
Packit 9c6abc
    predict = palette[i];
Packit 9c6abc
  }
Packit 9c6abc
  return (sign_found & (sign_found << 1)) != 0;  // two consequent signs.
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
// Palette
Packit 9c6abc
Packit 9c6abc
// If number of colors in the image is less than or equal to MAX_PALETTE_SIZE,
Packit 9c6abc
// creates a palette and returns true, else returns false.
Packit 9c6abc
static int AnalyzeAndCreatePalette(const WebPPicture* const pic,
Packit 9c6abc
                                   int low_effort,
Packit 9c6abc
                                   uint32_t palette[MAX_PALETTE_SIZE],
Packit 9c6abc
                                   int* const palette_size) {
Packit 9c6abc
  const int num_colors = WebPGetColorPalette(pic, palette);
Packit 9c6abc
  if (num_colors > MAX_PALETTE_SIZE) {
Packit 9c6abc
    *palette_size = 0;
Packit 9c6abc
    return 0;
Packit 9c6abc
  }
Packit 9c6abc
  *palette_size = num_colors;
Packit 9c6abc
  qsort(palette, num_colors, sizeof(*palette), PaletteCompareColorsForQsort);
Packit 9c6abc
  if (!low_effort && PaletteHasNonMonotonousDeltas(palette, num_colors)) {
Packit 9c6abc
    GreedyMinimizeDeltas(palette, num_colors);
Packit 9c6abc
  }
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// These five modes are evaluated and their respective entropy is computed.
Packit 9c6abc
typedef enum {
Packit 9c6abc
  kDirect = 0,
Packit 9c6abc
  kSpatial = 1,
Packit 9c6abc
  kSubGreen = 2,
Packit 9c6abc
  kSpatialSubGreen = 3,
Packit 9c6abc
  kPalette = 4,
Packit 9c6abc
  kNumEntropyIx = 5
Packit 9c6abc
} EntropyIx;
Packit 9c6abc
Packit 9c6abc
typedef enum {
Packit 9c6abc
  kHistoAlpha = 0,
Packit 9c6abc
  kHistoAlphaPred,
Packit 9c6abc
  kHistoGreen,
Packit 9c6abc
  kHistoGreenPred,
Packit 9c6abc
  kHistoRed,
Packit 9c6abc
  kHistoRedPred,
Packit 9c6abc
  kHistoBlue,
Packit 9c6abc
  kHistoBluePred,
Packit 9c6abc
  kHistoRedSubGreen,
Packit 9c6abc
  kHistoRedPredSubGreen,
Packit 9c6abc
  kHistoBlueSubGreen,
Packit 9c6abc
  kHistoBluePredSubGreen,
Packit 9c6abc
  kHistoPalette,
Packit 9c6abc
  kHistoTotal  // Must be last.
Packit 9c6abc
} HistoIx;
Packit 9c6abc
Packit 9c6abc
static void AddSingleSubGreen(int p, uint32_t* const r, uint32_t* const b) {
Packit 9c6abc
  const int green = p >> 8;  // The upper bits are masked away later.
Packit 9c6abc
  ++r[((p >> 16) - green) & 0xff];
Packit 9c6abc
  ++b[((p >>  0) - green) & 0xff];
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void AddSingle(uint32_t p,
Packit 9c6abc
                      uint32_t* const a, uint32_t* const r,
Packit 9c6abc
                      uint32_t* const g, uint32_t* const b) {
Packit 9c6abc
  ++a[(p >> 24) & 0xff];
Packit 9c6abc
  ++r[(p >> 16) & 0xff];
Packit 9c6abc
  ++g[(p >>  8) & 0xff];
Packit 9c6abc
  ++b[(p >>  0) & 0xff];
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WEBP_INLINE uint32_t HashPix(uint32_t pix) {
Packit 9c6abc
  // Note that masking with 0xffffffffu is for preventing an
Packit 9c6abc
  // 'unsigned int overflow' warning. Doesn't impact the compiled code.
Packit 9c6abc
  return ((((uint64_t)pix + (pix >> 19)) * 0x39c5fba7ull) & 0xffffffffu) >> 24;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int AnalyzeEntropy(const uint32_t* argb,
Packit 9c6abc
                          int width, int height, int argb_stride,
Packit 9c6abc
                          int use_palette,
Packit 9c6abc
                          int palette_size, int transform_bits,
Packit 9c6abc
                          EntropyIx* const min_entropy_ix,
Packit 9c6abc
                          int* const red_and_blue_always_zero) {
Packit 9c6abc
  // Allocate histogram set with cache_bits = 0.
Packit 9c6abc
  uint32_t* histo;
Packit 9c6abc
Packit 9c6abc
  if (use_palette && palette_size <= 16) {
Packit 9c6abc
    // In the case of small palettes, we pack 2, 4 or 8 pixels together. In
Packit 9c6abc
    // practice, small palettes are better than any other transform.
Packit 9c6abc
    *min_entropy_ix = kPalette;
Packit 9c6abc
    *red_and_blue_always_zero = 1;
Packit 9c6abc
    return 1;
Packit 9c6abc
  }
Packit 9c6abc
  histo = (uint32_t*)WebPSafeCalloc(kHistoTotal, sizeof(*histo) * 256);
Packit 9c6abc
  if (histo != NULL) {
Packit 9c6abc
    int i, x, y;
Packit 9c6abc
    const uint32_t* prev_row = NULL;
Packit 9c6abc
    const uint32_t* curr_row = argb;
Packit 9c6abc
    uint32_t pix_prev = argb[0];  // Skip the first pixel.
Packit 9c6abc
    for (y = 0; y < height; ++y) {
Packit 9c6abc
      for (x = 0; x < width; ++x) {
Packit 9c6abc
        const uint32_t pix = curr_row[x];
Packit 9c6abc
        const uint32_t pix_diff = VP8LSubPixels(pix, pix_prev);
Packit 9c6abc
        pix_prev = pix;
Packit 9c6abc
        if ((pix_diff == 0) || (prev_row != NULL && pix == prev_row[x])) {
Packit 9c6abc
          continue;
Packit 9c6abc
        }
Packit 9c6abc
        AddSingle(pix,
Packit 9c6abc
                  &histo[kHistoAlpha * 256],
Packit 9c6abc
                  &histo[kHistoRed * 256],
Packit 9c6abc
                  &histo[kHistoGreen * 256],
Packit 9c6abc
                  &histo[kHistoBlue * 256]);
Packit 9c6abc
        AddSingle(pix_diff,
Packit 9c6abc
                  &histo[kHistoAlphaPred * 256],
Packit 9c6abc
                  &histo[kHistoRedPred * 256],
Packit 9c6abc
                  &histo[kHistoGreenPred * 256],
Packit 9c6abc
                  &histo[kHistoBluePred * 256]);
Packit 9c6abc
        AddSingleSubGreen(pix,
Packit 9c6abc
                          &histo[kHistoRedSubGreen * 256],
Packit 9c6abc
                          &histo[kHistoBlueSubGreen * 256]);
Packit 9c6abc
        AddSingleSubGreen(pix_diff,
Packit 9c6abc
                          &histo[kHistoRedPredSubGreen * 256],
Packit 9c6abc
                          &histo[kHistoBluePredSubGreen * 256]);
Packit 9c6abc
        {
Packit 9c6abc
          // Approximate the palette by the entropy of the multiplicative hash.
Packit 9c6abc
          const uint32_t hash = HashPix(pix);
Packit 9c6abc
          ++histo[kHistoPalette * 256 + hash];
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
      prev_row = curr_row;
Packit 9c6abc
      curr_row += argb_stride;
Packit 9c6abc
    }
Packit 9c6abc
    {
Packit 9c6abc
      double entropy_comp[kHistoTotal];
Packit 9c6abc
      double entropy[kNumEntropyIx];
Packit 9c6abc
      int k;
Packit 9c6abc
      int last_mode_to_analyze = use_palette ? kPalette : kSpatialSubGreen;
Packit 9c6abc
      int j;
Packit 9c6abc
      // Let's add one zero to the predicted histograms. The zeros are removed
Packit 9c6abc
      // too efficiently by the pix_diff == 0 comparison, at least one of the
Packit 9c6abc
      // zeros is likely to exist.
Packit 9c6abc
      ++histo[kHistoRedPredSubGreen * 256];
Packit 9c6abc
      ++histo[kHistoBluePredSubGreen * 256];
Packit 9c6abc
      ++histo[kHistoRedPred * 256];
Packit 9c6abc
      ++histo[kHistoGreenPred * 256];
Packit 9c6abc
      ++histo[kHistoBluePred * 256];
Packit 9c6abc
      ++histo[kHistoAlphaPred * 256];
Packit 9c6abc
Packit 9c6abc
      for (j = 0; j < kHistoTotal; ++j) {
Packit 9c6abc
        entropy_comp[j] = VP8LBitsEntropy(&histo[j * 256], 256);
Packit 9c6abc
      }
Packit 9c6abc
      entropy[kDirect] = entropy_comp[kHistoAlpha] +
Packit 9c6abc
          entropy_comp[kHistoRed] +
Packit 9c6abc
          entropy_comp[kHistoGreen] +
Packit 9c6abc
          entropy_comp[kHistoBlue];
Packit 9c6abc
      entropy[kSpatial] = entropy_comp[kHistoAlphaPred] +
Packit 9c6abc
          entropy_comp[kHistoRedPred] +
Packit 9c6abc
          entropy_comp[kHistoGreenPred] +
Packit 9c6abc
          entropy_comp[kHistoBluePred];
Packit 9c6abc
      entropy[kSubGreen] = entropy_comp[kHistoAlpha] +
Packit 9c6abc
          entropy_comp[kHistoRedSubGreen] +
Packit 9c6abc
          entropy_comp[kHistoGreen] +
Packit 9c6abc
          entropy_comp[kHistoBlueSubGreen];
Packit 9c6abc
      entropy[kSpatialSubGreen] = entropy_comp[kHistoAlphaPred] +
Packit 9c6abc
          entropy_comp[kHistoRedPredSubGreen] +
Packit 9c6abc
          entropy_comp[kHistoGreenPred] +
Packit 9c6abc
          entropy_comp[kHistoBluePredSubGreen];
Packit 9c6abc
      entropy[kPalette] = entropy_comp[kHistoPalette];
Packit 9c6abc
Packit 9c6abc
      // When including transforms, there is an overhead in bits from
Packit 9c6abc
      // storing them. This overhead is small but matters for small images.
Packit 9c6abc
      // For spatial, there are 14 transformations.
Packit 9c6abc
      entropy[kSpatial] += VP8LSubSampleSize(width, transform_bits) *
Packit 9c6abc
                           VP8LSubSampleSize(height, transform_bits) *
Packit 9c6abc
                           VP8LFastLog2(14);
Packit 9c6abc
      // For color transforms: 24 as only 3 channels are considered in a
Packit 9c6abc
      // ColorTransformElement.
Packit 9c6abc
      entropy[kSpatialSubGreen] += VP8LSubSampleSize(width, transform_bits) *
Packit 9c6abc
                                   VP8LSubSampleSize(height, transform_bits) *
Packit 9c6abc
                                   VP8LFastLog2(24);
Packit 9c6abc
      // For palettes, add the cost of storing the palette.
Packit 9c6abc
      // We empirically estimate the cost of a compressed entry as 8 bits.
Packit 9c6abc
      // The palette is differential-coded when compressed hence a much
Packit 9c6abc
      // lower cost than sizeof(uint32_t)*8.
Packit 9c6abc
      entropy[kPalette] += palette_size * 8;
Packit 9c6abc
Packit 9c6abc
      *min_entropy_ix = kDirect;
Packit 9c6abc
      for (k = kDirect + 1; k <= last_mode_to_analyze; ++k) {
Packit 9c6abc
        if (entropy[*min_entropy_ix] > entropy[k]) {
Packit 9c6abc
          *min_entropy_ix = (EntropyIx)k;
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
      assert((int)*min_entropy_ix <= last_mode_to_analyze);
Packit 9c6abc
      *red_and_blue_always_zero = 1;
Packit 9c6abc
      // Let's check if the histogram of the chosen entropy mode has
Packit 9c6abc
      // non-zero red and blue values. If all are zero, we can later skip
Packit 9c6abc
      // the cross color optimization.
Packit 9c6abc
      {
Packit 9c6abc
        static const uint8_t kHistoPairs[5][2] = {
Packit 9c6abc
          { kHistoRed, kHistoBlue },
Packit 9c6abc
          { kHistoRedPred, kHistoBluePred },
Packit 9c6abc
          { kHistoRedSubGreen, kHistoBlueSubGreen },
Packit 9c6abc
          { kHistoRedPredSubGreen, kHistoBluePredSubGreen },
Packit 9c6abc
          { kHistoRed, kHistoBlue }
Packit 9c6abc
        };
Packit 9c6abc
        const uint32_t* const red_histo =
Packit 9c6abc
            &histo[256 * kHistoPairs[*min_entropy_ix][0]];
Packit 9c6abc
        const uint32_t* const blue_histo =
Packit 9c6abc
            &histo[256 * kHistoPairs[*min_entropy_ix][1]];
Packit 9c6abc
        for (i = 1; i < 256; ++i) {
Packit 9c6abc
          if ((red_histo[i] | blue_histo[i]) != 0) {
Packit 9c6abc
            *red_and_blue_always_zero = 0;
Packit 9c6abc
            break;
Packit 9c6abc
          }
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    WebPSafeFree(histo);
Packit 9c6abc
    return 1;
Packit 9c6abc
  } else {
Packit 9c6abc
    return 0;
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int GetHistoBits(int method, int use_palette, int width, int height) {
Packit 9c6abc
  // Make tile size a function of encoding method (Range: 0 to 6).
Packit 9c6abc
  int histo_bits = (use_palette ? 9 : 7) - method;
Packit 9c6abc
  while (1) {
Packit 9c6abc
    const int huff_image_size = VP8LSubSampleSize(width, histo_bits) *
Packit 9c6abc
                                VP8LSubSampleSize(height, histo_bits);
Packit 9c6abc
    if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break;
Packit 9c6abc
    ++histo_bits;
Packit 9c6abc
  }
Packit 9c6abc
  return (histo_bits < MIN_HUFFMAN_BITS) ? MIN_HUFFMAN_BITS :
Packit 9c6abc
         (histo_bits > MAX_HUFFMAN_BITS) ? MAX_HUFFMAN_BITS : histo_bits;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int GetTransformBits(int method, int histo_bits) {
Packit 9c6abc
  const int max_transform_bits = (method < 4) ? 6 : (method > 4) ? 4 : 5;
Packit 9c6abc
  const int res =
Packit 9c6abc
      (histo_bits > max_transform_bits) ? max_transform_bits : histo_bits;
Packit 9c6abc
  assert(res <= MAX_TRANSFORM_BITS);
Packit 9c6abc
  return res;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Set of parameters to be used in each iteration of the cruncher.
Packit 9c6abc
#define CRUNCH_CONFIGS_LZ77_MAX 2
Packit 9c6abc
typedef struct {
Packit 9c6abc
  int entropy_idx_;
Packit 9c6abc
  int lz77s_types_to_try_[CRUNCH_CONFIGS_LZ77_MAX];
Packit 9c6abc
  int lz77s_types_to_try_size_;
Packit 9c6abc
} CrunchConfig;
Packit 9c6abc
Packit 9c6abc
#define CRUNCH_CONFIGS_MAX kNumEntropyIx
Packit 9c6abc
Packit 9c6abc
static int EncoderAnalyze(VP8LEncoder* const enc,
Packit 9c6abc
                          CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX],
Packit 9c6abc
                          int* const crunch_configs_size,
Packit 9c6abc
                          int* const red_and_blue_always_zero) {
Packit 9c6abc
  const WebPPicture* const pic = enc->pic_;
Packit 9c6abc
  const int width = pic->width;
Packit 9c6abc
  const int height = pic->height;
Packit 9c6abc
  const WebPConfig* const config = enc->config_;
Packit 9c6abc
  const int method = config->method;
Packit 9c6abc
  const int low_effort = (config->method == 0);
Packit 9c6abc
  int i;
Packit 9c6abc
  int use_palette;
Packit 9c6abc
  int n_lz77s;
Packit 9c6abc
  assert(pic != NULL && pic->argb != NULL);
Packit 9c6abc
Packit 9c6abc
  use_palette =
Packit 9c6abc
      AnalyzeAndCreatePalette(pic, low_effort,
Packit 9c6abc
                              enc->palette_, &enc->palette_size_);
Packit 9c6abc
Packit 9c6abc
  // Empirical bit sizes.
Packit 9c6abc
  enc->histo_bits_ = GetHistoBits(method, use_palette,
Packit 9c6abc
                                  pic->width, pic->height);
Packit 9c6abc
  enc->transform_bits_ = GetTransformBits(method, enc->histo_bits_);
Packit 9c6abc
Packit 9c6abc
  if (low_effort) {
Packit 9c6abc
    // AnalyzeEntropy is somewhat slow.
Packit 9c6abc
    crunch_configs[0].entropy_idx_ = use_palette ? kPalette : kSpatialSubGreen;
Packit 9c6abc
    n_lz77s = 1;
Packit 9c6abc
    *crunch_configs_size = 1;
Packit 9c6abc
  } else {
Packit 9c6abc
    EntropyIx min_entropy_ix;
Packit 9c6abc
    // Try out multiple LZ77 on images with few colors.
Packit 9c6abc
    n_lz77s = (enc->palette_size_ > 0 && enc->palette_size_ <= 16) ? 2 : 1;
Packit 9c6abc
    if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride, use_palette,
Packit 9c6abc
                        enc->palette_size_, enc->transform_bits_,
Packit 9c6abc
                        &min_entropy_ix, red_and_blue_always_zero)) {
Packit 9c6abc
      return 0;
Packit 9c6abc
    }
Packit 9c6abc
    if (method == 6 && config->quality == 100) {
Packit 9c6abc
      // Go brute force on all transforms.
Packit 9c6abc
      *crunch_configs_size = 0;
Packit 9c6abc
      for (i = 0; i < kNumEntropyIx; ++i) {
Packit 9c6abc
        if (i != kPalette || use_palette) {
Packit 9c6abc
          assert(*crunch_configs_size < CRUNCH_CONFIGS_MAX);
Packit 9c6abc
          crunch_configs[(*crunch_configs_size)++].entropy_idx_ = i;
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
    } else {
Packit 9c6abc
      // Only choose the guessed best transform.
Packit 9c6abc
      *crunch_configs_size = 1;
Packit 9c6abc
      crunch_configs[0].entropy_idx_ = min_entropy_ix;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  // Fill in the different LZ77s.
Packit 9c6abc
  assert(n_lz77s <= CRUNCH_CONFIGS_LZ77_MAX);
Packit 9c6abc
  for (i = 0; i < *crunch_configs_size; ++i) {
Packit 9c6abc
    int j;
Packit 9c6abc
    for (j = 0; j < n_lz77s; ++j) {
Packit 9c6abc
      crunch_configs[i].lz77s_types_to_try_[j] =
Packit 9c6abc
          (j == 0) ? kLZ77Standard | kLZ77RLE : kLZ77Box;
Packit 9c6abc
    }
Packit 9c6abc
    crunch_configs[i].lz77s_types_to_try_size_ = n_lz77s;
Packit 9c6abc
  }
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int EncoderInit(VP8LEncoder* const enc) {
Packit 9c6abc
  const WebPPicture* const pic = enc->pic_;
Packit 9c6abc
  const int width = pic->width;
Packit 9c6abc
  const int height = pic->height;
Packit 9c6abc
  const int pix_cnt = width * height;
Packit 9c6abc
  // we round the block size up, so we're guaranteed to have
Packit 9c6abc
  // at most MAX_REFS_BLOCK_PER_IMAGE blocks used:
Packit 9c6abc
  const int refs_block_size = (pix_cnt - 1) / MAX_REFS_BLOCK_PER_IMAGE + 1;
Packit 9c6abc
  int i;
Packit 9c6abc
  if (!VP8LHashChainInit(&enc->hash_chain_, pix_cnt)) return 0;
Packit 9c6abc
Packit 9c6abc
  for (i = 0; i < 3; ++i) VP8LBackwardRefsInit(&enc->refs_[i], refs_block_size);
Packit 9c6abc
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Returns false in case of memory error.
Packit 9c6abc
static int GetHuffBitLengthsAndCodes(
Packit 9c6abc
    const VP8LHistogramSet* const histogram_image,
Packit 9c6abc
    HuffmanTreeCode* const huffman_codes) {
Packit 9c6abc
  int i, k;
Packit 9c6abc
  int ok = 0;
Packit 9c6abc
  uint64_t total_length_size = 0;
Packit 9c6abc
  uint8_t* mem_buf = NULL;
Packit 9c6abc
  const int histogram_image_size = histogram_image->size;
Packit 9c6abc
  int max_num_symbols = 0;
Packit 9c6abc
  uint8_t* buf_rle = NULL;
Packit 9c6abc
  HuffmanTree* huff_tree = NULL;
Packit 9c6abc
Packit 9c6abc
  // Iterate over all histograms and get the aggregate number of codes used.
Packit 9c6abc
  for (i = 0; i < histogram_image_size; ++i) {
Packit 9c6abc
    const VP8LHistogram* const histo = histogram_image->histograms[i];
Packit 9c6abc
    HuffmanTreeCode* const codes = &huffman_codes[5 * i];
Packit 9c6abc
    for (k = 0; k < 5; ++k) {
Packit 9c6abc
      const int num_symbols =
Packit 9c6abc
          (k == 0) ? VP8LHistogramNumCodes(histo->palette_code_bits_) :
Packit 9c6abc
          (k == 4) ? NUM_DISTANCE_CODES : 256;
Packit 9c6abc
      codes[k].num_symbols = num_symbols;
Packit 9c6abc
      total_length_size += num_symbols;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Allocate and Set Huffman codes.
Packit 9c6abc
  {
Packit 9c6abc
    uint16_t* codes;
Packit 9c6abc
    uint8_t* lengths;
Packit 9c6abc
    mem_buf = (uint8_t*)WebPSafeCalloc(total_length_size,
Packit 9c6abc
                                       sizeof(*lengths) + sizeof(*codes));
Packit 9c6abc
    if (mem_buf == NULL) goto End;
Packit 9c6abc
Packit 9c6abc
    codes = (uint16_t*)mem_buf;
Packit 9c6abc
    lengths = (uint8_t*)&codes[total_length_size];
Packit 9c6abc
    for (i = 0; i < 5 * histogram_image_size; ++i) {
Packit 9c6abc
      const int bit_length = huffman_codes[i].num_symbols;
Packit 9c6abc
      huffman_codes[i].codes = codes;
Packit 9c6abc
      huffman_codes[i].code_lengths = lengths;
Packit 9c6abc
      codes += bit_length;
Packit 9c6abc
      lengths += bit_length;
Packit 9c6abc
      if (max_num_symbols < bit_length) {
Packit 9c6abc
        max_num_symbols = bit_length;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  buf_rle = (uint8_t*)WebPSafeMalloc(1ULL, max_num_symbols);
Packit 9c6abc
  huff_tree = (HuffmanTree*)WebPSafeMalloc(3ULL * max_num_symbols,
Packit 9c6abc
                                           sizeof(*huff_tree));
Packit 9c6abc
  if (buf_rle == NULL || huff_tree == NULL) goto End;
Packit 9c6abc
Packit 9c6abc
  // Create Huffman trees.
Packit 9c6abc
  for (i = 0; i < histogram_image_size; ++i) {
Packit 9c6abc
    HuffmanTreeCode* const codes = &huffman_codes[5 * i];
Packit 9c6abc
    VP8LHistogram* const histo = histogram_image->histograms[i];
Packit 9c6abc
    VP8LCreateHuffmanTree(histo->literal_, 15, buf_rle, huff_tree, codes + 0);
Packit 9c6abc
    VP8LCreateHuffmanTree(histo->red_, 15, buf_rle, huff_tree, codes + 1);
Packit 9c6abc
    VP8LCreateHuffmanTree(histo->blue_, 15, buf_rle, huff_tree, codes + 2);
Packit 9c6abc
    VP8LCreateHuffmanTree(histo->alpha_, 15, buf_rle, huff_tree, codes + 3);
Packit 9c6abc
    VP8LCreateHuffmanTree(histo->distance_, 15, buf_rle, huff_tree, codes + 4);
Packit 9c6abc
  }
Packit 9c6abc
  ok = 1;
Packit 9c6abc
 End:
Packit 9c6abc
  WebPSafeFree(huff_tree);
Packit 9c6abc
  WebPSafeFree(buf_rle);
Packit 9c6abc
  if (!ok) {
Packit 9c6abc
    WebPSafeFree(mem_buf);
Packit 9c6abc
    memset(huffman_codes, 0, 5 * histogram_image_size * sizeof(*huffman_codes));
Packit 9c6abc
  }
Packit 9c6abc
  return ok;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void StoreHuffmanTreeOfHuffmanTreeToBitMask(
Packit 9c6abc
    VP8LBitWriter* const bw, const uint8_t* code_length_bitdepth) {
Packit 9c6abc
  // RFC 1951 will calm you down if you are worried about this funny sequence.
Packit 9c6abc
  // This sequence is tuned from that, but more weighted for lower symbol count,
Packit 9c6abc
  // and more spiking histograms.
Packit 9c6abc
  static const uint8_t kStorageOrder[CODE_LENGTH_CODES] = {
Packit 9c6abc
    17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
Packit 9c6abc
  };
Packit 9c6abc
  int i;
Packit 9c6abc
  // Throw away trailing zeros:
Packit 9c6abc
  int codes_to_store = CODE_LENGTH_CODES;
Packit 9c6abc
  for (; codes_to_store > 4; --codes_to_store) {
Packit 9c6abc
    if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) {
Packit 9c6abc
      break;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  VP8LPutBits(bw, codes_to_store - 4, 4);
Packit 9c6abc
  for (i = 0; i < codes_to_store; ++i) {
Packit 9c6abc
    VP8LPutBits(bw, code_length_bitdepth[kStorageOrder[i]], 3);
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void ClearHuffmanTreeIfOnlyOneSymbol(
Packit 9c6abc
    HuffmanTreeCode* const huffman_code) {
Packit 9c6abc
  int k;
Packit 9c6abc
  int count = 0;
Packit 9c6abc
  for (k = 0; k < huffman_code->num_symbols; ++k) {
Packit 9c6abc
    if (huffman_code->code_lengths[k] != 0) {
Packit 9c6abc
      ++count;
Packit 9c6abc
      if (count > 1) return;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  for (k = 0; k < huffman_code->num_symbols; ++k) {
Packit 9c6abc
    huffman_code->code_lengths[k] = 0;
Packit 9c6abc
    huffman_code->codes[k] = 0;
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void StoreHuffmanTreeToBitMask(
Packit 9c6abc
    VP8LBitWriter* const bw,
Packit 9c6abc
    const HuffmanTreeToken* const tokens, const int num_tokens,
Packit 9c6abc
    const HuffmanTreeCode* const huffman_code) {
Packit 9c6abc
  int i;
Packit 9c6abc
  for (i = 0; i < num_tokens; ++i) {
Packit 9c6abc
    const int ix = tokens[i].code;
Packit 9c6abc
    const int extra_bits = tokens[i].extra_bits;
Packit 9c6abc
    VP8LPutBits(bw, huffman_code->codes[ix], huffman_code->code_lengths[ix]);
Packit 9c6abc
    switch (ix) {
Packit 9c6abc
      case 16:
Packit 9c6abc
        VP8LPutBits(bw, extra_bits, 2);
Packit 9c6abc
        break;
Packit 9c6abc
      case 17:
Packit 9c6abc
        VP8LPutBits(bw, extra_bits, 3);
Packit 9c6abc
        break;
Packit 9c6abc
      case 18:
Packit 9c6abc
        VP8LPutBits(bw, extra_bits, 7);
Packit 9c6abc
        break;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// 'huff_tree' and 'tokens' are pre-alloacted buffers.
Packit 9c6abc
static void StoreFullHuffmanCode(VP8LBitWriter* const bw,
Packit 9c6abc
                                 HuffmanTree* const huff_tree,
Packit 9c6abc
                                 HuffmanTreeToken* const tokens,
Packit 9c6abc
                                 const HuffmanTreeCode* const tree) {
Packit 9c6abc
  uint8_t code_length_bitdepth[CODE_LENGTH_CODES] = { 0 };
Packit 9c6abc
  uint16_t code_length_bitdepth_symbols[CODE_LENGTH_CODES] = { 0 };
Packit 9c6abc
  const int max_tokens = tree->num_symbols;
Packit 9c6abc
  int num_tokens;
Packit 9c6abc
  HuffmanTreeCode huffman_code;
Packit 9c6abc
  huffman_code.num_symbols = CODE_LENGTH_CODES;
Packit 9c6abc
  huffman_code.code_lengths = code_length_bitdepth;
Packit 9c6abc
  huffman_code.codes = code_length_bitdepth_symbols;
Packit 9c6abc
Packit 9c6abc
  VP8LPutBits(bw, 0, 1);
Packit 9c6abc
  num_tokens = VP8LCreateCompressedHuffmanTree(tree, tokens, max_tokens);
Packit 9c6abc
  {
Packit 9c6abc
    uint32_t histogram[CODE_LENGTH_CODES] = { 0 };
Packit 9c6abc
    uint8_t buf_rle[CODE_LENGTH_CODES] = { 0 };
Packit 9c6abc
    int i;
Packit 9c6abc
    for (i = 0; i < num_tokens; ++i) {
Packit 9c6abc
      ++histogram[tokens[i].code];
Packit 9c6abc
    }
Packit 9c6abc
Packit 9c6abc
    VP8LCreateHuffmanTree(histogram, 7, buf_rle, huff_tree, &huffman_code);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  StoreHuffmanTreeOfHuffmanTreeToBitMask(bw, code_length_bitdepth);
Packit 9c6abc
  ClearHuffmanTreeIfOnlyOneSymbol(&huffman_code);
Packit 9c6abc
  {
Packit 9c6abc
    int trailing_zero_bits = 0;
Packit 9c6abc
    int trimmed_length = num_tokens;
Packit 9c6abc
    int write_trimmed_length;
Packit 9c6abc
    int length;
Packit 9c6abc
    int i = num_tokens;
Packit 9c6abc
    while (i-- > 0) {
Packit 9c6abc
      const int ix = tokens[i].code;
Packit 9c6abc
      if (ix == 0 || ix == 17 || ix == 18) {
Packit 9c6abc
        --trimmed_length;   // discount trailing zeros
Packit 9c6abc
        trailing_zero_bits += code_length_bitdepth[ix];
Packit 9c6abc
        if (ix == 17) {
Packit 9c6abc
          trailing_zero_bits += 3;
Packit 9c6abc
        } else if (ix == 18) {
Packit 9c6abc
          trailing_zero_bits += 7;
Packit 9c6abc
        }
Packit 9c6abc
      } else {
Packit 9c6abc
        break;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    write_trimmed_length = (trimmed_length > 1 && trailing_zero_bits > 12);
Packit 9c6abc
    length = write_trimmed_length ? trimmed_length : num_tokens;
Packit 9c6abc
    VP8LPutBits(bw, write_trimmed_length, 1);
Packit 9c6abc
    if (write_trimmed_length) {
Packit 9c6abc
      if (trimmed_length == 2) {
Packit 9c6abc
        VP8LPutBits(bw, 0, 3 + 2);     // nbitpairs=1, trimmed_length=2
Packit 9c6abc
      } else {
Packit 9c6abc
        const int nbits = BitsLog2Floor(trimmed_length - 2);
Packit 9c6abc
        const int nbitpairs = nbits / 2 + 1;
Packit 9c6abc
        assert(trimmed_length > 2);
Packit 9c6abc
        assert(nbitpairs - 1 < 8);
Packit 9c6abc
        VP8LPutBits(bw, nbitpairs - 1, 3);
Packit 9c6abc
        VP8LPutBits(bw, trimmed_length - 2, nbitpairs * 2);
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    StoreHuffmanTreeToBitMask(bw, tokens, length, &huffman_code);
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// 'huff_tree' and 'tokens' are pre-alloacted buffers.
Packit 9c6abc
static void StoreHuffmanCode(VP8LBitWriter* const bw,
Packit 9c6abc
                             HuffmanTree* const huff_tree,
Packit 9c6abc
                             HuffmanTreeToken* const tokens,
Packit 9c6abc
                             const HuffmanTreeCode* const huffman_code) {
Packit 9c6abc
  int i;
Packit 9c6abc
  int count = 0;
Packit 9c6abc
  int symbols[2] = { 0, 0 };
Packit 9c6abc
  const int kMaxBits = 8;
Packit 9c6abc
  const int kMaxSymbol = 1 << kMaxBits;
Packit 9c6abc
Packit 9c6abc
  // Check whether it's a small tree.
Packit 9c6abc
  for (i = 0; i < huffman_code->num_symbols && count < 3; ++i) {
Packit 9c6abc
    if (huffman_code->code_lengths[i] != 0) {
Packit 9c6abc
      if (count < 2) symbols[count] = i;
Packit 9c6abc
      ++count;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (count == 0) {   // emit minimal tree for empty cases
Packit 9c6abc
    // bits: small tree marker: 1, count-1: 0, large 8-bit code: 0, code: 0
Packit 9c6abc
    VP8LPutBits(bw, 0x01, 4);
Packit 9c6abc
  } else if (count <= 2 && symbols[0] < kMaxSymbol && symbols[1] < kMaxSymbol) {
Packit 9c6abc
    VP8LPutBits(bw, 1, 1);  // Small tree marker to encode 1 or 2 symbols.
Packit 9c6abc
    VP8LPutBits(bw, count - 1, 1);
Packit 9c6abc
    if (symbols[0] <= 1) {
Packit 9c6abc
      VP8LPutBits(bw, 0, 1);  // Code bit for small (1 bit) symbol value.
Packit 9c6abc
      VP8LPutBits(bw, symbols[0], 1);
Packit 9c6abc
    } else {
Packit 9c6abc
      VP8LPutBits(bw, 1, 1);
Packit 9c6abc
      VP8LPutBits(bw, symbols[0], 8);
Packit 9c6abc
    }
Packit 9c6abc
    if (count == 2) {
Packit 9c6abc
      VP8LPutBits(bw, symbols[1], 8);
Packit 9c6abc
    }
Packit 9c6abc
  } else {
Packit 9c6abc
    StoreFullHuffmanCode(bw, huff_tree, tokens, huffman_code);
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WEBP_INLINE void WriteHuffmanCode(VP8LBitWriter* const bw,
Packit 9c6abc
                             const HuffmanTreeCode* const code,
Packit 9c6abc
                             int code_index) {
Packit 9c6abc
  const int depth = code->code_lengths[code_index];
Packit 9c6abc
  const int symbol = code->codes[code_index];
Packit 9c6abc
  VP8LPutBits(bw, symbol, depth);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WEBP_INLINE void WriteHuffmanCodeWithExtraBits(
Packit 9c6abc
    VP8LBitWriter* const bw,
Packit 9c6abc
    const HuffmanTreeCode* const code,
Packit 9c6abc
    int code_index,
Packit 9c6abc
    int bits,
Packit 9c6abc
    int n_bits) {
Packit 9c6abc
  const int depth = code->code_lengths[code_index];
Packit 9c6abc
  const int symbol = code->codes[code_index];
Packit 9c6abc
  VP8LPutBits(bw, (bits << depth) | symbol, depth + n_bits);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPEncodingError StoreImageToBitMask(
Packit 9c6abc
    VP8LBitWriter* const bw, int width, int histo_bits,
Packit 9c6abc
    const VP8LBackwardRefs* const refs,
Packit 9c6abc
    const uint16_t* histogram_symbols,
Packit 9c6abc
    const HuffmanTreeCode* const huffman_codes) {
Packit 9c6abc
  const int histo_xsize = histo_bits ? VP8LSubSampleSize(width, histo_bits) : 1;
Packit 9c6abc
  const int tile_mask = (histo_bits == 0) ? 0 : -(1 << histo_bits);
Packit 9c6abc
  // x and y trace the position in the image.
Packit 9c6abc
  int x = 0;
Packit 9c6abc
  int y = 0;
Packit 9c6abc
  int tile_x = x & tile_mask;
Packit 9c6abc
  int tile_y = y & tile_mask;
Packit 9c6abc
  int histogram_ix = histogram_symbols[0];
Packit 9c6abc
  const HuffmanTreeCode* codes = huffman_codes + 5 * histogram_ix;
Packit 9c6abc
  VP8LRefsCursor c = VP8LRefsCursorInit(refs);
Packit 9c6abc
  while (VP8LRefsCursorOk(&c)) {
Packit 9c6abc
    const PixOrCopy* const v = c.cur_pos;
Packit 9c6abc
    if ((tile_x != (x & tile_mask)) || (tile_y != (y & tile_mask))) {
Packit 9c6abc
      tile_x = x & tile_mask;
Packit 9c6abc
      tile_y = y & tile_mask;
Packit 9c6abc
      histogram_ix = histogram_symbols[(y >> histo_bits) * histo_xsize +
Packit 9c6abc
                                       (x >> histo_bits)];
Packit 9c6abc
      codes = huffman_codes + 5 * histogram_ix;
Packit 9c6abc
    }
Packit 9c6abc
    if (PixOrCopyIsLiteral(v)) {
Packit 9c6abc
      static const uint8_t order[] = { 1, 2, 0, 3 };
Packit 9c6abc
      int k;
Packit 9c6abc
      for (k = 0; k < 4; ++k) {
Packit 9c6abc
        const int code = PixOrCopyLiteral(v, order[k]);
Packit 9c6abc
        WriteHuffmanCode(bw, codes + k, code);
Packit 9c6abc
      }
Packit 9c6abc
    } else if (PixOrCopyIsCacheIdx(v)) {
Packit 9c6abc
      const int code = PixOrCopyCacheIdx(v);
Packit 9c6abc
      const int literal_ix = 256 + NUM_LENGTH_CODES + code;
Packit 9c6abc
      WriteHuffmanCode(bw, codes, literal_ix);
Packit 9c6abc
    } else {
Packit 9c6abc
      int bits, n_bits;
Packit 9c6abc
      int code;
Packit 9c6abc
Packit 9c6abc
      const int distance = PixOrCopyDistance(v);
Packit 9c6abc
      VP8LPrefixEncode(v->len, &code, &n_bits, &bits);
Packit 9c6abc
      WriteHuffmanCodeWithExtraBits(bw, codes, 256 + code, bits, n_bits);
Packit 9c6abc
Packit 9c6abc
      // Don't write the distance with the extra bits code since
Packit 9c6abc
      // the distance can be up to 18 bits of extra bits, and the prefix
Packit 9c6abc
      // 15 bits, totaling to 33, and our PutBits only supports up to 32 bits.
Packit 9c6abc
      VP8LPrefixEncode(distance, &code, &n_bits, &bits);
Packit 9c6abc
      WriteHuffmanCode(bw, codes + 4, code);
Packit 9c6abc
      VP8LPutBits(bw, bits, n_bits);
Packit 9c6abc
    }
Packit 9c6abc
    x += PixOrCopyLength(v);
Packit 9c6abc
    while (x >= width) {
Packit 9c6abc
      x -= width;
Packit 9c6abc
      ++y;
Packit 9c6abc
    }
Packit 9c6abc
    VP8LRefsCursorNext(&c);
Packit 9c6abc
  }
Packit 9c6abc
  return bw->error_ ? VP8_ENC_ERROR_OUT_OF_MEMORY : VP8_ENC_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31
Packit 9c6abc
static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
Packit 9c6abc
                                              const uint32_t* const argb,
Packit 9c6abc
                                              VP8LHashChain* const hash_chain,
Packit 9c6abc
                                              VP8LBackwardRefs* const refs_tmp1,
Packit 9c6abc
                                              VP8LBackwardRefs* const refs_tmp2,
Packit 9c6abc
                                              int width, int height,
Packit 9c6abc
                                              int quality, int low_effort) {
Packit 9c6abc
  int i;
Packit 9c6abc
  int max_tokens = 0;
Packit 9c6abc
  WebPEncodingError err = VP8_ENC_OK;
Packit 9c6abc
  VP8LBackwardRefs* refs;
Packit 9c6abc
  HuffmanTreeToken* tokens = NULL;
Packit 9c6abc
  HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } };
Packit 9c6abc
  const uint16_t histogram_symbols[1] = { 0 };    // only one tree, one symbol
Packit 9c6abc
  int cache_bits = 0;
Packit 9c6abc
  VP8LHistogramSet* histogram_image = NULL;
Packit 9c6abc
  HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc(
Packit 9c6abc
        3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
Packit 9c6abc
  if (huff_tree == NULL) {
Packit 9c6abc
    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Calculate backward references from ARGB image.
Packit 9c6abc
  if (!VP8LHashChainFill(hash_chain, quality, argb, width, height,
Packit 9c6abc
                         low_effort)) {
Packit 9c6abc
    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
  refs = VP8LGetBackwardReferences(width, height, argb, quality, 0,
Packit 9c6abc
                                   kLZ77Standard | kLZ77RLE, &cache_bits,
Packit 9c6abc
                                   hash_chain, refs_tmp1, refs_tmp2);
Packit 9c6abc
  if (refs == NULL) {
Packit 9c6abc
    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
  histogram_image = VP8LAllocateHistogramSet(1, cache_bits);
Packit 9c6abc
  if (histogram_image == NULL) {
Packit 9c6abc
    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Build histogram image and symbols from backward references.
Packit 9c6abc
  VP8LHistogramStoreRefs(refs, histogram_image->histograms[0]);
Packit 9c6abc
Packit 9c6abc
  // Create Huffman bit lengths and codes for each histogram image.
Packit 9c6abc
  assert(histogram_image->size == 1);
Packit 9c6abc
  if (!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
Packit 9c6abc
    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // No color cache, no Huffman image.
Packit 9c6abc
  VP8LPutBits(bw, 0, 1);
Packit 9c6abc
Packit 9c6abc
  // Find maximum number of symbols for the huffman tree-set.
Packit 9c6abc
  for (i = 0; i < 5; ++i) {
Packit 9c6abc
    HuffmanTreeCode* const codes = &huffman_codes[i];
Packit 9c6abc
    if (max_tokens < codes->num_symbols) {
Packit 9c6abc
      max_tokens = codes->num_symbols;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens));
Packit 9c6abc
  if (tokens == NULL) {
Packit 9c6abc
    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Store Huffman codes.
Packit 9c6abc
  for (i = 0; i < 5; ++i) {
Packit 9c6abc
    HuffmanTreeCode* const codes = &huffman_codes[i];
Packit 9c6abc
    StoreHuffmanCode(bw, huff_tree, tokens, codes);
Packit 9c6abc
    ClearHuffmanTreeIfOnlyOneSymbol(codes);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Store actual literals.
Packit 9c6abc
  err = StoreImageToBitMask(bw, width, 0, refs, histogram_symbols,
Packit 9c6abc
                            huffman_codes);
Packit 9c6abc
Packit 9c6abc
 Error:
Packit 9c6abc
  WebPSafeFree(tokens);
Packit 9c6abc
  WebPSafeFree(huff_tree);
Packit 9c6abc
  VP8LFreeHistogramSet(histogram_image);
Packit 9c6abc
  WebPSafeFree(huffman_codes[0].codes);
Packit 9c6abc
  return err;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPEncodingError EncodeImageInternal(
Packit 9c6abc
    VP8LBitWriter* const bw, const uint32_t* const argb,
Packit 9c6abc
    VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[3], int width,
Packit 9c6abc
    int height, int quality, int low_effort, int use_cache,
Packit 9c6abc
    const CrunchConfig* const config, int* cache_bits, int histogram_bits,
Packit 9c6abc
    size_t init_byte_position, int* const hdr_size, int* const data_size) {
Packit 9c6abc
  WebPEncodingError err = VP8_ENC_OK;
Packit 9c6abc
  const uint32_t histogram_image_xysize =
Packit 9c6abc
      VP8LSubSampleSize(width, histogram_bits) *
Packit 9c6abc
      VP8LSubSampleSize(height, histogram_bits);
Packit 9c6abc
  VP8LHistogramSet* histogram_image = NULL;
Packit 9c6abc
  VP8LHistogram* tmp_histo = NULL;
Packit 9c6abc
  int histogram_image_size = 0;
Packit 9c6abc
  size_t bit_array_size = 0;
Packit 9c6abc
  HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc(
Packit 9c6abc
      3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
Packit 9c6abc
  HuffmanTreeToken* tokens = NULL;
Packit 9c6abc
  HuffmanTreeCode* huffman_codes = NULL;
Packit 9c6abc
  VP8LBackwardRefs* refs_best;
Packit 9c6abc
  VP8LBackwardRefs* refs_tmp;
Packit 9c6abc
  uint16_t* const histogram_symbols =
Packit 9c6abc
      (uint16_t*)WebPSafeMalloc(histogram_image_xysize,
Packit 9c6abc
                                sizeof(*histogram_symbols));
Packit 9c6abc
  int lz77s_idx;
Packit 9c6abc
  VP8LBitWriter bw_init = *bw, bw_best;
Packit 9c6abc
  int hdr_size_tmp;
Packit 9c6abc
  assert(histogram_bits >= MIN_HUFFMAN_BITS);
Packit 9c6abc
  assert(histogram_bits <= MAX_HUFFMAN_BITS);
Packit 9c6abc
  assert(hdr_size != NULL);
Packit 9c6abc
  assert(data_size != NULL);
Packit 9c6abc
Packit 9c6abc
  if (histogram_symbols == NULL) {
Packit 9c6abc
    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (use_cache) {
Packit 9c6abc
    // If the value is different from zero, it has been set during the
Packit 9c6abc
    // palette analysis.
Packit 9c6abc
    if (*cache_bits == 0) *cache_bits = MAX_COLOR_CACHE_BITS;
Packit 9c6abc
  } else {
Packit 9c6abc
    *cache_bits = 0;
Packit 9c6abc
  }
Packit 9c6abc
  // 'best_refs' is the reference to the best backward refs and points to one
Packit 9c6abc
  // of refs_array[0] or refs_array[1].
Packit 9c6abc
  // Calculate backward references from ARGB image.
Packit 9c6abc
  if (huff_tree == NULL ||
Packit 9c6abc
      !VP8LHashChainFill(hash_chain, quality, argb, width, height,
Packit 9c6abc
                         low_effort) ||
Packit 9c6abc
      !VP8LBitWriterInit(&bw_best, 0) ||
Packit 9c6abc
      (config->lz77s_types_to_try_size_ > 1 &&
Packit 9c6abc
       !VP8LBitWriterClone(bw, &bw_best))) {
Packit 9c6abc
    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
  for (lz77s_idx = 0; lz77s_idx < config->lz77s_types_to_try_size_;
Packit 9c6abc
       ++lz77s_idx) {
Packit 9c6abc
    refs_best = VP8LGetBackwardReferences(
Packit 9c6abc
        width, height, argb, quality, low_effort,
Packit 9c6abc
        config->lz77s_types_to_try_[lz77s_idx], cache_bits, hash_chain,
Packit 9c6abc
        &refs_array[0], &refs_array[1]);
Packit 9c6abc
    if (refs_best == NULL) {
Packit 9c6abc
      err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
      goto Error;
Packit 9c6abc
    }
Packit 9c6abc
    // Keep the best references aside and use the other element from the first
Packit 9c6abc
    // two as a temporary for later usage.
Packit 9c6abc
    refs_tmp = &refs_array[refs_best == &refs_array[0] ? 1 : 0];
Packit 9c6abc
Packit 9c6abc
    histogram_image =
Packit 9c6abc
        VP8LAllocateHistogramSet(histogram_image_xysize, *cache_bits);
Packit 9c6abc
    tmp_histo = VP8LAllocateHistogram(*cache_bits);
Packit 9c6abc
    if (histogram_image == NULL || tmp_histo == NULL) {
Packit 9c6abc
      err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
      goto Error;
Packit 9c6abc
    }
Packit 9c6abc
Packit 9c6abc
    // Build histogram image and symbols from backward references.
Packit 9c6abc
    if (!VP8LGetHistoImageSymbols(width, height, refs_best, quality, low_effort,
Packit 9c6abc
                                  histogram_bits, *cache_bits, histogram_image,
Packit 9c6abc
                                  tmp_histo, histogram_symbols)) {
Packit 9c6abc
      err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
      goto Error;
Packit 9c6abc
    }
Packit 9c6abc
    // Create Huffman bit lengths and codes for each histogram image.
Packit 9c6abc
    histogram_image_size = histogram_image->size;
Packit 9c6abc
    bit_array_size = 5 * histogram_image_size;
Packit 9c6abc
    huffman_codes = (HuffmanTreeCode*)WebPSafeCalloc(bit_array_size,
Packit 9c6abc
                                                     sizeof(*huffman_codes));
Packit 9c6abc
    // Note: some histogram_image entries may point to tmp_histos[], so the
Packit 9c6abc
    // latter need to outlive the following call to GetHuffBitLengthsAndCodes().
Packit 9c6abc
    if (huffman_codes == NULL ||
Packit 9c6abc
        !GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
Packit 9c6abc
      err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
      goto Error;
Packit 9c6abc
    }
Packit 9c6abc
    // Free combined histograms.
Packit 9c6abc
    VP8LFreeHistogramSet(histogram_image);
Packit 9c6abc
    histogram_image = NULL;
Packit 9c6abc
Packit 9c6abc
    // Free scratch histograms.
Packit 9c6abc
    VP8LFreeHistogram(tmp_histo);
Packit 9c6abc
    tmp_histo = NULL;
Packit 9c6abc
Packit 9c6abc
    // Color Cache parameters.
Packit 9c6abc
    if (*cache_bits > 0) {
Packit 9c6abc
      VP8LPutBits(bw, 1, 1);
Packit 9c6abc
      VP8LPutBits(bw, *cache_bits, 4);
Packit 9c6abc
    } else {
Packit 9c6abc
      VP8LPutBits(bw, 0, 1);
Packit 9c6abc
    }
Packit 9c6abc
Packit 9c6abc
    // Huffman image + meta huffman.
Packit 9c6abc
    {
Packit 9c6abc
      const int write_histogram_image = (histogram_image_size > 1);
Packit 9c6abc
      VP8LPutBits(bw, write_histogram_image, 1);
Packit 9c6abc
      if (write_histogram_image) {
Packit 9c6abc
        uint32_t* const histogram_argb =
Packit 9c6abc
            (uint32_t*)WebPSafeMalloc(histogram_image_xysize,
Packit 9c6abc
                                      sizeof(*histogram_argb));
Packit 9c6abc
        int max_index = 0;
Packit 9c6abc
        uint32_t i;
Packit 9c6abc
        if (histogram_argb == NULL) {
Packit 9c6abc
          err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
          goto Error;
Packit 9c6abc
        }
Packit 9c6abc
        for (i = 0; i < histogram_image_xysize; ++i) {
Packit 9c6abc
          const int symbol_index = histogram_symbols[i] & 0xffff;
Packit 9c6abc
          histogram_argb[i] = (symbol_index << 8);
Packit 9c6abc
          if (symbol_index >= max_index) {
Packit 9c6abc
            max_index = symbol_index + 1;
Packit 9c6abc
          }
Packit 9c6abc
        }
Packit 9c6abc
        histogram_image_size = max_index;
Packit 9c6abc
Packit 9c6abc
        VP8LPutBits(bw, histogram_bits - 2, 3);
Packit 9c6abc
        err = EncodeImageNoHuffman(
Packit 9c6abc
            bw, histogram_argb, hash_chain, refs_tmp, &refs_array[2],
Packit 9c6abc
            VP8LSubSampleSize(width, histogram_bits),
Packit 9c6abc
            VP8LSubSampleSize(height, histogram_bits), quality, low_effort);
Packit 9c6abc
        WebPSafeFree(histogram_argb);
Packit 9c6abc
        if (err != VP8_ENC_OK) goto Error;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
Packit 9c6abc
    // Store Huffman codes.
Packit 9c6abc
    {
Packit 9c6abc
      int i;
Packit 9c6abc
      int max_tokens = 0;
Packit 9c6abc
      // Find maximum number of symbols for the huffman tree-set.
Packit 9c6abc
      for (i = 0; i < 5 * histogram_image_size; ++i) {
Packit 9c6abc
        HuffmanTreeCode* const codes = &huffman_codes[i];
Packit 9c6abc
        if (max_tokens < codes->num_symbols) {
Packit 9c6abc
          max_tokens = codes->num_symbols;
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
      tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens));
Packit 9c6abc
      if (tokens == NULL) {
Packit 9c6abc
        err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
        goto Error;
Packit 9c6abc
      }
Packit 9c6abc
      for (i = 0; i < 5 * histogram_image_size; ++i) {
Packit 9c6abc
        HuffmanTreeCode* const codes = &huffman_codes[i];
Packit 9c6abc
        StoreHuffmanCode(bw, huff_tree, tokens, codes);
Packit 9c6abc
        ClearHuffmanTreeIfOnlyOneSymbol(codes);
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    // Store actual literals.
Packit 9c6abc
    hdr_size_tmp = (int)(VP8LBitWriterNumBytes(bw) - init_byte_position);
Packit 9c6abc
    err = StoreImageToBitMask(bw, width, histogram_bits, refs_best,
Packit 9c6abc
                              histogram_symbols, huffman_codes);
Packit 9c6abc
    // Keep track of the smallest image so far.
Packit 9c6abc
    if (lz77s_idx == 0 ||
Packit 9c6abc
        VP8LBitWriterNumBytes(bw) < VP8LBitWriterNumBytes(&bw_best)) {
Packit 9c6abc
      *hdr_size = hdr_size_tmp;
Packit 9c6abc
      *data_size =
Packit 9c6abc
          (int)(VP8LBitWriterNumBytes(bw) - init_byte_position - *hdr_size);
Packit 9c6abc
      VP8LBitWriterSwap(bw, &bw_best);
Packit 9c6abc
    }
Packit 9c6abc
    // Reset the bit writer for the following iteration if any.
Packit 9c6abc
    if (config->lz77s_types_to_try_size_ > 1) VP8LBitWriterReset(&bw_init, bw);
Packit 9c6abc
    WebPSafeFree(tokens);
Packit 9c6abc
    tokens = NULL;
Packit 9c6abc
    if (huffman_codes != NULL) {
Packit 9c6abc
      WebPSafeFree(huffman_codes->codes);
Packit 9c6abc
      WebPSafeFree(huffman_codes);
Packit 9c6abc
      huffman_codes = NULL;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  VP8LBitWriterSwap(bw, &bw_best);
Packit 9c6abc
Packit 9c6abc
 Error:
Packit 9c6abc
  WebPSafeFree(tokens);
Packit 9c6abc
  WebPSafeFree(huff_tree);
Packit 9c6abc
  VP8LFreeHistogramSet(histogram_image);
Packit 9c6abc
  VP8LFreeHistogram(tmp_histo);
Packit 9c6abc
  if (huffman_codes != NULL) {
Packit 9c6abc
    WebPSafeFree(huffman_codes->codes);
Packit 9c6abc
    WebPSafeFree(huffman_codes);
Packit 9c6abc
  }
Packit 9c6abc
  WebPSafeFree(histogram_symbols);
Packit 9c6abc
  VP8LBitWriterWipeOut(&bw_best);
Packit 9c6abc
  return err;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
// Transforms
Packit 9c6abc
Packit 9c6abc
static void ApplySubtractGreen(VP8LEncoder* const enc, int width, int height,
Packit 9c6abc
                               VP8LBitWriter* const bw) {
Packit 9c6abc
  VP8LPutBits(bw, TRANSFORM_PRESENT, 1);
Packit 9c6abc
  VP8LPutBits(bw, SUBTRACT_GREEN, 2);
Packit 9c6abc
  VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc,
Packit 9c6abc
                                            int width, int height,
Packit 9c6abc
                                            int quality, int low_effort,
Packit 9c6abc
                                            int used_subtract_green,
Packit 9c6abc
                                            VP8LBitWriter* const bw) {
Packit 9c6abc
  const int pred_bits = enc->transform_bits_;
Packit 9c6abc
  const int transform_width = VP8LSubSampleSize(width, pred_bits);
Packit 9c6abc
  const int transform_height = VP8LSubSampleSize(height, pred_bits);
Packit 9c6abc
  // we disable near-lossless quantization if palette is used.
Packit 9c6abc
  const int near_lossless_strength = enc->use_palette_ ? 100
Packit 9c6abc
                                   : enc->config_->near_lossless;
Packit 9c6abc
Packit 9c6abc
  VP8LResidualImage(width, height, pred_bits, low_effort, enc->argb_,
Packit 9c6abc
                    enc->argb_scratch_, enc->transform_data_,
Packit 9c6abc
                    near_lossless_strength, enc->config_->exact,
Packit 9c6abc
                    used_subtract_green);
Packit 9c6abc
  VP8LPutBits(bw, TRANSFORM_PRESENT, 1);
Packit 9c6abc
  VP8LPutBits(bw, PREDICTOR_TRANSFORM, 2);
Packit 9c6abc
  assert(pred_bits >= 2);
Packit 9c6abc
  VP8LPutBits(bw, pred_bits - 2, 3);
Packit 9c6abc
  return EncodeImageNoHuffman(
Packit 9c6abc
      bw, enc->transform_data_, (VP8LHashChain*)&enc->hash_chain_,
Packit 9c6abc
      (VP8LBackwardRefs*)&enc->refs_[0],  // cast const away
Packit 9c6abc
      (VP8LBackwardRefs*)&enc->refs_[1], transform_width, transform_height,
Packit 9c6abc
      quality, low_effort);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPEncodingError ApplyCrossColorFilter(const VP8LEncoder* const enc,
Packit 9c6abc
                                               int width, int height,
Packit 9c6abc
                                               int quality, int low_effort,
Packit 9c6abc
                                               VP8LBitWriter* const bw) {
Packit 9c6abc
  const int ccolor_transform_bits = enc->transform_bits_;
Packit 9c6abc
  const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits);
Packit 9c6abc
  const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits);
Packit 9c6abc
Packit 9c6abc
  VP8LColorSpaceTransform(width, height, ccolor_transform_bits, quality,
Packit 9c6abc
                          enc->argb_, enc->transform_data_);
Packit 9c6abc
  VP8LPutBits(bw, TRANSFORM_PRESENT, 1);
Packit 9c6abc
  VP8LPutBits(bw, CROSS_COLOR_TRANSFORM, 2);
Packit 9c6abc
  assert(ccolor_transform_bits >= 2);
Packit 9c6abc
  VP8LPutBits(bw, ccolor_transform_bits - 2, 3);
Packit 9c6abc
  return EncodeImageNoHuffman(
Packit 9c6abc
      bw, enc->transform_data_, (VP8LHashChain*)&enc->hash_chain_,
Packit 9c6abc
      (VP8LBackwardRefs*)&enc->refs_[0],  // cast const away
Packit 9c6abc
      (VP8LBackwardRefs*)&enc->refs_[1], transform_width, transform_height,
Packit 9c6abc
      quality, low_effort);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
Packit 9c6abc
static WebPEncodingError WriteRiffHeader(const WebPPicture* const pic,
Packit 9c6abc
                                         size_t riff_size, size_t vp8l_size) {
Packit 9c6abc
  uint8_t riff[RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8L_SIGNATURE_SIZE] = {
Packit 9c6abc
    'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P',
Packit 9c6abc
    'V', 'P', '8', 'L', 0, 0, 0, 0, VP8L_MAGIC_BYTE,
Packit 9c6abc
  };
Packit 9c6abc
  PutLE32(riff + TAG_SIZE, (uint32_t)riff_size);
Packit 9c6abc
  PutLE32(riff + RIFF_HEADER_SIZE + TAG_SIZE, (uint32_t)vp8l_size);
Packit 9c6abc
  if (!pic->writer(riff, sizeof(riff), pic)) {
Packit 9c6abc
    return VP8_ENC_ERROR_BAD_WRITE;
Packit 9c6abc
  }
Packit 9c6abc
  return VP8_ENC_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int WriteImageSize(const WebPPicture* const pic,
Packit 9c6abc
                          VP8LBitWriter* const bw) {
Packit 9c6abc
  const int width = pic->width - 1;
Packit 9c6abc
  const int height = pic->height - 1;
Packit 9c6abc
  assert(width < WEBP_MAX_DIMENSION && height < WEBP_MAX_DIMENSION);
Packit 9c6abc
Packit 9c6abc
  VP8LPutBits(bw, width, VP8L_IMAGE_SIZE_BITS);
Packit 9c6abc
  VP8LPutBits(bw, height, VP8L_IMAGE_SIZE_BITS);
Packit 9c6abc
  return !bw->error_;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int WriteRealAlphaAndVersion(VP8LBitWriter* const bw, int has_alpha) {
Packit 9c6abc
  VP8LPutBits(bw, has_alpha, 1);
Packit 9c6abc
  VP8LPutBits(bw, VP8L_VERSION, VP8L_VERSION_BITS);
Packit 9c6abc
  return !bw->error_;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPEncodingError WriteImage(const WebPPicture* const pic,
Packit 9c6abc
                                    VP8LBitWriter* const bw,
Packit 9c6abc
                                    size_t* const coded_size) {
Packit 9c6abc
  WebPEncodingError err = VP8_ENC_OK;
Packit 9c6abc
  const uint8_t* const webpll_data = VP8LBitWriterFinish(bw);
Packit 9c6abc
  const size_t webpll_size = VP8LBitWriterNumBytes(bw);
Packit 9c6abc
  const size_t vp8l_size = VP8L_SIGNATURE_SIZE + webpll_size;
Packit 9c6abc
  const size_t pad = vp8l_size & 1;
Packit 9c6abc
  const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad;
Packit 9c6abc
Packit 9c6abc
  err = WriteRiffHeader(pic, riff_size, vp8l_size);
Packit 9c6abc
  if (err != VP8_ENC_OK) goto Error;
Packit 9c6abc
Packit 9c6abc
  if (!pic->writer(webpll_data, webpll_size, pic)) {
Packit 9c6abc
    err = VP8_ENC_ERROR_BAD_WRITE;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (pad) {
Packit 9c6abc
    const uint8_t pad_byte[1] = { 0 };
Packit 9c6abc
    if (!pic->writer(pad_byte, 1, pic)) {
Packit 9c6abc
      err = VP8_ENC_ERROR_BAD_WRITE;
Packit 9c6abc
      goto Error;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  *coded_size = CHUNK_HEADER_SIZE + riff_size;
Packit 9c6abc
  return VP8_ENC_OK;
Packit 9c6abc
Packit 9c6abc
 Error:
Packit 9c6abc
  return err;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
Packit 9c6abc
static void ClearTransformBuffer(VP8LEncoder* const enc) {
Packit 9c6abc
  WebPSafeFree(enc->transform_mem_);
Packit 9c6abc
  enc->transform_mem_ = NULL;
Packit 9c6abc
  enc->transform_mem_size_ = 0;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Allocates the memory for argb (W x H) buffer, 2 rows of context for
Packit 9c6abc
// prediction and transform data.
Packit 9c6abc
// Flags influencing the memory allocated:
Packit 9c6abc
//  enc->transform_bits_
Packit 9c6abc
//  enc->use_predict_, enc->use_cross_color_
Packit 9c6abc
static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc,
Packit 9c6abc
                                                 int width, int height) {
Packit 9c6abc
  WebPEncodingError err = VP8_ENC_OK;
Packit 9c6abc
  const uint64_t image_size = width * height;
Packit 9c6abc
  // VP8LResidualImage needs room for 2 scanlines of uint32 pixels with an extra
Packit 9c6abc
  // pixel in each, plus 2 regular scanlines of bytes.
Packit 9c6abc
  // TODO(skal): Clean up by using arithmetic in bytes instead of words.
Packit 9c6abc
  const uint64_t argb_scratch_size =
Packit 9c6abc
      enc->use_predict_
Packit 9c6abc
          ? (width + 1) * 2 +
Packit 9c6abc
            (width * 2 + sizeof(uint32_t) - 1) / sizeof(uint32_t)
Packit 9c6abc
          : 0;
Packit 9c6abc
  const uint64_t transform_data_size =
Packit 9c6abc
      (enc->use_predict_ || enc->use_cross_color_)
Packit 9c6abc
          ? VP8LSubSampleSize(width, enc->transform_bits_) *
Packit 9c6abc
                VP8LSubSampleSize(height, enc->transform_bits_)
Packit 9c6abc
          : 0;
Packit 9c6abc
  const uint64_t max_alignment_in_words =
Packit 9c6abc
      (WEBP_ALIGN_CST + sizeof(uint32_t) - 1) / sizeof(uint32_t);
Packit 9c6abc
  const uint64_t mem_size =
Packit 9c6abc
      image_size + max_alignment_in_words +
Packit 9c6abc
      argb_scratch_size + max_alignment_in_words +
Packit 9c6abc
      transform_data_size;
Packit 9c6abc
  uint32_t* mem = enc->transform_mem_;
Packit 9c6abc
  if (mem == NULL || mem_size > enc->transform_mem_size_) {
Packit 9c6abc
    ClearTransformBuffer(enc);
Packit 9c6abc
    mem = (uint32_t*)WebPSafeMalloc(mem_size, sizeof(*mem));
Packit 9c6abc
    if (mem == NULL) {
Packit 9c6abc
      err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
      goto Error;
Packit 9c6abc
    }
Packit 9c6abc
    enc->transform_mem_ = mem;
Packit 9c6abc
    enc->transform_mem_size_ = (size_t)mem_size;
Packit 9c6abc
    enc->argb_content_ = kEncoderNone;
Packit 9c6abc
  }
Packit 9c6abc
  enc->argb_ = mem;
Packit 9c6abc
  mem = (uint32_t*)WEBP_ALIGN(mem + image_size);
Packit 9c6abc
  enc->argb_scratch_ = mem;
Packit 9c6abc
  mem = (uint32_t*)WEBP_ALIGN(mem + argb_scratch_size);
Packit 9c6abc
  enc->transform_data_ = mem;
Packit 9c6abc
Packit 9c6abc
  enc->current_width_ = width;
Packit 9c6abc
 Error:
Packit 9c6abc
  return err;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) {
Packit 9c6abc
  WebPEncodingError err = VP8_ENC_OK;
Packit 9c6abc
  const WebPPicture* const picture = enc->pic_;
Packit 9c6abc
  const int width = picture->width;
Packit 9c6abc
  const int height = picture->height;
Packit 9c6abc
  int y;
Packit 9c6abc
  err = AllocateTransformBuffer(enc, width, height);
Packit 9c6abc
  if (err != VP8_ENC_OK) return err;
Packit 9c6abc
  if (enc->argb_content_ == kEncoderARGB) return VP8_ENC_OK;
Packit 9c6abc
  for (y = 0; y < height; ++y) {
Packit 9c6abc
    memcpy(enc->argb_ + y * width,
Packit 9c6abc
           picture->argb + y * picture->argb_stride,
Packit 9c6abc
           width * sizeof(*enc->argb_));
Packit 9c6abc
  }
Packit 9c6abc
  enc->argb_content_ = kEncoderARGB;
Packit 9c6abc
  assert(enc->current_width_ == width);
Packit 9c6abc
  return VP8_ENC_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
Packit 9c6abc
static WEBP_INLINE int SearchColorNoIdx(const uint32_t sorted[], uint32_t color,
Packit 9c6abc
                                        int hi) {
Packit 9c6abc
  int low = 0;
Packit 9c6abc
  if (sorted[low] == color) return low;  // loop invariant: sorted[low] != color
Packit 9c6abc
  while (1) {
Packit 9c6abc
    const int mid = (low + hi) >> 1;
Packit 9c6abc
    if (sorted[mid] == color) {
Packit 9c6abc
      return mid;
Packit 9c6abc
    } else if (sorted[mid] < color) {
Packit 9c6abc
      low = mid;
Packit 9c6abc
    } else {
Packit 9c6abc
      hi = mid;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
#define APPLY_PALETTE_GREEDY_MAX 4
Packit 9c6abc
Packit 9c6abc
static WEBP_INLINE uint32_t SearchColorGreedy(const uint32_t palette[],
Packit 9c6abc
                                              int palette_size,
Packit 9c6abc
                                              uint32_t color) {
Packit 9c6abc
  (void)palette_size;
Packit 9c6abc
  assert(palette_size < APPLY_PALETTE_GREEDY_MAX);
Packit 9c6abc
  assert(3 == APPLY_PALETTE_GREEDY_MAX - 1);
Packit 9c6abc
  if (color == palette[0]) return 0;
Packit 9c6abc
  if (color == palette[1]) return 1;
Packit 9c6abc
  if (color == palette[2]) return 2;
Packit 9c6abc
  return 3;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WEBP_INLINE uint32_t ApplyPaletteHash0(uint32_t color) {
Packit 9c6abc
  // Focus on the green color.
Packit 9c6abc
  return (color >> 8) & 0xff;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
#define PALETTE_INV_SIZE_BITS 11
Packit 9c6abc
#define PALETTE_INV_SIZE (1 << PALETTE_INV_SIZE_BITS)
Packit 9c6abc
Packit 9c6abc
static WEBP_INLINE uint32_t ApplyPaletteHash1(uint32_t color) {
Packit 9c6abc
  // Forget about alpha.
Packit 9c6abc
  return ((uint32_t)((color & 0x00ffffffu) * 4222244071ull)) >>
Packit 9c6abc
         (32 - PALETTE_INV_SIZE_BITS);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WEBP_INLINE uint32_t ApplyPaletteHash2(uint32_t color) {
Packit 9c6abc
  // Forget about alpha.
Packit 9c6abc
  return ((uint32_t)((color & 0x00ffffffu) * ((1ull << 31) - 1))) >>
Packit 9c6abc
         (32 - PALETTE_INV_SIZE_BITS);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Sort palette in increasing order and prepare an inverse mapping array.
Packit 9c6abc
static void PrepareMapToPalette(const uint32_t palette[], int num_colors,
Packit 9c6abc
                                uint32_t sorted[], uint32_t idx_map[]) {
Packit 9c6abc
  int i;
Packit 9c6abc
  memcpy(sorted, palette, num_colors * sizeof(*sorted));
Packit 9c6abc
  qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort);
Packit 9c6abc
  for (i = 0; i < num_colors; ++i) {
Packit 9c6abc
    idx_map[SearchColorNoIdx(sorted, palette[i], num_colors)] = i;
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Use 1 pixel cache for ARGB pixels.
Packit 9c6abc
#define APPLY_PALETTE_FOR(COLOR_INDEX) do {         \
Packit 9c6abc
  uint32_t prev_pix = palette[0];                   \
Packit 9c6abc
  uint32_t prev_idx = 0;                            \
Packit 9c6abc
  for (y = 0; y < height; ++y) {                    \
Packit 9c6abc
    for (x = 0; x < width; ++x) {                   \
Packit 9c6abc
      const uint32_t pix = src[x];                  \
Packit 9c6abc
      if (pix != prev_pix) {                        \
Packit 9c6abc
        prev_idx = COLOR_INDEX;                     \
Packit 9c6abc
        prev_pix = pix;                             \
Packit 9c6abc
      }                                             \
Packit 9c6abc
      tmp_row[x] = prev_idx;                        \
Packit 9c6abc
    }                                               \
Packit 9c6abc
    VP8LBundleColorMap(tmp_row, width, xbits, dst); \
Packit 9c6abc
    src += src_stride;                              \
Packit 9c6abc
    dst += dst_stride;                              \
Packit 9c6abc
  }                                                 \
Packit 9c6abc
} while (0)
Packit 9c6abc
Packit 9c6abc
// Remap argb values in src[] to packed palettes entries in dst[]
Packit 9c6abc
// using 'row' as a temporary buffer of size 'width'.
Packit 9c6abc
// We assume that all src[] values have a corresponding entry in the palette.
Packit 9c6abc
// Note: src[] can be the same as dst[]
Packit 9c6abc
static WebPEncodingError ApplyPalette(const uint32_t* src, uint32_t src_stride,
Packit 9c6abc
                                      uint32_t* dst, uint32_t dst_stride,
Packit 9c6abc
                                      const uint32_t* palette, int palette_size,
Packit 9c6abc
                                      int width, int height, int xbits) {
Packit 9c6abc
  // TODO(skal): this tmp buffer is not needed if VP8LBundleColorMap() can be
Packit 9c6abc
  // made to work in-place.
Packit 9c6abc
  uint8_t* const tmp_row = (uint8_t*)WebPSafeMalloc(width, sizeof(*tmp_row));
Packit 9c6abc
  int x, y;
Packit 9c6abc
Packit 9c6abc
  if (tmp_row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
Packit 9c6abc
  if (palette_size < APPLY_PALETTE_GREEDY_MAX) {
Packit 9c6abc
    APPLY_PALETTE_FOR(SearchColorGreedy(palette, palette_size, pix));
Packit 9c6abc
  } else {
Packit 9c6abc
    int i, j;
Packit 9c6abc
    uint16_t buffer[PALETTE_INV_SIZE];
Packit 9c6abc
    uint32_t (*const hash_functions[])(uint32_t) = {
Packit 9c6abc
        ApplyPaletteHash0, ApplyPaletteHash1, ApplyPaletteHash2
Packit 9c6abc
    };
Packit 9c6abc
Packit 9c6abc
    // Try to find a perfect hash function able to go from a color to an index
Packit 9c6abc
    // within 1 << PALETTE_INV_SIZE_BITS in order to build a hash map to go
Packit 9c6abc
    // from color to index in palette.
Packit 9c6abc
    for (i = 0; i < 3; ++i) {
Packit 9c6abc
      int use_LUT = 1;
Packit 9c6abc
      // Set each element in buffer to max uint16_t.
Packit 9c6abc
      memset(buffer, 0xff, sizeof(buffer));
Packit 9c6abc
      for (j = 0; j < palette_size; ++j) {
Packit 9c6abc
        const uint32_t ind = hash_functions[i](palette[j]);
Packit 9c6abc
        if (buffer[ind] != 0xffffu) {
Packit 9c6abc
          use_LUT = 0;
Packit 9c6abc
          break;
Packit 9c6abc
        } else {
Packit 9c6abc
          buffer[ind] = j;
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
      if (use_LUT) break;
Packit 9c6abc
    }
Packit 9c6abc
Packit 9c6abc
    if (i == 0) {
Packit 9c6abc
      APPLY_PALETTE_FOR(buffer[ApplyPaletteHash0(pix)]);
Packit 9c6abc
    } else if (i == 1) {
Packit 9c6abc
      APPLY_PALETTE_FOR(buffer[ApplyPaletteHash1(pix)]);
Packit 9c6abc
    } else if (i == 2) {
Packit 9c6abc
      APPLY_PALETTE_FOR(buffer[ApplyPaletteHash2(pix)]);
Packit 9c6abc
    } else {
Packit 9c6abc
      uint32_t idx_map[MAX_PALETTE_SIZE];
Packit 9c6abc
      uint32_t palette_sorted[MAX_PALETTE_SIZE];
Packit 9c6abc
      PrepareMapToPalette(palette, palette_size, palette_sorted, idx_map);
Packit 9c6abc
      APPLY_PALETTE_FOR(
Packit 9c6abc
          idx_map[SearchColorNoIdx(palette_sorted, pix, palette_size)]);
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  WebPSafeFree(tmp_row);
Packit 9c6abc
  return VP8_ENC_OK;
Packit 9c6abc
}
Packit 9c6abc
#undef APPLY_PALETTE_FOR
Packit 9c6abc
#undef PALETTE_INV_SIZE_BITS
Packit 9c6abc
#undef PALETTE_INV_SIZE
Packit 9c6abc
#undef APPLY_PALETTE_GREEDY_MAX
Packit 9c6abc
Packit 9c6abc
// Note: Expects "enc->palette_" to be set properly.
Packit 9c6abc
static WebPEncodingError MapImageFromPalette(VP8LEncoder* const enc,
Packit 9c6abc
                                             int in_place) {
Packit 9c6abc
  WebPEncodingError err = VP8_ENC_OK;
Packit 9c6abc
  const WebPPicture* const pic = enc->pic_;
Packit 9c6abc
  const int width = pic->width;
Packit 9c6abc
  const int height = pic->height;
Packit 9c6abc
  const uint32_t* const palette = enc->palette_;
Packit 9c6abc
  const uint32_t* src = in_place ? enc->argb_ : pic->argb;
Packit 9c6abc
  const int src_stride = in_place ? enc->current_width_ : pic->argb_stride;
Packit 9c6abc
  const int palette_size = enc->palette_size_;
Packit 9c6abc
  int xbits;
Packit 9c6abc
Packit 9c6abc
  // Replace each input pixel by corresponding palette index.
Packit 9c6abc
  // This is done line by line.
Packit 9c6abc
  if (palette_size <= 4) {
Packit 9c6abc
    xbits = (palette_size <= 2) ? 3 : 2;
Packit 9c6abc
  } else {
Packit 9c6abc
    xbits = (palette_size <= 16) ? 1 : 0;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height);
Packit 9c6abc
  if (err != VP8_ENC_OK) return err;
Packit 9c6abc
Packit 9c6abc
  err = ApplyPalette(src, src_stride,
Packit 9c6abc
                     enc->argb_, enc->current_width_,
Packit 9c6abc
                     palette, palette_size, width, height, xbits);
Packit 9c6abc
  enc->argb_content_ = kEncoderPalette;
Packit 9c6abc
  return err;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Save palette_[] to bitstream.
Packit 9c6abc
static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, int low_effort,
Packit 9c6abc
                                       VP8LEncoder* const enc) {
Packit 9c6abc
  int i;
Packit 9c6abc
  uint32_t tmp_palette[MAX_PALETTE_SIZE];
Packit 9c6abc
  const int palette_size = enc->palette_size_;
Packit 9c6abc
  const uint32_t* const palette = enc->palette_;
Packit 9c6abc
  VP8LPutBits(bw, TRANSFORM_PRESENT, 1);
Packit 9c6abc
  VP8LPutBits(bw, COLOR_INDEXING_TRANSFORM, 2);
Packit 9c6abc
  assert(palette_size >= 1 && palette_size <= MAX_PALETTE_SIZE);
Packit 9c6abc
  VP8LPutBits(bw, palette_size - 1, 8);
Packit 9c6abc
  for (i = palette_size - 1; i >= 1; --i) {
Packit 9c6abc
    tmp_palette[i] = VP8LSubPixels(palette[i], palette[i - 1]);
Packit 9c6abc
  }
Packit 9c6abc
  tmp_palette[0] = palette[0];
Packit 9c6abc
  return EncodeImageNoHuffman(bw, tmp_palette, &enc->hash_chain_,
Packit 9c6abc
                              &enc->refs_[0], &enc->refs_[1], palette_size, 1,
Packit 9c6abc
                              20 /* quality */, low_effort);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
// VP8LEncoder
Packit 9c6abc
Packit 9c6abc
static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config,
Packit 9c6abc
                                   const WebPPicture* const picture) {
Packit 9c6abc
  VP8LEncoder* const enc = (VP8LEncoder*)WebPSafeCalloc(1ULL, sizeof(*enc));
Packit 9c6abc
  if (enc == NULL) {
Packit 9c6abc
    WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
Packit 9c6abc
    return NULL;
Packit 9c6abc
  }
Packit 9c6abc
  enc->config_ = config;
Packit 9c6abc
  enc->pic_ = picture;
Packit 9c6abc
  enc->argb_content_ = kEncoderNone;
Packit 9c6abc
Packit 9c6abc
  VP8LEncDspInit();
Packit 9c6abc
Packit 9c6abc
  return enc;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void VP8LEncoderDelete(VP8LEncoder* enc) {
Packit 9c6abc
  if (enc != NULL) {
Packit 9c6abc
    int i;
Packit 9c6abc
    VP8LHashChainClear(&enc->hash_chain_);
Packit 9c6abc
    for (i = 0; i < 3; ++i) VP8LBackwardRefsClear(&enc->refs_[i]);
Packit 9c6abc
    ClearTransformBuffer(enc);
Packit 9c6abc
    WebPSafeFree(enc);
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
// Main call
Packit 9c6abc
Packit 9c6abc
typedef struct {
Packit 9c6abc
  const WebPConfig* config_;
Packit 9c6abc
  const WebPPicture* picture_;
Packit 9c6abc
  VP8LBitWriter* bw_;
Packit 9c6abc
  VP8LEncoder* enc_;
Packit 9c6abc
  int use_cache_;
Packit 9c6abc
  CrunchConfig crunch_configs_[CRUNCH_CONFIGS_MAX];
Packit 9c6abc
  int num_crunch_configs_;
Packit 9c6abc
  int red_and_blue_always_zero_;
Packit 9c6abc
  WebPEncodingError err_;
Packit 9c6abc
  WebPAuxStats* stats_;
Packit 9c6abc
} StreamEncodeContext;
Packit 9c6abc
Packit 9c6abc
static int EncodeStreamHook(void* input, void* data2) {
Packit 9c6abc
  StreamEncodeContext* const params = (StreamEncodeContext*)input;
Packit 9c6abc
  const WebPConfig* const config = params->config_;
Packit 9c6abc
  const WebPPicture* const picture = params->picture_;
Packit 9c6abc
  VP8LBitWriter* const bw = params->bw_;
Packit 9c6abc
  VP8LEncoder* const enc = params->enc_;
Packit 9c6abc
  const int use_cache = params->use_cache_;
Packit 9c6abc
  const CrunchConfig* const crunch_configs = params->crunch_configs_;
Packit 9c6abc
  const int num_crunch_configs = params->num_crunch_configs_;
Packit 9c6abc
  const int red_and_blue_always_zero = params->red_and_blue_always_zero_;
Packit 9c6abc
#if !defined(WEBP_DISABLE_STATS)
Packit 9c6abc
  WebPAuxStats* const stats = params->stats_;
Packit 9c6abc
#endif
Packit 9c6abc
  WebPEncodingError err = VP8_ENC_OK;
Packit 9c6abc
  const int quality = (int)config->quality;
Packit 9c6abc
  const int low_effort = (config->method == 0);
Packit 9c6abc
#if (WEBP_NEAR_LOSSLESS == 1)
Packit 9c6abc
  const int width = picture->width;
Packit 9c6abc
#endif
Packit 9c6abc
  const int height = picture->height;
Packit 9c6abc
  const size_t byte_position = VP8LBitWriterNumBytes(bw);
Packit 9c6abc
#if (WEBP_NEAR_LOSSLESS == 1)
Packit 9c6abc
  int use_near_lossless = 0;
Packit 9c6abc
#endif
Packit 9c6abc
  int hdr_size = 0;
Packit 9c6abc
  int data_size = 0;
Packit 9c6abc
  int use_delta_palette = 0;
Packit 9c6abc
  int idx;
Packit 9c6abc
  size_t best_size = 0;
Packit 9c6abc
  VP8LBitWriter bw_init = *bw, bw_best;
Packit 9c6abc
  (void)data2;
Packit 9c6abc
Packit 9c6abc
  if (!VP8LBitWriterInit(&bw_best, 0) ||
Packit 9c6abc
      (num_crunch_configs > 1 && !VP8LBitWriterClone(bw, &bw_best))) {
Packit 9c6abc
    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  for (idx = 0; idx < num_crunch_configs; ++idx) {
Packit 9c6abc
    const int entropy_idx = crunch_configs[idx].entropy_idx_;
Packit 9c6abc
    enc->use_palette_ = (entropy_idx == kPalette);
Packit 9c6abc
    enc->use_subtract_green_ =
Packit 9c6abc
        (entropy_idx == kSubGreen) || (entropy_idx == kSpatialSubGreen);
Packit 9c6abc
    enc->use_predict_ =
Packit 9c6abc
        (entropy_idx == kSpatial) || (entropy_idx == kSpatialSubGreen);
Packit 9c6abc
    if (low_effort) {
Packit 9c6abc
      enc->use_cross_color_ = 0;
Packit 9c6abc
    } else {
Packit 9c6abc
      enc->use_cross_color_ = red_and_blue_always_zero ? 0 : enc->use_predict_;
Packit 9c6abc
    }
Packit 9c6abc
    // Reset any parameter in the encoder that is set in the previous iteration.
Packit 9c6abc
    enc->cache_bits_ = 0;
Packit 9c6abc
    VP8LBackwardRefsClear(&enc->refs_[0]);
Packit 9c6abc
    VP8LBackwardRefsClear(&enc->refs_[1]);
Packit 9c6abc
Packit 9c6abc
#if (WEBP_NEAR_LOSSLESS == 1)
Packit 9c6abc
    // Apply near-lossless preprocessing.
Packit 9c6abc
    use_near_lossless = (config->near_lossless < 100) && !enc->use_palette_ &&
Packit 9c6abc
                        !enc->use_predict_;
Packit 9c6abc
    if (use_near_lossless) {
Packit 9c6abc
      err = AllocateTransformBuffer(enc, width, height);
Packit 9c6abc
      if (err != VP8_ENC_OK) goto Error;
Packit 9c6abc
      if ((enc->argb_content_ != kEncoderNearLossless) &&
Packit 9c6abc
          !VP8ApplyNearLossless(picture, config->near_lossless, enc->argb_)) {
Packit 9c6abc
        err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
        goto Error;
Packit 9c6abc
      }
Packit 9c6abc
      enc->argb_content_ = kEncoderNearLossless;
Packit 9c6abc
    } else {
Packit 9c6abc
      enc->argb_content_ = kEncoderNone;
Packit 9c6abc
    }
Packit 9c6abc
#else
Packit 9c6abc
    enc->argb_content_ = kEncoderNone;
Packit 9c6abc
#endif
Packit 9c6abc
Packit 9c6abc
    // Encode palette
Packit 9c6abc
    if (enc->use_palette_) {
Packit 9c6abc
      err = EncodePalette(bw, low_effort, enc);
Packit 9c6abc
      if (err != VP8_ENC_OK) goto Error;
Packit 9c6abc
      err = MapImageFromPalette(enc, use_delta_palette);
Packit 9c6abc
      if (err != VP8_ENC_OK) goto Error;
Packit 9c6abc
      // If using a color cache, do not have it bigger than the number of
Packit 9c6abc
      // colors.
Packit 9c6abc
      if (use_cache && enc->palette_size_ < (1 << MAX_COLOR_CACHE_BITS)) {
Packit 9c6abc
        enc->cache_bits_ = BitsLog2Floor(enc->palette_size_) + 1;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    if (!use_delta_palette) {
Packit 9c6abc
      // In case image is not packed.
Packit 9c6abc
      if (enc->argb_content_ != kEncoderNearLossless &&
Packit 9c6abc
          enc->argb_content_ != kEncoderPalette) {
Packit 9c6abc
        err = MakeInputImageCopy(enc);
Packit 9c6abc
        if (err != VP8_ENC_OK) goto Error;
Packit 9c6abc
      }
Packit 9c6abc
Packit 9c6abc
      // -----------------------------------------------------------------------
Packit 9c6abc
      // Apply transforms and write transform data.
Packit 9c6abc
Packit 9c6abc
      if (enc->use_subtract_green_) {
Packit 9c6abc
        ApplySubtractGreen(enc, enc->current_width_, height, bw);
Packit 9c6abc
      }
Packit 9c6abc
Packit 9c6abc
      if (enc->use_predict_) {
Packit 9c6abc
        err = ApplyPredictFilter(enc, enc->current_width_, height, quality,
Packit 9c6abc
                                 low_effort, enc->use_subtract_green_, bw);
Packit 9c6abc
        if (err != VP8_ENC_OK) goto Error;
Packit 9c6abc
      }
Packit 9c6abc
Packit 9c6abc
      if (enc->use_cross_color_) {
Packit 9c6abc
        err = ApplyCrossColorFilter(enc, enc->current_width_, height, quality,
Packit 9c6abc
                                    low_effort, bw);
Packit 9c6abc
        if (err != VP8_ENC_OK) goto Error;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
Packit 9c6abc
    VP8LPutBits(bw, !TRANSFORM_PRESENT, 1);  // No more transforms.
Packit 9c6abc
Packit 9c6abc
    // -------------------------------------------------------------------------
Packit 9c6abc
    // Encode and write the transformed image.
Packit 9c6abc
    err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_,
Packit 9c6abc
                              enc->current_width_, height, quality, low_effort,
Packit 9c6abc
                              use_cache, &crunch_configs[idx],
Packit 9c6abc
                              &enc->cache_bits_, enc->histo_bits_,
Packit 9c6abc
                              byte_position, &hdr_size, &data_size);
Packit 9c6abc
    if (err != VP8_ENC_OK) goto Error;
Packit 9c6abc
Packit 9c6abc
    // If we are better than what we already have.
Packit 9c6abc
    if (idx == 0 || VP8LBitWriterNumBytes(bw) < best_size) {
Packit 9c6abc
      best_size = VP8LBitWriterNumBytes(bw);
Packit 9c6abc
      // Store the BitWriter.
Packit 9c6abc
      VP8LBitWriterSwap(bw, &bw_best);
Packit 9c6abc
#if !defined(WEBP_DISABLE_STATS)
Packit 9c6abc
      // Update the stats.
Packit 9c6abc
      if (stats != NULL) {
Packit 9c6abc
        stats->lossless_features = 0;
Packit 9c6abc
        if (enc->use_predict_) stats->lossless_features |= 1;
Packit 9c6abc
        if (enc->use_cross_color_) stats->lossless_features |= 2;
Packit 9c6abc
        if (enc->use_subtract_green_) stats->lossless_features |= 4;
Packit 9c6abc
        if (enc->use_palette_) stats->lossless_features |= 8;
Packit 9c6abc
        stats->histogram_bits = enc->histo_bits_;
Packit 9c6abc
        stats->transform_bits = enc->transform_bits_;
Packit 9c6abc
        stats->cache_bits = enc->cache_bits_;
Packit 9c6abc
        stats->palette_size = enc->palette_size_;
Packit 9c6abc
        stats->lossless_size = (int)(best_size - byte_position);
Packit 9c6abc
        stats->lossless_hdr_size = hdr_size;
Packit 9c6abc
        stats->lossless_data_size = data_size;
Packit 9c6abc
      }
Packit 9c6abc
#endif
Packit 9c6abc
    }
Packit 9c6abc
    // Reset the bit writer for the following iteration if any.
Packit 9c6abc
    if (num_crunch_configs > 1) VP8LBitWriterReset(&bw_init, bw);
Packit 9c6abc
  }
Packit 9c6abc
  VP8LBitWriterSwap(&bw_best, bw);
Packit 9c6abc
Packit 9c6abc
Error:
Packit 9c6abc
  VP8LBitWriterWipeOut(&bw_best);
Packit 9c6abc
  params->err_ = err;
Packit 9c6abc
  // The hook should return false in case of error.
Packit 9c6abc
  return (err == VP8_ENC_OK);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
Packit 9c6abc
                                   const WebPPicture* const picture,
Packit 9c6abc
                                   VP8LBitWriter* const bw_main,
Packit 9c6abc
                                   int use_cache) {
Packit 9c6abc
  WebPEncodingError err = VP8_ENC_OK;
Packit 9c6abc
  VP8LEncoder* const enc_main = VP8LEncoderNew(config, picture);
Packit 9c6abc
  VP8LEncoder* enc_side = NULL;
Packit 9c6abc
  CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX];
Packit 9c6abc
  int num_crunch_configs_main, num_crunch_configs_side = 0;
Packit 9c6abc
  int idx;
Packit 9c6abc
  int red_and_blue_always_zero = 0;
Packit 9c6abc
  WebPWorker worker_main, worker_side;
Packit 9c6abc
  StreamEncodeContext params_main, params_side;
Packit 9c6abc
  // The main thread uses picture->stats, the side thread uses stats_side.
Packit 9c6abc
  WebPAuxStats stats_side;
Packit 9c6abc
  VP8LBitWriter bw_side;
Packit 9c6abc
  const WebPWorkerInterface* const worker_interface = WebPGetWorkerInterface();
Packit 9c6abc
  int ok_main;
Packit 9c6abc
Packit 9c6abc
  // Analyze image (entropy, num_palettes etc)
Packit 9c6abc
  if (enc_main == NULL ||
Packit 9c6abc
      !EncoderAnalyze(enc_main, crunch_configs, &num_crunch_configs_main,
Packit 9c6abc
                      &red_and_blue_always_zero) ||
Packit 9c6abc
      !EncoderInit(enc_main) || !VP8LBitWriterInit(&bw_side, 0)) {
Packit 9c6abc
    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Split the configs between the main and side threads (if any).
Packit 9c6abc
  if (config->thread_level > 0) {
Packit 9c6abc
    num_crunch_configs_side = num_crunch_configs_main / 2;
Packit 9c6abc
    for (idx = 0; idx < num_crunch_configs_side; ++idx) {
Packit 9c6abc
      params_side.crunch_configs_[idx] =
Packit 9c6abc
          crunch_configs[num_crunch_configs_main - num_crunch_configs_side +
Packit 9c6abc
                         idx];
Packit 9c6abc
    }
Packit 9c6abc
    params_side.num_crunch_configs_ = num_crunch_configs_side;
Packit 9c6abc
  }
Packit 9c6abc
  num_crunch_configs_main -= num_crunch_configs_side;
Packit 9c6abc
  for (idx = 0; idx < num_crunch_configs_main; ++idx) {
Packit 9c6abc
    params_main.crunch_configs_[idx] = crunch_configs[idx];
Packit 9c6abc
  }
Packit 9c6abc
  params_main.num_crunch_configs_ = num_crunch_configs_main;
Packit 9c6abc
Packit 9c6abc
  // Fill in the parameters for the thread workers.
Packit 9c6abc
  {
Packit 9c6abc
    const int params_size = (num_crunch_configs_side > 0) ? 2 : 1;
Packit 9c6abc
    for (idx = 0; idx < params_size; ++idx) {
Packit 9c6abc
      // Create the parameters for each worker.
Packit 9c6abc
      WebPWorker* const worker = (idx == 0) ? &worker_main : &worker_side;
Packit 9c6abc
      StreamEncodeContext* const param =
Packit 9c6abc
          (idx == 0) ? &params_main : &params_side;
Packit 9c6abc
      param->config_ = config;
Packit 9c6abc
      param->picture_ = picture;
Packit 9c6abc
      param->use_cache_ = use_cache;
Packit 9c6abc
      param->red_and_blue_always_zero_ = red_and_blue_always_zero;
Packit 9c6abc
      if (idx == 0) {
Packit 9c6abc
        param->stats_ = picture->stats;
Packit 9c6abc
        param->bw_ = bw_main;
Packit 9c6abc
        param->enc_ = enc_main;
Packit 9c6abc
      } else {
Packit 9c6abc
        param->stats_ = (picture->stats == NULL) ? NULL : &stats_side;
Packit 9c6abc
        // Create a side bit writer.
Packit 9c6abc
        if (!VP8LBitWriterClone(bw_main, &bw_side)) {
Packit 9c6abc
          err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
          goto Error;
Packit 9c6abc
        }
Packit 9c6abc
        param->bw_ = &bw_side;
Packit 9c6abc
        // Create a side encoder.
Packit 9c6abc
        enc_side = VP8LEncoderNew(config, picture);
Packit 9c6abc
        if (enc_side == NULL || !EncoderInit(enc_side)) {
Packit 9c6abc
          err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
          goto Error;
Packit 9c6abc
        }
Packit 9c6abc
        // Copy the values that were computed for the main encoder.
Packit 9c6abc
        enc_side->histo_bits_ = enc_main->histo_bits_;
Packit 9c6abc
        enc_side->transform_bits_ = enc_main->transform_bits_;
Packit 9c6abc
        enc_side->palette_size_ = enc_main->palette_size_;
Packit 9c6abc
        memcpy(enc_side->palette_, enc_main->palette_,
Packit 9c6abc
               sizeof(enc_main->palette_));
Packit 9c6abc
        param->enc_ = enc_side;
Packit 9c6abc
      }
Packit 9c6abc
      // Create the workers.
Packit 9c6abc
      worker_interface->Init(worker);
Packit 9c6abc
      worker->data1 = param;
Packit 9c6abc
      worker->data2 = NULL;
Packit 9c6abc
      worker->hook = EncodeStreamHook;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Start the second thread if needed.
Packit 9c6abc
  if (num_crunch_configs_side != 0) {
Packit 9c6abc
    if (!worker_interface->Reset(&worker_side)) {
Packit 9c6abc
      err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
      goto Error;
Packit 9c6abc
    }
Packit 9c6abc
#if !defined(WEBP_DISABLE_STATS)
Packit 9c6abc
    // This line is here and not in the param initialization above to remove a
Packit 9c6abc
    // Clang static analyzer warning.
Packit 9c6abc
    if (picture->stats != NULL) {
Packit 9c6abc
      memcpy(&stats_side, picture->stats, sizeof(stats_side));
Packit 9c6abc
    }
Packit 9c6abc
#endif
Packit 9c6abc
    // This line is only useful to remove a Clang static analyzer warning.
Packit 9c6abc
    params_side.err_ = VP8_ENC_OK;
Packit 9c6abc
    worker_interface->Launch(&worker_side);
Packit 9c6abc
  }
Packit 9c6abc
  // Execute the main thread.
Packit 9c6abc
  worker_interface->Execute(&worker_main);
Packit 9c6abc
  ok_main = worker_interface->Sync(&worker_main);
Packit 9c6abc
  worker_interface->End(&worker_main);
Packit 9c6abc
  if (num_crunch_configs_side != 0) {
Packit 9c6abc
    // Wait for the second thread.
Packit 9c6abc
    const int ok_side = worker_interface->Sync(&worker_side);
Packit 9c6abc
    worker_interface->End(&worker_side);
Packit 9c6abc
    if (!ok_main || !ok_side) {
Packit 9c6abc
      err = ok_main ? params_side.err_ : params_main.err_;
Packit 9c6abc
      goto Error;
Packit 9c6abc
    }
Packit 9c6abc
    if (VP8LBitWriterNumBytes(&bw_side) < VP8LBitWriterNumBytes(bw_main)) {
Packit 9c6abc
      VP8LBitWriterSwap(bw_main, &bw_side);
Packit 9c6abc
#if !defined(WEBP_DISABLE_STATS)
Packit 9c6abc
      if (picture->stats != NULL) {
Packit 9c6abc
        memcpy(picture->stats, &stats_side, sizeof(*picture->stats));
Packit 9c6abc
      }
Packit 9c6abc
#endif
Packit 9c6abc
    }
Packit 9c6abc
  } else {
Packit 9c6abc
    if (!ok_main) {
Packit 9c6abc
      err = params_main.err_;
Packit 9c6abc
      goto Error;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
Error:
Packit 9c6abc
  VP8LBitWriterWipeOut(&bw_side);
Packit 9c6abc
  VP8LEncoderDelete(enc_main);
Packit 9c6abc
  VP8LEncoderDelete(enc_side);
Packit 9c6abc
  return err;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
#undef CRUNCH_CONFIGS_MAX
Packit 9c6abc
#undef CRUNCH_CONFIGS_LZ77_MAX
Packit 9c6abc
Packit 9c6abc
int VP8LEncodeImage(const WebPConfig* const config,
Packit 9c6abc
                    const WebPPicture* const picture) {
Packit 9c6abc
  int width, height;
Packit 9c6abc
  int has_alpha;
Packit 9c6abc
  size_t coded_size;
Packit 9c6abc
  int percent = 0;
Packit 9c6abc
  int initial_size;
Packit 9c6abc
  WebPEncodingError err = VP8_ENC_OK;
Packit 9c6abc
  VP8LBitWriter bw;
Packit 9c6abc
Packit 9c6abc
  if (picture == NULL) return 0;
Packit 9c6abc
Packit 9c6abc
  if (config == NULL || picture->argb == NULL) {
Packit 9c6abc
    err = VP8_ENC_ERROR_NULL_PARAMETER;
Packit 9c6abc
    WebPEncodingSetError(picture, err);
Packit 9c6abc
    return 0;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  width = picture->width;
Packit 9c6abc
  height = picture->height;
Packit 9c6abc
  // Initialize BitWriter with size corresponding to 16 bpp to photo images and
Packit 9c6abc
  // 8 bpp for graphical images.
Packit 9c6abc
  initial_size = (config->image_hint == WEBP_HINT_GRAPH) ?
Packit 9c6abc
      width * height : width * height * 2;
Packit 9c6abc
  if (!VP8LBitWriterInit(&bw, initial_size)) {
Packit 9c6abc
    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (!WebPReportProgress(picture, 1, &percent)) {
Packit 9c6abc
 UserAbort:
Packit 9c6abc
    err = VP8_ENC_ERROR_USER_ABORT;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
  // Reset stats (for pure lossless coding)
Packit 9c6abc
  if (picture->stats != NULL) {
Packit 9c6abc
    WebPAuxStats* const stats = picture->stats;
Packit 9c6abc
    memset(stats, 0, sizeof(*stats));
Packit 9c6abc
    stats->PSNR[0] = 99.f;
Packit 9c6abc
    stats->PSNR[1] = 99.f;
Packit 9c6abc
    stats->PSNR[2] = 99.f;
Packit 9c6abc
    stats->PSNR[3] = 99.f;
Packit 9c6abc
    stats->PSNR[4] = 99.f;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Write image size.
Packit 9c6abc
  if (!WriteImageSize(picture, &bw)) {
Packit 9c6abc
    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  has_alpha = WebPPictureHasTransparency(picture);
Packit 9c6abc
  // Write the non-trivial Alpha flag and lossless version.
Packit 9c6abc
  if (!WriteRealAlphaAndVersion(&bw, has_alpha)) {
Packit 9c6abc
    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (!WebPReportProgress(picture, 5, &percent)) goto UserAbort;
Packit 9c6abc
Packit 9c6abc
  // Encode main image stream.
Packit 9c6abc
  err = VP8LEncodeStream(config, picture, &bw, 1 /*use_cache*/);
Packit 9c6abc
  if (err != VP8_ENC_OK) goto Error;
Packit 9c6abc
Packit 9c6abc
  if (!WebPReportProgress(picture, 90, &percent)) goto UserAbort;
Packit 9c6abc
Packit 9c6abc
  // Finish the RIFF chunk.
Packit 9c6abc
  err = WriteImage(picture, &bw, &coded_size);
Packit 9c6abc
  if (err != VP8_ENC_OK) goto Error;
Packit 9c6abc
Packit 9c6abc
  if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort;
Packit 9c6abc
Packit 9c6abc
#if !defined(WEBP_DISABLE_STATS)
Packit 9c6abc
  // Save size.
Packit 9c6abc
  if (picture->stats != NULL) {
Packit 9c6abc
    picture->stats->coded_size += (int)coded_size;
Packit 9c6abc
    picture->stats->lossless_size = (int)coded_size;
Packit 9c6abc
  }
Packit 9c6abc
#endif
Packit 9c6abc
Packit 9c6abc
  if (picture->extra_info != NULL) {
Packit 9c6abc
    const int mb_w = (width + 15) >> 4;
Packit 9c6abc
    const int mb_h = (height + 15) >> 4;
Packit 9c6abc
    memset(picture->extra_info, 0, mb_w * mb_h * sizeof(*picture->extra_info));
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
 Error:
Packit 9c6abc
  if (bw.error_) err = VP8_ENC_ERROR_OUT_OF_MEMORY;
Packit 9c6abc
  VP8LBitWriterWipeOut(&bw;;
Packit 9c6abc
  if (err != VP8_ENC_OK) {
Packit 9c6abc
    WebPEncodingSetError(picture, err);
Packit 9c6abc
    return 0;
Packit 9c6abc
  }
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------