summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/libwebp/src/enc
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/libwebp/src/enc')
-rw-r--r--src/3rdparty/libwebp/src/enc/alpha.c410
-rw-r--r--src/3rdparty/libwebp/src/enc/analysis.c497
-rw-r--r--src/3rdparty/libwebp/src/enc/backward_references.c894
-rw-r--r--src/3rdparty/libwebp/src/enc/backward_references.h152
-rw-r--r--src/3rdparty/libwebp/src/enc/config.c140
-rw-r--r--src/3rdparty/libwebp/src/enc/cost.c489
-rw-r--r--src/3rdparty/libwebp/src/enc/cost.h51
-rw-r--r--src/3rdparty/libwebp/src/enc/filter.c471
-rw-r--r--src/3rdparty/libwebp/src/enc/frame.c1068
-rw-r--r--src/3rdparty/libwebp/src/enc/histogram.c512
-rw-r--r--src/3rdparty/libwebp/src/enc/histogram.h101
-rw-r--r--src/3rdparty/libwebp/src/enc/iterator.c456
-rw-r--r--src/3rdparty/libwebp/src/enc/layer.c44
-rw-r--r--src/3rdparty/libwebp/src/enc/picture.c1324
-rw-r--r--src/3rdparty/libwebp/src/enc/quant.c1156
-rw-r--r--src/3rdparty/libwebp/src/enc/syntax.c423
-rw-r--r--src/3rdparty/libwebp/src/enc/token.c273
-rw-r--r--src/3rdparty/libwebp/src/enc/tree.c504
-rw-r--r--src/3rdparty/libwebp/src/enc/vp8enci.h570
-rw-r--r--src/3rdparty/libwebp/src/enc/vp8l.c1168
-rw-r--r--src/3rdparty/libwebp/src/enc/vp8li.h70
-rw-r--r--src/3rdparty/libwebp/src/enc/webpenc.c404
22 files changed, 11177 insertions, 0 deletions
diff --git a/src/3rdparty/libwebp/src/enc/alpha.c b/src/3rdparty/libwebp/src/enc/alpha.c
new file mode 100644
index 0000000..21d4b5c
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/alpha.c
@@ -0,0 +1,410 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Alpha-plane compression.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "./vp8enci.h"
+#include "../utils/filters.h"
+#include "../utils/quant_levels.h"
+#include "../webp/format_constants.h"
+
+// -----------------------------------------------------------------------------
+// Encodes the given alpha data via specified compression method 'method'.
+// The pre-processing (quantization) is performed if 'quality' is less than 100.
+// For such cases, the encoding is lossy. The valid range is [0, 100] for
+// 'quality' and [0, 1] for 'method':
+// 'method = 0' - No compression;
+// 'method = 1' - Use lossless coder on the alpha plane only
+// 'filter' values [0, 4] correspond to prediction modes none, horizontal,
+// vertical & gradient filters. The prediction mode 4 will try all the
+// prediction modes 0 to 3 and pick the best one.
+// 'effort_level': specifies how much effort must be spent to try and reduce
+// the compressed output size. In range 0 (quick) to 6 (slow).
+//
+// 'output' corresponds to the buffer containing compressed alpha data.
+// This buffer is allocated by this method and caller should call
+// free(*output) when done.
+// 'output_size' corresponds to size of this compressed alpha buffer.
+//
+// Returns 1 on successfully encoding the alpha and
+// 0 if either:
+// invalid quality or method, or
+// memory allocation for the compressed data fails.
+
+#include "../enc/vp8li.h"
+
+static int EncodeLossless(const uint8_t* const data, int width, int height,
+ int effort_level, // in [0..6] range
+ VP8BitWriter* const bw,
+ WebPAuxStats* const stats) {
+ int ok = 0;
+ WebPConfig config;
+ WebPPicture picture;
+ VP8LBitWriter tmp_bw;
+
+ WebPPictureInit(&picture);
+ picture.width = width;
+ picture.height = height;
+ picture.use_argb = 1;
+ picture.stats = stats;
+ if (!WebPPictureAlloc(&picture)) return 0;
+
+ // Transfer the alpha values to the green channel.
+ {
+ int i, j;
+ uint32_t* dst = picture.argb;
+ const uint8_t* src = data;
+ for (j = 0; j < picture.height; ++j) {
+ for (i = 0; i < picture.width; ++i) {
+ dst[i] = src[i] << 8; // we leave A/R/B channels zero'd.
+ }
+ src += width;
+ dst += picture.argb_stride;
+ }
+ }
+
+ WebPConfigInit(&config);
+ config.lossless = 1;
+ config.method = effort_level; // impact is very small
+ // Set a low default quality for encoding alpha. Ensure that Alpha quality at
+ // lower methods (3 and below) is less than the threshold for triggering
+ // costly 'BackwardReferencesTraceBackwards'.
+ config.quality = 8.f * effort_level;
+ assert(config.quality >= 0 && config.quality <= 100.f);
+
+ ok = VP8LBitWriterInit(&tmp_bw, (width * height) >> 3);
+ ok = ok && (VP8LEncodeStream(&config, &picture, &tmp_bw) == VP8_ENC_OK);
+ WebPPictureFree(&picture);
+ if (ok) {
+ const uint8_t* const buffer = VP8LBitWriterFinish(&tmp_bw);
+ const size_t buffer_size = VP8LBitWriterNumBytes(&tmp_bw);
+ VP8BitWriterAppend(bw, buffer, buffer_size);
+ }
+ VP8LBitWriterDestroy(&tmp_bw);
+ return ok && !bw->error_;
+}
+
+// -----------------------------------------------------------------------------
+
+// Small struct to hold the result of a filter mode compression attempt.
+typedef struct {
+ size_t score;
+ VP8BitWriter bw;
+ WebPAuxStats stats;
+} FilterTrial;
+
+// This function always returns an initialized 'bw' object, even upon error.
+static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
+ int method, int filter, int reduce_levels,
+ int effort_level, // in [0..6] range
+ uint8_t* const tmp_alpha,
+ FilterTrial* result) {
+ int ok = 0;
+ const uint8_t* alpha_src;
+ WebPFilterFunc filter_func;
+ uint8_t header;
+ size_t expected_size;
+ const size_t data_size = width * height;
+
+ assert((uint64_t)data_size == (uint64_t)width * height); // as per spec
+ assert(filter >= 0 && filter < WEBP_FILTER_LAST);
+ assert(method >= ALPHA_NO_COMPRESSION);
+ assert(method <= ALPHA_LOSSLESS_COMPRESSION);
+ assert(sizeof(header) == ALPHA_HEADER_LEN);
+ // TODO(skal): have a common function and #define's to validate alpha params.
+
+ expected_size =
+ (method == ALPHA_NO_COMPRESSION) ? (ALPHA_HEADER_LEN + data_size)
+ : (data_size >> 5);
+ header = method | (filter << 2);
+ if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4;
+
+ VP8BitWriterInit(&result->bw, expected_size);
+ VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN);
+
+ filter_func = WebPFilters[filter];
+ if (filter_func != NULL) {
+ filter_func(data, width, height, width, tmp_alpha);
+ alpha_src = tmp_alpha;
+ } else {
+ alpha_src = data;
+ }
+
+ if (method == ALPHA_NO_COMPRESSION) {
+ ok = VP8BitWriterAppend(&result->bw, alpha_src, width * height);
+ ok = ok && !result->bw.error_;
+ } else {
+ ok = EncodeLossless(alpha_src, width, height, effort_level,
+ &result->bw, &result->stats);
+ VP8BitWriterFinish(&result->bw);
+ }
+ result->score = VP8BitWriterSize(&result->bw);
+ return ok;
+}
+
+// -----------------------------------------------------------------------------
+
+// TODO(skal): move to dsp/ ?
+static void CopyPlane(const uint8_t* src, int src_stride,
+ uint8_t* dst, int dst_stride, int width, int height) {
+ while (height-- > 0) {
+ memcpy(dst, src, width);
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+static int GetNumColors(const uint8_t* data, int width, int height,
+ int stride) {
+ int j;
+ int colors = 0;
+ uint8_t color[256] = { 0 };
+
+ for (j = 0; j < height; ++j) {
+ int i;
+ const uint8_t* const p = data + j * stride;
+ for (i = 0; i < width; ++i) {
+ color[p[i]] = 1;
+ }
+ }
+ for (j = 0; j < 256; ++j) {
+ if (color[j] > 0) ++colors;
+ }
+ return colors;
+}
+
+#define FILTER_TRY_NONE (1 << WEBP_FILTER_NONE)
+#define FILTER_TRY_ALL ((1 << WEBP_FILTER_LAST) - 1)
+
+// Given the input 'filter' option, return an OR'd bit-set of filters to try.
+static uint32_t GetFilterMap(const uint8_t* alpha, int width, int height,
+ int filter, int effort_level) {
+ uint32_t bit_map = 0U;
+ if (filter == WEBP_FILTER_FAST) {
+ // Quick estimate of the best candidate.
+ int try_filter_none = (effort_level > 3);
+ const int kMinColorsForFilterNone = 16;
+ const int kMaxColorsForFilterNone = 192;
+ const int num_colors = GetNumColors(alpha, width, height, width);
+ // For low number of colors, NONE yields better compression.
+ filter = (num_colors <= kMinColorsForFilterNone) ? WEBP_FILTER_NONE :
+ EstimateBestFilter(alpha, width, height, width);
+ bit_map |= 1 << filter;
+ // For large number of colors, try FILTER_NONE in addition to the best
+ // filter as well.
+ if (try_filter_none || num_colors > kMaxColorsForFilterNone) {
+ bit_map |= FILTER_TRY_NONE;
+ }
+ } else if (filter == WEBP_FILTER_NONE) {
+ bit_map = FILTER_TRY_NONE;
+ } else { // WEBP_FILTER_BEST -> try all
+ bit_map = FILTER_TRY_ALL;
+ }
+ return bit_map;
+}
+
+static void InitFilterTrial(FilterTrial* const score) {
+ score->score = (size_t)~0U;
+ VP8BitWriterInit(&score->bw, 0);
+}
+
+static int ApplyFiltersAndEncode(const uint8_t* alpha, int width, int height,
+ size_t data_size, int method, int filter,
+ int reduce_levels, int effort_level,
+ uint8_t** const output,
+ size_t* const output_size,
+ WebPAuxStats* const stats) {
+ int ok = 1;
+ FilterTrial best;
+ uint32_t try_map =
+ GetFilterMap(alpha, width, height, filter, effort_level);
+ InitFilterTrial(&best);
+ if (try_map != FILTER_TRY_NONE) {
+ uint8_t* filtered_alpha = (uint8_t*)malloc(data_size);
+ if (filtered_alpha == NULL) return 0;
+
+ for (filter = WEBP_FILTER_NONE; ok && try_map; ++filter, try_map >>= 1) {
+ if (try_map & 1) {
+ FilterTrial trial;
+ ok = EncodeAlphaInternal(alpha, width, height, method, filter,
+ reduce_levels, effort_level, filtered_alpha,
+ &trial);
+ if (ok && trial.score < best.score) {
+ VP8BitWriterWipeOut(&best.bw);
+ best = trial;
+ } else {
+ VP8BitWriterWipeOut(&trial.bw);
+ }
+ }
+ }
+ free(filtered_alpha);
+ } else {
+ ok = EncodeAlphaInternal(alpha, width, height, method, WEBP_FILTER_NONE,
+ reduce_levels, effort_level, NULL, &best);
+ }
+ if (ok) {
+ if (stats != NULL) *stats = best.stats;
+ *output_size = VP8BitWriterSize(&best.bw);
+ *output = VP8BitWriterBuf(&best.bw);
+ } else {
+ VP8BitWriterWipeOut(&best.bw);
+ }
+ return ok;
+}
+
+static int EncodeAlpha(VP8Encoder* const enc,
+ int quality, int method, int filter,
+ int effort_level,
+ uint8_t** const output, size_t* const output_size) {
+ const WebPPicture* const pic = enc->pic_;
+ const int width = pic->width;
+ const int height = pic->height;
+
+ uint8_t* quant_alpha = NULL;
+ const size_t data_size = width * height;
+ uint64_t sse = 0;
+ int ok = 1;
+ const int reduce_levels = (quality < 100);
+
+ // quick sanity checks
+ assert((uint64_t)data_size == (uint64_t)width * height); // as per spec
+ assert(enc != NULL && pic != NULL && pic->a != NULL);
+ assert(output != NULL && output_size != NULL);
+ assert(width > 0 && height > 0);
+ assert(pic->a_stride >= width);
+ assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
+
+ if (quality < 0 || quality > 100) {
+ return 0;
+ }
+
+ if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) {
+ return 0;
+ }
+
+ if (method == ALPHA_NO_COMPRESSION) {
+ // Don't filter, as filtering will make no impact on compressed size.
+ filter = WEBP_FILTER_NONE;
+ }
+
+ quant_alpha = (uint8_t*)malloc(data_size);
+ if (quant_alpha == NULL) {
+ return 0;
+ }
+
+ // Extract alpha data (width x height) from raw_data (stride x height).
+ CopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height);
+
+ if (reduce_levels) { // No Quantization required for 'quality = 100'.
+ // 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
+ // mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16]
+ // and Quality:]70, 100] -> Levels:]16, 256].
+ const int alpha_levels = (quality <= 70) ? (2 + quality / 5)
+ : (16 + (quality - 70) * 8);
+ ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &sse);
+ }
+
+ if (ok) {
+ ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method,
+ filter, reduce_levels, effort_level, output,
+ output_size, pic->stats);
+ if (pic->stats != NULL) { // need stats?
+ pic->stats->coded_size += (int)(*output_size);
+ enc->sse_[3] = sse;
+ }
+ }
+
+ free(quant_alpha);
+ return ok;
+}
+
+//------------------------------------------------------------------------------
+// Main calls
+
+static int CompressAlphaJob(VP8Encoder* const enc, void* dummy) {
+ const WebPConfig* config = enc->config_;
+ uint8_t* alpha_data = NULL;
+ size_t alpha_size = 0;
+ const int effort_level = config->method; // maps to [0..6]
+ const WEBP_FILTER_TYPE filter =
+ (config->alpha_filtering == 0) ? WEBP_FILTER_NONE :
+ (config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
+ WEBP_FILTER_BEST;
+ if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression,
+ filter, effort_level, &alpha_data, &alpha_size)) {
+ return 0;
+ }
+ if (alpha_size != (uint32_t)alpha_size) { // Sanity check.
+ free(alpha_data);
+ return 0;
+ }
+ enc->alpha_data_size_ = (uint32_t)alpha_size;
+ enc->alpha_data_ = alpha_data;
+ (void)dummy;
+ return 1;
+}
+
+void VP8EncInitAlpha(VP8Encoder* const enc) {
+ enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_);
+ enc->alpha_data_ = NULL;
+ enc->alpha_data_size_ = 0;
+ if (enc->thread_level_ > 0) {
+ WebPWorker* const worker = &enc->alpha_worker_;
+ WebPWorkerInit(worker);
+ worker->data1 = enc;
+ worker->data2 = NULL;
+ worker->hook = (WebPWorkerHook)CompressAlphaJob;
+ }
+}
+
+int VP8EncStartAlpha(VP8Encoder* const enc) {
+ if (enc->has_alpha_) {
+ if (enc->thread_level_ > 0) {
+ WebPWorker* const worker = &enc->alpha_worker_;
+ if (!WebPWorkerReset(worker)) { // Makes sure worker is good to go.
+ return 0;
+ }
+ WebPWorkerLaunch(worker);
+ return 1;
+ } else {
+ return CompressAlphaJob(enc, NULL); // just do the job right away
+ }
+ }
+ return 1;
+}
+
+int VP8EncFinishAlpha(VP8Encoder* const enc) {
+ if (enc->has_alpha_) {
+ if (enc->thread_level_ > 0) {
+ WebPWorker* const worker = &enc->alpha_worker_;
+ if (!WebPWorkerSync(worker)) return 0; // error
+ }
+ }
+ return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
+}
+
+int VP8EncDeleteAlpha(VP8Encoder* const enc) {
+ int ok = 1;
+ if (enc->thread_level_ > 0) {
+ WebPWorker* const worker = &enc->alpha_worker_;
+ ok = WebPWorkerSync(worker); // finish anything left in flight
+ WebPWorkerEnd(worker); // still need to end the worker, even if !ok
+ }
+ free(enc->alpha_data_);
+ enc->alpha_data_ = NULL;
+ enc->alpha_data_size_ = 0;
+ enc->has_alpha_ = 0;
+ return ok;
+}
+
diff --git a/src/3rdparty/libwebp/src/enc/analysis.c b/src/3rdparty/libwebp/src/enc/analysis.c
new file mode 100644
index 0000000..7d4cfdc
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/analysis.c
@@ -0,0 +1,497 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Macroblock analysis
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "./vp8enci.h"
+#include "./cost.h"
+#include "../utils/utils.h"
+
+#define MAX_ITERS_K_MEANS 6
+
+//------------------------------------------------------------------------------
+// Smooth the segment map by replacing isolated block by the majority of its
+// neighbours.
+
+static void SmoothSegmentMap(VP8Encoder* const enc) {
+ int n, x, y;
+ const int w = enc->mb_w_;
+ const int h = enc->mb_h_;
+ const int majority_cnt_3_x_3_grid = 5;
+ uint8_t* const tmp = (uint8_t*)WebPSafeMalloc((uint64_t)w * h, sizeof(*tmp));
+ assert((uint64_t)(w * h) == (uint64_t)w * h); // no overflow, as per spec
+
+ if (tmp == NULL) return;
+ for (y = 1; y < h - 1; ++y) {
+ for (x = 1; x < w - 1; ++x) {
+ int cnt[NUM_MB_SEGMENTS] = { 0 };
+ const VP8MBInfo* const mb = &enc->mb_info_[x + w * y];
+ int majority_seg = mb->segment_;
+ // Check the 8 neighbouring segment values.
+ cnt[mb[-w - 1].segment_]++; // top-left
+ cnt[mb[-w + 0].segment_]++; // top
+ cnt[mb[-w + 1].segment_]++; // top-right
+ cnt[mb[ - 1].segment_]++; // left
+ cnt[mb[ + 1].segment_]++; // right
+ cnt[mb[ w - 1].segment_]++; // bottom-left
+ cnt[mb[ w + 0].segment_]++; // bottom
+ cnt[mb[ w + 1].segment_]++; // bottom-right
+ for (n = 0; n < NUM_MB_SEGMENTS; ++n) {
+ if (cnt[n] >= majority_cnt_3_x_3_grid) {
+ majority_seg = n;
+ break;
+ }
+ }
+ tmp[x + y * w] = majority_seg;
+ }
+ }
+ for (y = 1; y < h - 1; ++y) {
+ for (x = 1; x < w - 1; ++x) {
+ VP8MBInfo* const mb = &enc->mb_info_[x + w * y];
+ mb->segment_ = tmp[x + y * w];
+ }
+ }
+ free(tmp);
+}
+
+//------------------------------------------------------------------------------
+// set segment susceptibility alpha_ / beta_
+
+static WEBP_INLINE int clip(int v, int m, int M) {
+ return (v < m) ? m : (v > M) ? M : v;
+}
+
+static void SetSegmentAlphas(VP8Encoder* const enc,
+ const int centers[NUM_MB_SEGMENTS],
+ int mid) {
+ const int nb = enc->segment_hdr_.num_segments_;
+ int min = centers[0], max = centers[0];
+ int n;
+
+ if (nb > 1) {
+ for (n = 0; n < nb; ++n) {
+ if (min > centers[n]) min = centers[n];
+ if (max < centers[n]) max = centers[n];
+ }
+ }
+ if (max == min) max = min + 1;
+ assert(mid <= max && mid >= min);
+ for (n = 0; n < nb; ++n) {
+ const int alpha = 255 * (centers[n] - mid) / (max - min);
+ const int beta = 255 * (centers[n] - min) / (max - min);
+ enc->dqm_[n].alpha_ = clip(alpha, -127, 127);
+ enc->dqm_[n].beta_ = clip(beta, 0, 255);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Compute susceptibility based on DCT-coeff histograms:
+// the higher, the "easier" the macroblock is to compress.
+
+#define MAX_ALPHA 255 // 8b of precision for susceptibilities.
+#define ALPHA_SCALE (2 * MAX_ALPHA) // scaling factor for alpha.
+#define DEFAULT_ALPHA (-1)
+#define IS_BETTER_ALPHA(alpha, best_alpha) ((alpha) > (best_alpha))
+
+static int FinalAlphaValue(int alpha) {
+ alpha = MAX_ALPHA - alpha;
+ return clip(alpha, 0, MAX_ALPHA);
+}
+
+static int GetAlpha(const VP8Histogram* const histo) {
+ int max_value = 0, last_non_zero = 1;
+ int k;
+ int alpha;
+ for (k = 0; k <= MAX_COEFF_THRESH; ++k) {
+ const int value = histo->distribution[k];
+ if (value > 0) {
+ if (value > max_value) max_value = value;
+ last_non_zero = k;
+ }
+ }
+ // 'alpha' will later be clipped to [0..MAX_ALPHA] range, clamping outer
+ // values which happen to be mostly noise. This leaves the maximum precision
+ // for handling the useful small values which contribute most.
+ alpha = (max_value > 1) ? ALPHA_SCALE * last_non_zero / max_value : 0;
+ return alpha;
+}
+
+static void MergeHistograms(const VP8Histogram* const in,
+ VP8Histogram* const out) {
+ int i;
+ for (i = 0; i <= MAX_COEFF_THRESH; ++i) {
+ out->distribution[i] += in->distribution[i];
+ }
+}
+
+//------------------------------------------------------------------------------
+// Simplified k-Means, to assign Nb segments based on alpha-histogram
+
+static void AssignSegments(VP8Encoder* const enc,
+ const int alphas[MAX_ALPHA + 1]) {
+ const int nb = enc->segment_hdr_.num_segments_;
+ int centers[NUM_MB_SEGMENTS];
+ int weighted_average = 0;
+ int map[MAX_ALPHA + 1];
+ int a, n, k;
+ int min_a = 0, max_a = MAX_ALPHA, range_a;
+ // 'int' type is ok for histo, and won't overflow
+ int accum[NUM_MB_SEGMENTS], dist_accum[NUM_MB_SEGMENTS];
+
+ assert(nb >= 1);
+
+ // bracket the input
+ for (n = 0; n <= MAX_ALPHA && alphas[n] == 0; ++n) {}
+ min_a = n;
+ for (n = MAX_ALPHA; n > min_a && alphas[n] == 0; --n) {}
+ max_a = n;
+ range_a = max_a - min_a;
+
+ // Spread initial centers evenly
+ for (k = 0, n = 1; k < nb; ++k, n += 2) {
+ assert(n < 2 * nb);
+ centers[k] = min_a + (n * range_a) / (2 * nb);
+ }
+
+ for (k = 0; k < MAX_ITERS_K_MEANS; ++k) { // few iters are enough
+ int total_weight;
+ int displaced;
+ // Reset stats
+ for (n = 0; n < nb; ++n) {
+ accum[n] = 0;
+ dist_accum[n] = 0;
+ }
+ // Assign nearest center for each 'a'
+ n = 0; // track the nearest center for current 'a'
+ for (a = min_a; a <= max_a; ++a) {
+ if (alphas[a]) {
+ while (n + 1 < nb && abs(a - centers[n + 1]) < abs(a - centers[n])) {
+ n++;
+ }
+ map[a] = n;
+ // accumulate contribution into best centroid
+ dist_accum[n] += a * alphas[a];
+ accum[n] += alphas[a];
+ }
+ }
+ // All point are classified. Move the centroids to the
+ // center of their respective cloud.
+ displaced = 0;
+ weighted_average = 0;
+ total_weight = 0;
+ for (n = 0; n < nb; ++n) {
+ if (accum[n]) {
+ const int new_center = (dist_accum[n] + accum[n] / 2) / accum[n];
+ displaced += abs(centers[n] - new_center);
+ centers[n] = new_center;
+ weighted_average += new_center * accum[n];
+ total_weight += accum[n];
+ }
+ }
+ weighted_average = (weighted_average + total_weight / 2) / total_weight;
+ if (displaced < 5) break; // no need to keep on looping...
+ }
+
+ // Map each original value to the closest centroid
+ for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
+ VP8MBInfo* const mb = &enc->mb_info_[n];
+ const int alpha = mb->alpha_;
+ mb->segment_ = map[alpha];
+ mb->alpha_ = centers[map[alpha]]; // for the record.
+ }
+
+ if (nb > 1) {
+ const int smooth = (enc->config_->preprocessing & 1);
+ if (smooth) SmoothSegmentMap(enc);
+ }
+
+ SetSegmentAlphas(enc, centers, weighted_average); // pick some alphas.
+}
+
+//------------------------------------------------------------------------------
+// Macroblock analysis: collect histogram for each mode, deduce the maximal
+// susceptibility and set best modes for this macroblock.
+// Segment assignment is done later.
+
+// Number of modes to inspect for alpha_ evaluation. For high-quality settings
+// (method >= FAST_ANALYSIS_METHOD) we don't need to test all the possible modes
+// during the analysis phase.
+#define FAST_ANALYSIS_METHOD 4 // method above which we do partial analysis
+#define MAX_INTRA16_MODE 2
+#define MAX_INTRA4_MODE 2
+#define MAX_UV_MODE 2
+
+static int MBAnalyzeBestIntra16Mode(VP8EncIterator* const it) {
+ const int max_mode =
+ (it->enc_->method_ >= FAST_ANALYSIS_METHOD) ? MAX_INTRA16_MODE
+ : NUM_PRED_MODES;
+ int mode;
+ int best_alpha = DEFAULT_ALPHA;
+ int best_mode = 0;
+
+ VP8MakeLuma16Preds(it);
+ for (mode = 0; mode < max_mode; ++mode) {
+ VP8Histogram histo = { { 0 } };
+ int alpha;
+
+ VP8CollectHistogram(it->yuv_in_ + Y_OFF,
+ it->yuv_p_ + VP8I16ModeOffsets[mode],
+ 0, 16, &histo);
+ alpha = GetAlpha(&histo);
+ if (IS_BETTER_ALPHA(alpha, best_alpha)) {
+ best_alpha = alpha;
+ best_mode = mode;
+ }
+ }
+ VP8SetIntra16Mode(it, best_mode);
+ return best_alpha;
+}
+
+static int MBAnalyzeBestIntra4Mode(VP8EncIterator* const it,
+ int best_alpha) {
+ uint8_t modes[16];
+ const int max_mode =
+ (it->enc_->method_ >= FAST_ANALYSIS_METHOD) ? MAX_INTRA4_MODE
+ : NUM_BMODES;
+ int i4_alpha;
+ VP8Histogram total_histo = { { 0 } };
+ int cur_histo = 0;
+
+ VP8IteratorStartI4(it);
+ do {
+ int mode;
+ int best_mode_alpha = DEFAULT_ALPHA;
+ VP8Histogram histos[2];
+ const uint8_t* const src = it->yuv_in_ + Y_OFF + VP8Scan[it->i4_];
+
+ VP8MakeIntra4Preds(it);
+ for (mode = 0; mode < max_mode; ++mode) {
+ int alpha;
+
+ memset(&histos[cur_histo], 0, sizeof(histos[cur_histo]));
+ VP8CollectHistogram(src, it->yuv_p_ + VP8I4ModeOffsets[mode],
+ 0, 1, &histos[cur_histo]);
+ alpha = GetAlpha(&histos[cur_histo]);
+ if (IS_BETTER_ALPHA(alpha, best_mode_alpha)) {
+ best_mode_alpha = alpha;
+ modes[it->i4_] = mode;
+ cur_histo ^= 1; // keep track of best histo so far.
+ }
+ }
+ // accumulate best histogram
+ MergeHistograms(&histos[cur_histo ^ 1], &total_histo);
+ // Note: we reuse the original samples for predictors
+ } while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF));
+
+ i4_alpha = GetAlpha(&total_histo);
+ if (IS_BETTER_ALPHA(i4_alpha, best_alpha)) {
+ VP8SetIntra4Mode(it, modes);
+ best_alpha = i4_alpha;
+ }
+ return best_alpha;
+}
+
+static int MBAnalyzeBestUVMode(VP8EncIterator* const it) {
+ int best_alpha = DEFAULT_ALPHA;
+ int best_mode = 0;
+ const int max_mode =
+ (it->enc_->method_ >= FAST_ANALYSIS_METHOD) ? MAX_UV_MODE
+ : NUM_PRED_MODES;
+ int mode;
+ VP8MakeChroma8Preds(it);
+ for (mode = 0; mode < max_mode; ++mode) {
+ VP8Histogram histo = { { 0 } };
+ int alpha;
+ VP8CollectHistogram(it->yuv_in_ + U_OFF,
+ it->yuv_p_ + VP8UVModeOffsets[mode],
+ 16, 16 + 4 + 4, &histo);
+ alpha = GetAlpha(&histo);
+ if (IS_BETTER_ALPHA(alpha, best_alpha)) {
+ best_alpha = alpha;
+ best_mode = mode;
+ }
+ }
+ VP8SetIntraUVMode(it, best_mode);
+ return best_alpha;
+}
+
+static void MBAnalyze(VP8EncIterator* const it,
+ int alphas[MAX_ALPHA + 1],
+ int* const alpha, int* const uv_alpha) {
+ const VP8Encoder* const enc = it->enc_;
+ int best_alpha, best_uv_alpha;
+
+ VP8SetIntra16Mode(it, 0); // default: Intra16, DC_PRED
+ VP8SetSkip(it, 0); // not skipped
+ VP8SetSegment(it, 0); // default segment, spec-wise.
+
+ best_alpha = MBAnalyzeBestIntra16Mode(it);
+ if (enc->method_ >= 5) {
+ // We go and make a fast decision for intra4/intra16.
+ // It's usually not a good and definitive pick, but helps seeding the stats
+ // about level bit-cost.
+ // TODO(skal): improve criterion.
+ best_alpha = MBAnalyzeBestIntra4Mode(it, best_alpha);
+ }
+ best_uv_alpha = MBAnalyzeBestUVMode(it);
+
+ // Final susceptibility mix
+ best_alpha = (3 * best_alpha + best_uv_alpha + 2) >> 2;
+ best_alpha = FinalAlphaValue(best_alpha);
+ alphas[best_alpha]++;
+ it->mb_->alpha_ = best_alpha; // for later remapping.
+
+ // Accumulate for later complexity analysis.
+ *alpha += best_alpha; // mixed susceptibility (not just luma)
+ *uv_alpha += best_uv_alpha;
+}
+
+static void DefaultMBInfo(VP8MBInfo* const mb) {
+ mb->type_ = 1; // I16x16
+ mb->uv_mode_ = 0;
+ mb->skip_ = 0; // not skipped
+ mb->segment_ = 0; // default segment
+ mb->alpha_ = 0;
+}
+
+//------------------------------------------------------------------------------
+// Main analysis loop:
+// Collect all susceptibilities for each macroblock and record their
+// distribution in alphas[]. Segments is assigned a-posteriori, based on
+// this histogram.
+// We also pick an intra16 prediction mode, which shouldn't be considered
+// final except for fast-encode settings. We can also pick some intra4 modes
+// and decide intra4/intra16, but that's usually almost always a bad choice at
+// this stage.
+
+static void ResetAllMBInfo(VP8Encoder* const enc) {
+ int n;
+ for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
+ DefaultMBInfo(&enc->mb_info_[n]);
+ }
+ // Default susceptibilities.
+ enc->dqm_[0].alpha_ = 0;
+ enc->dqm_[0].beta_ = 0;
+ // Note: we can't compute this alpha_ / uv_alpha_ -> set to default value.
+ enc->alpha_ = 0;
+ enc->uv_alpha_ = 0;
+ WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
+}
+
+// struct used to collect job result
+typedef struct {
+ WebPWorker worker;
+ int alphas[MAX_ALPHA + 1];
+ int alpha, uv_alpha;
+ VP8EncIterator it;
+ int delta_progress;
+} SegmentJob;
+
+// main work call
+static int DoSegmentsJob(SegmentJob* const job, VP8EncIterator* const it) {
+ int ok = 1;
+ if (!VP8IteratorIsDone(it)) {
+ uint8_t tmp[32 + ALIGN_CST];
+ uint8_t* const scratch = (uint8_t*)DO_ALIGN(tmp);
+ do {
+ // Let's pretend we have perfect lossless reconstruction.
+ VP8IteratorImport(it, scratch);
+ MBAnalyze(it, job->alphas, &job->alpha, &job->uv_alpha);
+ ok = VP8IteratorProgress(it, job->delta_progress);
+ } while (ok && VP8IteratorNext(it));
+ }
+ return ok;
+}
+
+static void MergeJobs(const SegmentJob* const src, SegmentJob* const dst) {
+ int i;
+ for (i = 0; i <= MAX_ALPHA; ++i) dst->alphas[i] += src->alphas[i];
+ dst->alpha += src->alpha;
+ dst->uv_alpha += src->uv_alpha;
+}
+
+// initialize the job struct with some TODOs
+static void InitSegmentJob(VP8Encoder* const enc, SegmentJob* const job,
+ int start_row, int end_row) {
+ WebPWorkerInit(&job->worker);
+ job->worker.data1 = job;
+ job->worker.data2 = &job->it;
+ job->worker.hook = (WebPWorkerHook)DoSegmentsJob;
+ VP8IteratorInit(enc, &job->it);
+ VP8IteratorSetRow(&job->it, start_row);
+ VP8IteratorSetCountDown(&job->it, (end_row - start_row) * enc->mb_w_);
+ memset(job->alphas, 0, sizeof(job->alphas));
+ job->alpha = 0;
+ job->uv_alpha = 0;
+ // only one of both jobs can record the progress, since we don't
+ // expect the user's hook to be multi-thread safe
+ job->delta_progress = (start_row == 0) ? 20 : 0;
+}
+
+// main entry point
+int VP8EncAnalyze(VP8Encoder* const enc) {
+ int ok = 1;
+ const int do_segments =
+ enc->config_->emulate_jpeg_size || // We need the complexity evaluation.
+ (enc->segment_hdr_.num_segments_ > 1) ||
+ (enc->method_ == 0); // for method 0, we need preds_[] to be filled.
+ if (do_segments) {
+ const int last_row = enc->mb_h_;
+ // We give a little more than a half work to the main thread.
+ const int split_row = (9 * last_row + 15) >> 4;
+ const int total_mb = last_row * enc->mb_w_;
+#ifdef WEBP_USE_THREAD
+ const int kMinSplitRow = 2; // minimal rows needed for mt to be worth it
+ const int do_mt = (enc->thread_level_ > 0) && (split_row >= kMinSplitRow);
+#else
+ const int do_mt = 0;
+#endif
+ SegmentJob main_job;
+ if (do_mt) {
+ SegmentJob side_job;
+ // Note the use of '&' instead of '&&' because we must call the functions
+ // no matter what.
+ InitSegmentJob(enc, &main_job, 0, split_row);
+ InitSegmentJob(enc, &side_job, split_row, last_row);
+ // we don't need to call Reset() on main_job.worker, since we're calling
+ // WebPWorkerExecute() on it
+ ok &= WebPWorkerReset(&side_job.worker);
+ // launch the two jobs in parallel
+ if (ok) {
+ WebPWorkerLaunch(&side_job.worker);
+ WebPWorkerExecute(&main_job.worker);
+ ok &= WebPWorkerSync(&side_job.worker);
+ ok &= WebPWorkerSync(&main_job.worker);
+ }
+ WebPWorkerEnd(&side_job.worker);
+ if (ok) MergeJobs(&side_job, &main_job); // merge results together
+ } else {
+ // Even for single-thread case, we use the generic Worker tools.
+ InitSegmentJob(enc, &main_job, 0, last_row);
+ WebPWorkerExecute(&main_job.worker);
+ ok &= WebPWorkerSync(&main_job.worker);
+ }
+ WebPWorkerEnd(&main_job.worker);
+ if (ok) {
+ enc->alpha_ = main_job.alpha / total_mb;
+ enc->uv_alpha_ = main_job.uv_alpha / total_mb;
+ AssignSegments(enc, main_job.alphas);
+ }
+ } else { // Use only one default segment.
+ ResetAllMBInfo(enc);
+ }
+ return ok;
+}
+
diff --git a/src/3rdparty/libwebp/src/enc/backward_references.c b/src/3rdparty/libwebp/src/enc/backward_references.c
new file mode 100644
index 0000000..77b4be7
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/backward_references.c
@@ -0,0 +1,894 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Author: Jyrki Alakuijala (jyrki@google.com)
+//
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "./backward_references.h"
+#include "./histogram.h"
+#include "../dsp/lossless.h"
+#include "../utils/color_cache.h"
+#include "../utils/utils.h"
+
+#define VALUES_IN_BYTE 256
+
+#define HASH_BITS 18
+#define HASH_SIZE (1 << HASH_BITS)
+#define HASH_MULTIPLIER (0xc6a4a7935bd1e995ULL)
+
+// 1M window (4M bytes) minus 120 special codes for short distances.
+#define WINDOW_SIZE ((1 << 20) - 120)
+
+// Bounds for the match length.
+#define MIN_LENGTH 2
+#define MAX_LENGTH 4096
+
+typedef struct {
+ // Stores the most recently added position with the given hash value.
+ int32_t hash_to_first_index_[HASH_SIZE];
+ // chain_[pos] stores the previous position with the same hash value
+ // for every pixel in the image.
+ int32_t* chain_;
+} HashChain;
+
+// -----------------------------------------------------------------------------
+
+static const uint8_t plane_to_code_lut[128] = {
+ 96, 73, 55, 39, 23, 13, 5, 1, 255, 255, 255, 255, 255, 255, 255, 255,
+ 101, 78, 58, 42, 26, 16, 8, 2, 0, 3, 9, 17, 27, 43, 59, 79,
+ 102, 86, 62, 46, 32, 20, 10, 6, 4, 7, 11, 21, 33, 47, 63, 87,
+ 105, 90, 70, 52, 37, 28, 18, 14, 12, 15, 19, 29, 38, 53, 71, 91,
+ 110, 99, 82, 66, 48, 35, 30, 24, 22, 25, 31, 36, 49, 67, 83, 100,
+ 115, 108, 94, 76, 64, 50, 44, 40, 34, 41, 45, 51, 65, 77, 95, 109,
+ 118, 113, 103, 92, 80, 68, 60, 56, 54, 57, 61, 69, 81, 93, 104, 114,
+ 119, 116, 111, 106, 97, 88, 84, 74, 72, 75, 85, 89, 98, 107, 112, 117
+};
+
+static int DistanceToPlaneCode(int xsize, int dist) {
+ const int yoffset = dist / xsize;
+ const int xoffset = dist - yoffset * xsize;
+ if (xoffset <= 8 && yoffset < 8) {
+ return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1;
+ } else if (xoffset > xsize - 8 && yoffset < 7) {
+ return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1;
+ }
+ return dist + 120;
+}
+
+static WEBP_INLINE int FindMatchLength(const uint32_t* const array1,
+ const uint32_t* const array2,
+ const int max_limit) {
+ int match_len = 0;
+ while (match_len < max_limit && array1[match_len] == array2[match_len]) {
+ ++match_len;
+ }
+ return match_len;
+}
+
+// -----------------------------------------------------------------------------
+// VP8LBackwardRefs
+
+void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs) {
+ if (refs != NULL) {
+ refs->refs = NULL;
+ refs->size = 0;
+ refs->max_size = 0;
+ }
+}
+
+void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs) {
+ if (refs != NULL) {
+ free(refs->refs);
+ VP8LInitBackwardRefs(refs);
+ }
+}
+
+int VP8LBackwardRefsAlloc(VP8LBackwardRefs* const refs, int max_size) {
+ assert(refs != NULL);
+ refs->size = 0;
+ refs->max_size = 0;
+ refs->refs = (PixOrCopy*)WebPSafeMalloc((uint64_t)max_size,
+ sizeof(*refs->refs));
+ if (refs->refs == NULL) return 0;
+ refs->max_size = max_size;
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+// Hash chains
+
+static WEBP_INLINE uint64_t GetPixPairHash64(const uint32_t* const argb) {
+ uint64_t key = ((uint64_t)(argb[1]) << 32) | argb[0];
+ key = (key * HASH_MULTIPLIER) >> (64 - HASH_BITS);
+ return key;
+}
+
+static int HashChainInit(HashChain* const p, int size) {
+ int i;
+ p->chain_ = (int*)WebPSafeMalloc((uint64_t)size, sizeof(*p->chain_));
+ if (p->chain_ == NULL) {
+ return 0;
+ }
+ for (i = 0; i < size; ++i) {
+ p->chain_[i] = -1;
+ }
+ for (i = 0; i < HASH_SIZE; ++i) {
+ p->hash_to_first_index_[i] = -1;
+ }
+ return 1;
+}
+
+static void HashChainDelete(HashChain* const p) {
+ if (p != NULL) {
+ free(p->chain_);
+ free(p);
+ }
+}
+
+// Insertion of two pixels at a time.
+static void HashChainInsert(HashChain* const p,
+ const uint32_t* const argb, int pos) {
+ const uint64_t hash_code = GetPixPairHash64(argb);
+ p->chain_[pos] = p->hash_to_first_index_[hash_code];
+ p->hash_to_first_index_[hash_code] = pos;
+}
+
+static void GetParamsForHashChainFindCopy(int quality, int xsize,
+ int cache_bits, int* window_size,
+ int* iter_pos, int* iter_limit) {
+ const int iter_mult = (quality < 27) ? 1 : 1 + ((quality - 27) >> 4);
+ const int iter_neg = -iter_mult * (quality >> 1);
+ // Limit the backward-ref window size for lower qualities.
+ const int max_window_size = (quality > 50) ? WINDOW_SIZE
+ : (quality > 25) ? (xsize << 8)
+ : (xsize << 4);
+ assert(xsize > 0);
+ *window_size = (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE
+ : max_window_size;
+ *iter_pos = 8 + (quality >> 3);
+ // For lower entropy images, the rigorous search loop in HashChainFindCopy
+ // can be relaxed.
+ *iter_limit = (cache_bits > 0) ? iter_neg : iter_neg / 2;
+}
+
+static int HashChainFindCopy(const HashChain* const p,
+ int base_position, int xsize_signed,
+ const uint32_t* const argb, int max_len,
+ int window_size, int iter_pos, int iter_limit,
+ int* const distance_ptr,
+ int* const length_ptr) {
+ const uint32_t* const argb_start = argb + base_position;
+ uint64_t best_val = 0;
+ uint32_t best_length = 1;
+ uint32_t best_distance = 0;
+ const uint32_t xsize = (uint32_t)xsize_signed;
+ const int min_pos =
+ (base_position > window_size) ? base_position - window_size : 0;
+ int pos;
+ assert(xsize > 0);
+ if (max_len > MAX_LENGTH) {
+ max_len = MAX_LENGTH;
+ }
+ for (pos = p->hash_to_first_index_[GetPixPairHash64(argb_start)];
+ pos >= min_pos;
+ pos = p->chain_[pos]) {
+ uint64_t val;
+ uint32_t curr_length;
+ uint32_t distance;
+ const uint64_t* const ptr1 =
+ (const uint64_t*)(argb + pos + best_length - 1);
+ const uint64_t* const ptr2 =
+ (const uint64_t*)(argb_start + best_length - 1);
+
+ if (iter_pos < 0) {
+ if (iter_pos < iter_limit || best_val >= 0xff0000) {
+ break;
+ }
+ }
+ --iter_pos;
+
+ // Before 'expensive' linear match, check if the two arrays match at the
+ // current best length index and also for the succeeding elements.
+ if (*ptr1 != *ptr2) continue;
+
+ curr_length = FindMatchLength(argb + pos, argb_start, max_len);
+ if (curr_length < best_length) continue;
+
+ distance = (uint32_t)(base_position - pos);
+ val = curr_length << 16;
+ // Favoring 2d locality here gives savings for certain images.
+ if (distance < 9 * xsize) {
+ const uint32_t y = distance / xsize;
+ uint32_t x = distance % xsize;
+ if (x > (xsize >> 1)) {
+ x = xsize - x;
+ }
+ if (x <= 7) {
+ val += 9 * 9 + 9 * 9;
+ val -= y * y + x * x;
+ }
+ }
+ if (best_val < val) {
+ best_val = val;
+ best_length = curr_length;
+ best_distance = distance;
+ if (curr_length >= (uint32_t)max_len) {
+ break;
+ }
+ if ((best_distance == 1 || distance == xsize) &&
+ best_length >= 128) {
+ break;
+ }
+ }
+ }
+ *distance_ptr = (int)best_distance;
+ *length_ptr = best_length;
+ return (best_length >= MIN_LENGTH);
+}
+
+static WEBP_INLINE void PushBackCopy(VP8LBackwardRefs* const refs, int length) {
+ int size = refs->size;
+ while (length >= MAX_LENGTH) {
+ refs->refs[size++] = PixOrCopyCreateCopy(1, MAX_LENGTH);
+ length -= MAX_LENGTH;
+ }
+ if (length > 0) {
+ refs->refs[size++] = PixOrCopyCreateCopy(1, length);
+ }
+ refs->size = size;
+}
+
+static void BackwardReferencesRle(int xsize, int ysize,
+ const uint32_t* const argb,
+ VP8LBackwardRefs* const refs) {
+ const int pix_count = xsize * ysize;
+ int match_len = 0;
+ int i;
+ refs->size = 0;
+ PushBackCopy(refs, match_len); // i=0 case
+ refs->refs[refs->size++] = PixOrCopyCreateLiteral(argb[0]);
+ for (i = 1; i < pix_count; ++i) {
+ if (argb[i] == argb[i - 1]) {
+ ++match_len;
+ } else {
+ PushBackCopy(refs, match_len);
+ match_len = 0;
+ refs->refs[refs->size++] = PixOrCopyCreateLiteral(argb[i]);
+ }
+ }
+ PushBackCopy(refs, match_len);
+}
+
+static int BackwardReferencesHashChain(int xsize, int ysize,
+ const uint32_t* const argb,
+ int cache_bits, int quality,
+ VP8LBackwardRefs* const refs) {
+ int i;
+ int ok = 0;
+ int cc_init = 0;
+ const int use_color_cache = (cache_bits > 0);
+ const int pix_count = xsize * ysize;
+ HashChain* const hash_chain = (HashChain*)malloc(sizeof(*hash_chain));
+ VP8LColorCache hashers;
+ int window_size = WINDOW_SIZE;
+ int iter_pos = 1;
+ int iter_limit = -1;
+
+ if (hash_chain == NULL) return 0;
+ if (use_color_cache) {
+ cc_init = VP8LColorCacheInit(&hashers, cache_bits);
+ if (!cc_init) goto Error;
+ }
+
+ if (!HashChainInit(hash_chain, pix_count)) goto Error;
+
+ refs->size = 0;
+ GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
+ &window_size, &iter_pos, &iter_limit);
+ for (i = 0; i < pix_count; ) {
+ // Alternative#1: Code the pixels starting at 'i' using backward reference.
+ int offset = 0;
+ int len = 0;
+ if (i < pix_count - 1) { // FindCopy(i,..) reads pixels at [i] and [i + 1].
+ int max_len = pix_count - i;
+ HashChainFindCopy(hash_chain, i, xsize, argb, max_len,
+ window_size, iter_pos, iter_limit,
+ &offset, &len);
+ }
+ if (len >= MIN_LENGTH) {
+ // Alternative#2: Insert the pixel at 'i' as literal, and code the
+ // pixels starting at 'i + 1' using backward reference.
+ int offset2 = 0;
+ int len2 = 0;
+ int k;
+ HashChainInsert(hash_chain, &argb[i], i);
+ if (i < pix_count - 2) { // FindCopy(i+1,..) reads [i + 1] and [i + 2].
+ int max_len = pix_count - (i + 1);
+ HashChainFindCopy(hash_chain, i + 1, xsize, argb, max_len,
+ window_size, iter_pos, iter_limit,
+ &offset2, &len2);
+ if (len2 > len + 1) {
+ const uint32_t pixel = argb[i];
+ // Alternative#2 is a better match. So push pixel at 'i' as literal.
+ if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) {
+ const int ix = VP8LColorCacheGetIndex(&hashers, pixel);
+ refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix);
+ } else {
+ if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel);
+ refs->refs[refs->size] = PixOrCopyCreateLiteral(pixel);
+ }
+ ++refs->size;
+ i++; // Backward reference to be done for next pixel.
+ len = len2;
+ offset = offset2;
+ }
+ }
+ if (len >= MAX_LENGTH) {
+ len = MAX_LENGTH - 1;
+ }
+ refs->refs[refs->size++] = PixOrCopyCreateCopy(offset, len);
+ if (use_color_cache) {
+ for (k = 0; k < len; ++k) {
+ VP8LColorCacheInsert(&hashers, argb[i + k]);
+ }
+ }
+ // Add to the hash_chain (but cannot add the last pixel).
+ {
+ const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
+ for (k = 1; k < last; ++k) {
+ HashChainInsert(hash_chain, &argb[i + k], i + k);
+ }
+ }
+ i += len;
+ } else {
+ const uint32_t pixel = argb[i];
+ if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) {
+ // push pixel as a PixOrCopyCreateCacheIdx pixel
+ const int ix = VP8LColorCacheGetIndex(&hashers, pixel);
+ refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix);
+ } else {
+ if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel);
+ refs->refs[refs->size] = PixOrCopyCreateLiteral(pixel);
+ }
+ ++refs->size;
+ if (i + 1 < pix_count) {
+ HashChainInsert(hash_chain, &argb[i], i);
+ }
+ ++i;
+ }
+ }
+ ok = 1;
+Error:
+ if (cc_init) VP8LColorCacheClear(&hashers);
+ HashChainDelete(hash_chain);
+ return ok;
+}
+
+// -----------------------------------------------------------------------------
+
+typedef struct {
+ double alpha_[VALUES_IN_BYTE];
+ double red_[VALUES_IN_BYTE];
+ double literal_[PIX_OR_COPY_CODES_MAX];
+ double blue_[VALUES_IN_BYTE];
+ double distance_[NUM_DISTANCE_CODES];
+} CostModel;
+
+static int BackwardReferencesTraceBackwards(
+ int xsize, int ysize, int recursive_cost_model,
+ const uint32_t* const argb, int quality, int cache_bits,
+ VP8LBackwardRefs* const refs);
+
+static void ConvertPopulationCountTableToBitEstimates(
+ int num_symbols, const int population_counts[], double output[]) {
+ int sum = 0;
+ int nonzeros = 0;
+ int i;
+ for (i = 0; i < num_symbols; ++i) {
+ sum += population_counts[i];
+ if (population_counts[i] > 0) {
+ ++nonzeros;
+ }
+ }
+ if (nonzeros <= 1) {
+ memset(output, 0, num_symbols * sizeof(*output));
+ } else {
+ const double logsum = VP8LFastLog2(sum);
+ for (i = 0; i < num_symbols; ++i) {
+ output[i] = logsum - VP8LFastLog2(population_counts[i]);
+ }
+ }
+}
+
+static int CostModelBuild(CostModel* const m, int xsize, int ysize,
+ int recursion_level, const uint32_t* const argb,
+ int quality, int cache_bits) {
+ int ok = 0;
+ VP8LHistogram histo;
+ VP8LBackwardRefs refs;
+
+ if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize)) goto Error;
+
+ if (recursion_level > 0) {
+ if (!BackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1,
+ argb, quality, cache_bits, &refs)) {
+ goto Error;
+ }
+ } else {
+ if (!BackwardReferencesHashChain(xsize, ysize, argb, cache_bits, quality,
+ &refs)) {
+ goto Error;
+ }
+ }
+ VP8LHistogramCreate(&histo, &refs, cache_bits);
+ ConvertPopulationCountTableToBitEstimates(
+ VP8LHistogramNumCodes(&histo), histo.literal_, m->literal_);
+ ConvertPopulationCountTableToBitEstimates(
+ VALUES_IN_BYTE, histo.red_, m->red_);
+ ConvertPopulationCountTableToBitEstimates(
+ VALUES_IN_BYTE, histo.blue_, m->blue_);
+ ConvertPopulationCountTableToBitEstimates(
+ VALUES_IN_BYTE, histo.alpha_, m->alpha_);
+ ConvertPopulationCountTableToBitEstimates(
+ NUM_DISTANCE_CODES, histo.distance_, m->distance_);
+ ok = 1;
+
+ Error:
+ VP8LClearBackwardRefs(&refs);
+ return ok;
+}
+
+static WEBP_INLINE double GetLiteralCost(const CostModel* const m, uint32_t v) {
+ return m->alpha_[v >> 24] +
+ m->red_[(v >> 16) & 0xff] +
+ m->literal_[(v >> 8) & 0xff] +
+ m->blue_[v & 0xff];
+}
+
+static WEBP_INLINE double GetCacheCost(const CostModel* const m, uint32_t idx) {
+ const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx;
+ return m->literal_[literal_idx];
+}
+
+static WEBP_INLINE double GetLengthCost(const CostModel* const m,
+ uint32_t length) {
+ int code, extra_bits;
+ VP8LPrefixEncodeBits(length, &code, &extra_bits);
+ return m->literal_[VALUES_IN_BYTE + code] + extra_bits;
+}
+
+static WEBP_INLINE double GetDistanceCost(const CostModel* const m,
+ uint32_t distance) {
+ int code, extra_bits;
+ VP8LPrefixEncodeBits(distance, &code, &extra_bits);
+ return m->distance_[code] + extra_bits;
+}
+
+static int BackwardReferencesHashChainDistanceOnly(
+ int xsize, int ysize, int recursive_cost_model, const uint32_t* const argb,
+ int quality, int cache_bits, uint32_t* const dist_array) {
+ int i;
+ int ok = 0;
+ int cc_init = 0;
+ const int pix_count = xsize * ysize;
+ const int use_color_cache = (cache_bits > 0);
+ float* const cost =
+ (float*)WebPSafeMalloc((uint64_t)pix_count, sizeof(*cost));
+ CostModel* cost_model = (CostModel*)malloc(sizeof(*cost_model));
+ HashChain* hash_chain = (HashChain*)malloc(sizeof(*hash_chain));
+ VP8LColorCache hashers;
+ const double mul0 = (recursive_cost_model != 0) ? 1.0 : 0.68;
+ const double mul1 = (recursive_cost_model != 0) ? 1.0 : 0.82;
+ const int min_distance_code = 2; // TODO(vikasa): tune as function of quality
+ int window_size = WINDOW_SIZE;
+ int iter_pos = 1;
+ int iter_limit = -1;
+
+ if (cost == NULL || cost_model == NULL || hash_chain == NULL) goto Error;
+
+ if (!HashChainInit(hash_chain, pix_count)) goto Error;
+
+ if (use_color_cache) {
+ cc_init = VP8LColorCacheInit(&hashers, cache_bits);
+ if (!cc_init) goto Error;
+ }
+
+ if (!CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, argb,
+ quality, cache_bits)) {
+ goto Error;
+ }
+
+ for (i = 0; i < pix_count; ++i) cost[i] = 1e38f;
+
+ // We loop one pixel at a time, but store all currently best points to
+ // non-processed locations from this point.
+ dist_array[0] = 0;
+ GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
+ &window_size, &iter_pos, &iter_limit);
+ for (i = 0; i < pix_count; ++i) {
+ double prev_cost = 0.0;
+ int shortmax;
+ if (i > 0) {
+ prev_cost = cost[i - 1];
+ }
+ for (shortmax = 0; shortmax < 2; ++shortmax) {
+ int offset = 0;
+ int len = 0;
+ if (i < pix_count - 1) { // FindCopy reads pixels at [i] and [i + 1].
+ int max_len = shortmax ? 2 : pix_count - i;
+ HashChainFindCopy(hash_chain, i, xsize, argb, max_len,
+ window_size, iter_pos, iter_limit,
+ &offset, &len);
+ }
+ if (len >= MIN_LENGTH) {
+ const int code = DistanceToPlaneCode(xsize, offset);
+ const double distance_cost =
+ prev_cost + GetDistanceCost(cost_model, code);
+ int k;
+ for (k = 1; k < len; ++k) {
+ const double cost_val = distance_cost + GetLengthCost(cost_model, k);
+ if (cost[i + k] > cost_val) {
+ cost[i + k] = (float)cost_val;
+ dist_array[i + k] = k + 1;
+ }
+ }
+ // This if is for speedup only. It roughly doubles the speed, and
+ // makes compression worse by .1 %.
+ if (len >= 128 && code <= min_distance_code) {
+ // Long copy for short distances, let's skip the middle
+ // lookups for better copies.
+ // 1) insert the hashes.
+ if (use_color_cache) {
+ for (k = 0; k < len; ++k) {
+ VP8LColorCacheInsert(&hashers, argb[i + k]);
+ }
+ }
+ // 2) Add to the hash_chain (but cannot add the last pixel)
+ {
+ const int last = (len + i < pix_count - 1) ? len + i
+ : pix_count - 1;
+ for (k = i; k < last; ++k) {
+ HashChainInsert(hash_chain, &argb[k], k);
+ }
+ }
+ // 3) jump.
+ i += len - 1; // for loop does ++i, thus -1 here.
+ goto next_symbol;
+ }
+ }
+ }
+ if (i < pix_count - 1) {
+ HashChainInsert(hash_chain, &argb[i], i);
+ }
+ {
+ // inserting a literal pixel
+ double cost_val = prev_cost;
+ if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) {
+ const int ix = VP8LColorCacheGetIndex(&hashers, argb[i]);
+ cost_val += GetCacheCost(cost_model, ix) * mul0;
+ } else {
+ if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
+ cost_val += GetLiteralCost(cost_model, argb[i]) * mul1;
+ }
+ if (cost[i] > cost_val) {
+ cost[i] = (float)cost_val;
+ dist_array[i] = 1; // only one is inserted.
+ }
+ }
+ next_symbol: ;
+ }
+ // Last pixel still to do, it can only be a single step if not reached
+ // through cheaper means already.
+ ok = 1;
+Error:
+ if (cc_init) VP8LColorCacheClear(&hashers);
+ HashChainDelete(hash_chain);
+ free(cost_model);
+ free(cost);
+ return ok;
+}
+
+// We pack the path at the end of *dist_array and return
+// a pointer to this part of the array. Example:
+// dist_array = [1x2xx3x2] => packed [1x2x1232], chosen_path = [1232]
+static void TraceBackwards(uint32_t* const dist_array,
+ int dist_array_size,
+ uint32_t** const chosen_path,
+ int* const chosen_path_size) {
+ uint32_t* path = dist_array + dist_array_size;
+ uint32_t* cur = dist_array + dist_array_size - 1;
+ while (cur >= dist_array) {
+ const int k = *cur;
+ --path;
+ *path = k;
+ cur -= k;
+ }
+ *chosen_path = path;
+ *chosen_path_size = (int)(dist_array + dist_array_size - path);
+}
+
+static int BackwardReferencesHashChainFollowChosenPath(
+ int xsize, int ysize, const uint32_t* const argb,
+ int quality, int cache_bits,
+ const uint32_t* const chosen_path, int chosen_path_size,
+ VP8LBackwardRefs* const refs) {
+ const int pix_count = xsize * ysize;
+ const int use_color_cache = (cache_bits > 0);
+ int size = 0;
+ int i = 0;
+ int k;
+ int ix;
+ int ok = 0;
+ int cc_init = 0;
+ int window_size = WINDOW_SIZE;
+ int iter_pos = 1;
+ int iter_limit = -1;
+ HashChain* hash_chain = (HashChain*)malloc(sizeof(*hash_chain));
+ VP8LColorCache hashers;
+
+ if (hash_chain == NULL || !HashChainInit(hash_chain, pix_count)) {
+ goto Error;
+ }
+ if (use_color_cache) {
+ cc_init = VP8LColorCacheInit(&hashers, cache_bits);
+ if (!cc_init) goto Error;
+ }
+
+ refs->size = 0;
+ GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
+ &window_size, &iter_pos, &iter_limit);
+ for (ix = 0; ix < chosen_path_size; ++ix, ++size) {
+ int offset = 0;
+ int len = 0;
+ int max_len = chosen_path[ix];
+ if (max_len != 1) {
+ HashChainFindCopy(hash_chain, i, xsize, argb, max_len,
+ window_size, iter_pos, iter_limit,
+ &offset, &len);
+ assert(len == max_len);
+ refs->refs[size] = PixOrCopyCreateCopy(offset, len);
+ if (use_color_cache) {
+ for (k = 0; k < len; ++k) {
+ VP8LColorCacheInsert(&hashers, argb[i + k]);
+ }
+ }
+ {
+ const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
+ for (k = 0; k < last; ++k) {
+ HashChainInsert(hash_chain, &argb[i + k], i + k);
+ }
+ }
+ i += len;
+ } else {
+ if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) {
+ // push pixel as a color cache index
+ const int idx = VP8LColorCacheGetIndex(&hashers, argb[i]);
+ refs->refs[size] = PixOrCopyCreateCacheIdx(idx);
+ } else {
+ if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
+ refs->refs[size] = PixOrCopyCreateLiteral(argb[i]);
+ }
+ if (i + 1 < pix_count) {
+ HashChainInsert(hash_chain, &argb[i], i);
+ }
+ ++i;
+ }
+ }
+ assert(size <= refs->max_size);
+ refs->size = size;
+ ok = 1;
+Error:
+ if (cc_init) VP8LColorCacheClear(&hashers);
+ HashChainDelete(hash_chain);
+ return ok;
+}
+
+// Returns 1 on success.
+static int BackwardReferencesTraceBackwards(int xsize, int ysize,
+ int recursive_cost_model,
+ const uint32_t* const argb,
+ int quality, int cache_bits,
+ VP8LBackwardRefs* const refs) {
+ int ok = 0;
+ const int dist_array_size = xsize * ysize;
+ uint32_t* chosen_path = NULL;
+ int chosen_path_size = 0;
+ uint32_t* dist_array =
+ (uint32_t*)WebPSafeMalloc((uint64_t)dist_array_size, sizeof(*dist_array));
+
+ if (dist_array == NULL) goto Error;
+
+ if (!BackwardReferencesHashChainDistanceOnly(
+ xsize, ysize, recursive_cost_model, argb, quality, cache_bits,
+ dist_array)) {
+ goto Error;
+ }
+ TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size);
+ if (!BackwardReferencesHashChainFollowChosenPath(
+ xsize, ysize, argb, quality, cache_bits, chosen_path, chosen_path_size,
+ refs)) {
+ goto Error;
+ }
+ ok = 1;
+ Error:
+ free(dist_array);
+ return ok;
+}
+
+static void BackwardReferences2DLocality(int xsize,
+ VP8LBackwardRefs* const refs) {
+ int i;
+ for (i = 0; i < refs->size; ++i) {
+ if (PixOrCopyIsCopy(&refs->refs[i])) {
+ const int dist = refs->refs[i].argb_or_distance;
+ const int transformed_dist = DistanceToPlaneCode(xsize, dist);
+ refs->refs[i].argb_or_distance = transformed_dist;
+ }
+ }
+}
+
+int VP8LGetBackwardReferences(int width, int height,
+ const uint32_t* const argb,
+ int quality, int cache_bits, int use_2d_locality,
+ VP8LBackwardRefs* const best) {
+ int ok = 0;
+ int lz77_is_useful;
+ VP8LBackwardRefs refs_rle, refs_lz77;
+ const int num_pix = width * height;
+
+ VP8LBackwardRefsAlloc(&refs_rle, num_pix);
+ VP8LBackwardRefsAlloc(&refs_lz77, num_pix);
+ VP8LInitBackwardRefs(best);
+ if (refs_rle.refs == NULL || refs_lz77.refs == NULL) {
+ Error1:
+ VP8LClearBackwardRefs(&refs_rle);
+ VP8LClearBackwardRefs(&refs_lz77);
+ goto End;
+ }
+
+ if (!BackwardReferencesHashChain(width, height, argb, cache_bits, quality,
+ &refs_lz77)) {
+ goto End;
+ }
+ // Backward Reference using RLE only.
+ BackwardReferencesRle(width, height, argb, &refs_rle);
+
+ {
+ double bit_cost_lz77, bit_cost_rle;
+ VP8LHistogram* const histo = (VP8LHistogram*)malloc(sizeof(*histo));
+ if (histo == NULL) goto Error1;
+ // Evaluate lz77 coding
+ VP8LHistogramCreate(histo, &refs_lz77, cache_bits);
+ bit_cost_lz77 = VP8LHistogramEstimateBits(histo);
+ // Evaluate RLE coding
+ VP8LHistogramCreate(histo, &refs_rle, cache_bits);
+ bit_cost_rle = VP8LHistogramEstimateBits(histo);
+ // Decide if LZ77 is useful.
+ lz77_is_useful = (bit_cost_lz77 < bit_cost_rle);
+ free(histo);
+ }
+
+ // Choose appropriate backward reference.
+ if (lz77_is_useful) {
+ // TraceBackwards is costly. Don't execute it at lower quality.
+ const int try_lz77_trace_backwards = (quality >= 25);
+ *best = refs_lz77; // default guess: lz77 is better
+ VP8LClearBackwardRefs(&refs_rle);
+ if (try_lz77_trace_backwards) {
+ // Set recursion level for large images using a color cache.
+ const int recursion_level =
+ (num_pix < 320 * 200) && (cache_bits > 0) ? 1 : 0;
+ VP8LBackwardRefs refs_trace;
+ if (!VP8LBackwardRefsAlloc(&refs_trace, num_pix)) {
+ goto End;
+ }
+ if (BackwardReferencesTraceBackwards(width, height, recursion_level, argb,
+ quality, cache_bits, &refs_trace)) {
+ VP8LClearBackwardRefs(&refs_lz77);
+ *best = refs_trace;
+ }
+ }
+ } else {
+ VP8LClearBackwardRefs(&refs_lz77);
+ *best = refs_rle;
+ }
+
+ if (use_2d_locality) BackwardReferences2DLocality(width, best);
+
+ ok = 1;
+
+ End:
+ if (!ok) {
+ VP8LClearBackwardRefs(best);
+ }
+ return ok;
+}
+
+// Returns 1 on success.
+static int ComputeCacheHistogram(const uint32_t* const argb,
+ int xsize, int ysize,
+ const VP8LBackwardRefs* const refs,
+ int cache_bits,
+ VP8LHistogram* const histo) {
+ int pixel_index = 0;
+ int i;
+ uint32_t k;
+ VP8LColorCache hashers;
+ const int use_color_cache = (cache_bits > 0);
+ int cc_init = 0;
+
+ if (use_color_cache) {
+ cc_init = VP8LColorCacheInit(&hashers, cache_bits);
+ if (!cc_init) return 0;
+ }
+
+ for (i = 0; i < refs->size; ++i) {
+ const PixOrCopy* const v = &refs->refs[i];
+ if (PixOrCopyIsLiteral(v)) {
+ if (use_color_cache &&
+ VP8LColorCacheContains(&hashers, argb[pixel_index])) {
+ // push pixel as a cache index
+ const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]);
+ const PixOrCopy token = PixOrCopyCreateCacheIdx(ix);
+ VP8LHistogramAddSinglePixOrCopy(histo, &token);
+ } else {
+ VP8LHistogramAddSinglePixOrCopy(histo, v);
+ }
+ } else {
+ VP8LHistogramAddSinglePixOrCopy(histo, v);
+ }
+ if (use_color_cache) {
+ for (k = 0; k < PixOrCopyLength(v); ++k) {
+ VP8LColorCacheInsert(&hashers, argb[pixel_index + k]);
+ }
+ }
+ pixel_index += PixOrCopyLength(v);
+ }
+ assert(pixel_index == xsize * ysize);
+ (void)xsize; // xsize is not used in non-debug compilations otherwise.
+ (void)ysize; // ysize is not used in non-debug compilations otherwise.
+ if (cc_init) VP8LColorCacheClear(&hashers);
+ return 1;
+}
+
+// Returns how many bits are to be used for a color cache.
+int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
+ int xsize, int ysize,
+ int* const best_cache_bits) {
+ int ok = 0;
+ int cache_bits;
+ double lowest_entropy = 1e99;
+ VP8LBackwardRefs refs;
+ static const double kSmallPenaltyForLargeCache = 4.0;
+ static const int quality = 30;
+ if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize) ||
+ !BackwardReferencesHashChain(xsize, ysize, argb, 0, quality, &refs)) {
+ goto Error;
+ }
+ for (cache_bits = 0; cache_bits <= MAX_COLOR_CACHE_BITS; ++cache_bits) {
+ double cur_entropy;
+ VP8LHistogram histo;
+ VP8LHistogramInit(&histo, cache_bits);
+ ComputeCacheHistogram(argb, xsize, ysize, &refs, cache_bits, &histo);
+ cur_entropy = VP8LHistogramEstimateBits(&histo) +
+ kSmallPenaltyForLargeCache * cache_bits;
+ if (cache_bits == 0 || cur_entropy < lowest_entropy) {
+ *best_cache_bits = cache_bits;
+ lowest_entropy = cur_entropy;
+ }
+ }
+ ok = 1;
+ Error:
+ VP8LClearBackwardRefs(&refs);
+ return ok;
+}
diff --git a/src/3rdparty/libwebp/src/enc/backward_references.h b/src/3rdparty/libwebp/src/enc/backward_references.h
new file mode 100644
index 0000000..e1c75f0
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/backward_references.h
@@ -0,0 +1,152 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Author: Jyrki Alakuijala (jyrki@google.com)
+//
+
+#ifndef WEBP_ENC_BACKWARD_REFERENCES_H_
+#define WEBP_ENC_BACKWARD_REFERENCES_H_
+
+#include <assert.h>
+#include <stdlib.h>
+#include "../webp/types.h"
+#include "../webp/format_constants.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// The spec allows 11, we use 9 bits to reduce memory consumption in encoding.
+// Having 9 instead of 11 only removes about 0.25 % of compression density.
+#define MAX_COLOR_CACHE_BITS 9
+
+// Max ever number of codes we'll use:
+#define PIX_OR_COPY_CODES_MAX \
+ (NUM_LITERAL_CODES + NUM_LENGTH_CODES + (1 << MAX_COLOR_CACHE_BITS))
+
+// -----------------------------------------------------------------------------
+// PixOrCopy
+
+enum Mode {
+ kLiteral,
+ kCacheIdx,
+ kCopy,
+ kNone
+};
+
+typedef struct {
+ // mode as uint8_t to make the memory layout to be exactly 8 bytes.
+ uint8_t mode;
+ uint16_t len;
+ uint32_t argb_or_distance;
+} PixOrCopy;
+
+static WEBP_INLINE PixOrCopy PixOrCopyCreateCopy(uint32_t distance,
+ uint16_t len) {
+ PixOrCopy retval;
+ retval.mode = kCopy;
+ retval.argb_or_distance = distance;
+ retval.len = len;
+ return retval;
+}
+
+static WEBP_INLINE PixOrCopy PixOrCopyCreateCacheIdx(int idx) {
+ PixOrCopy retval;
+ assert(idx >= 0);
+ assert(idx < (1 << MAX_COLOR_CACHE_BITS));
+ retval.mode = kCacheIdx;
+ retval.argb_or_distance = idx;
+ retval.len = 1;
+ return retval;
+}
+
+static WEBP_INLINE PixOrCopy PixOrCopyCreateLiteral(uint32_t argb) {
+ PixOrCopy retval;
+ retval.mode = kLiteral;
+ retval.argb_or_distance = argb;
+ retval.len = 1;
+ return retval;
+}
+
+static WEBP_INLINE int PixOrCopyIsLiteral(const PixOrCopy* const p) {
+ return (p->mode == kLiteral);
+}
+
+static WEBP_INLINE int PixOrCopyIsCacheIdx(const PixOrCopy* const p) {
+ return (p->mode == kCacheIdx);
+}
+
+static WEBP_INLINE int PixOrCopyIsCopy(const PixOrCopy* const p) {
+ return (p->mode == kCopy);
+}
+
+static WEBP_INLINE uint32_t PixOrCopyLiteral(const PixOrCopy* const p,
+ int component) {
+ assert(p->mode == kLiteral);
+ return (p->argb_or_distance >> (component * 8)) & 0xff;
+}
+
+static WEBP_INLINE uint32_t PixOrCopyLength(const PixOrCopy* const p) {
+ return p->len;
+}
+
+static WEBP_INLINE uint32_t PixOrCopyArgb(const PixOrCopy* const p) {
+ assert(p->mode == kLiteral);
+ return p->argb_or_distance;
+}
+
+static WEBP_INLINE uint32_t PixOrCopyCacheIdx(const PixOrCopy* const p) {
+ assert(p->mode == kCacheIdx);
+ assert(p->argb_or_distance < (1U << MAX_COLOR_CACHE_BITS));
+ return p->argb_or_distance;
+}
+
+static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy* const p) {
+ assert(p->mode == kCopy);
+ return p->argb_or_distance;
+}
+
+// -----------------------------------------------------------------------------
+// VP8LBackwardRefs
+
+typedef struct {
+ PixOrCopy* refs;
+ int size; // currently used
+ int max_size; // maximum capacity
+} VP8LBackwardRefs;
+
+// Initialize the object. Must be called first. 'refs' can be NULL.
+void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs);
+
+// Release memory and re-initialize the object. 'refs' can be NULL.
+void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs);
+
+// Allocate 'max_size' references. Returns false in case of memory error.
+int VP8LBackwardRefsAlloc(VP8LBackwardRefs* const refs, int max_size);
+
+// -----------------------------------------------------------------------------
+// Main entry points
+
+// Evaluates best possible backward references for specified quality.
+// Further optimize for 2D locality if use_2d_locality flag is set.
+int VP8LGetBackwardReferences(int width, int height,
+ const uint32_t* const argb,
+ int quality, int cache_bits, int use_2d_locality,
+ VP8LBackwardRefs* const best);
+
+// Produce an estimate for a good color cache size for the image.
+int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
+ int xsize, int ysize,
+ int* const best_cache_bits);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // WEBP_ENC_BACKWARD_REFERENCES_H_
diff --git a/src/3rdparty/libwebp/src/enc/config.c b/src/3rdparty/libwebp/src/enc/config.c
new file mode 100644
index 0000000..af7f0b0
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/config.c
@@ -0,0 +1,140 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Coding tools configuration
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "../webp/encode.h"
+
+//------------------------------------------------------------------------------
+// WebPConfig
+//------------------------------------------------------------------------------
+
+int WebPConfigInitInternal(WebPConfig* config,
+ WebPPreset preset, float quality, int version) {
+ if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) {
+ return 0; // caller/system version mismatch!
+ }
+ if (config == NULL) return 0;
+
+ config->quality = quality;
+ config->target_size = 0;
+ config->target_PSNR = 0.;
+ config->method = 4;
+ config->sns_strength = 50;
+ config->filter_strength = 60; // mid-filtering
+ config->filter_sharpness = 0;
+ config->filter_type = 1; // default: strong (so U/V is filtered too)
+ config->partitions = 0;
+ config->segments = 4;
+ config->pass = 1;
+ config->show_compressed = 0;
+ config->preprocessing = 0;
+ config->autofilter = 0;
+ config->partition_limit = 0;
+ config->alpha_compression = 1;
+ config->alpha_filtering = 1;
+ config->alpha_quality = 100;
+ config->lossless = 0;
+ config->image_hint = WEBP_HINT_DEFAULT;
+ config->emulate_jpeg_size = 0;
+ config->thread_level = 0;
+ config->low_memory = 0;
+
+ // TODO(skal): tune.
+ switch (preset) {
+ case WEBP_PRESET_PICTURE:
+ config->sns_strength = 80;
+ config->filter_sharpness = 4;
+ config->filter_strength = 35;
+ config->preprocessing &= ~2; // no dithering
+ break;
+ case WEBP_PRESET_PHOTO:
+ config->sns_strength = 80;
+ config->filter_sharpness = 3;
+ config->filter_strength = 30;
+ config->preprocessing |= 2;
+ break;
+ case WEBP_PRESET_DRAWING:
+ config->sns_strength = 25;
+ config->filter_sharpness = 6;
+ config->filter_strength = 10;
+ break;
+ case WEBP_PRESET_ICON:
+ config->sns_strength = 0;
+ config->filter_strength = 0; // disable filtering to retain sharpness
+ config->preprocessing &= ~2; // no dithering
+ break;
+ case WEBP_PRESET_TEXT:
+ config->sns_strength = 0;
+ config->filter_strength = 0; // disable filtering to retain sharpness
+ config->preprocessing &= ~2; // no dithering
+ config->segments = 2;
+ break;
+ case WEBP_PRESET_DEFAULT:
+ default:
+ break;
+ }
+ return WebPValidateConfig(config);
+}
+
+int WebPValidateConfig(const WebPConfig* config) {
+ if (config == NULL) return 0;
+ if (config->quality < 0 || config->quality > 100)
+ return 0;
+ if (config->target_size < 0)
+ return 0;
+ if (config->target_PSNR < 0)
+ return 0;
+ if (config->method < 0 || config->method > 6)
+ return 0;
+ if (config->segments < 1 || config->segments > 4)
+ return 0;
+ if (config->sns_strength < 0 || config->sns_strength > 100)
+ return 0;
+ if (config->filter_strength < 0 || config->filter_strength > 100)
+ return 0;
+ if (config->filter_sharpness < 0 || config->filter_sharpness > 7)
+ return 0;
+ if (config->filter_type < 0 || config->filter_type > 1)
+ return 0;
+ if (config->autofilter < 0 || config->autofilter > 1)
+ return 0;
+ if (config->pass < 1 || config->pass > 10)
+ return 0;
+ if (config->show_compressed < 0 || config->show_compressed > 1)
+ return 0;
+ if (config->preprocessing < 0 || config->preprocessing > 3)
+ return 0;
+ if (config->partitions < 0 || config->partitions > 3)
+ return 0;
+ if (config->partition_limit < 0 || config->partition_limit > 100)
+ return 0;
+ if (config->alpha_compression < 0)
+ return 0;
+ if (config->alpha_filtering < 0)
+ return 0;
+ if (config->alpha_quality < 0 || config->alpha_quality > 100)
+ return 0;
+ if (config->lossless < 0 || config->lossless > 1)
+ return 0;
+ if (config->image_hint >= WEBP_HINT_LAST)
+ return 0;
+ if (config->emulate_jpeg_size < 0 || config->emulate_jpeg_size > 1)
+ return 0;
+ if (config->thread_level < 0 || config->thread_level > 1)
+ return 0;
+ if (config->low_memory < 0 || config->low_memory > 1)
+ return 0;
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+
diff --git a/src/3rdparty/libwebp/src/enc/cost.c b/src/3rdparty/libwebp/src/enc/cost.c
new file mode 100644
index 0000000..09699f8
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/cost.c
@@ -0,0 +1,489 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Cost tables for level and modes
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "./cost.h"
+
+//------------------------------------------------------------------------------
+// Boolean-cost cost table
+
+const uint16_t VP8EntropyCost[256] = {
+ 1792, 1792, 1792, 1536, 1536, 1408, 1366, 1280, 1280, 1216,
+ 1178, 1152, 1110, 1076, 1061, 1024, 1024, 992, 968, 951,
+ 939, 911, 896, 878, 871, 854, 838, 820, 811, 794,
+ 786, 768, 768, 752, 740, 732, 720, 709, 704, 690,
+ 683, 672, 666, 655, 647, 640, 631, 622, 615, 607,
+ 598, 592, 586, 576, 572, 564, 559, 555, 547, 541,
+ 534, 528, 522, 512, 512, 504, 500, 494, 488, 483,
+ 477, 473, 467, 461, 458, 452, 448, 443, 438, 434,
+ 427, 424, 419, 415, 410, 406, 403, 399, 394, 390,
+ 384, 384, 377, 374, 370, 366, 362, 359, 355, 351,
+ 347, 342, 342, 336, 333, 330, 326, 323, 320, 316,
+ 312, 308, 305, 302, 299, 296, 293, 288, 287, 283,
+ 280, 277, 274, 272, 268, 266, 262, 256, 256, 256,
+ 251, 248, 245, 242, 240, 237, 234, 232, 228, 226,
+ 223, 221, 218, 216, 214, 211, 208, 205, 203, 201,
+ 198, 196, 192, 191, 188, 187, 183, 181, 179, 176,
+ 175, 171, 171, 168, 165, 163, 160, 159, 156, 154,
+ 152, 150, 148, 146, 144, 142, 139, 138, 135, 133,
+ 131, 128, 128, 125, 123, 121, 119, 117, 115, 113,
+ 111, 110, 107, 105, 103, 102, 100, 98, 96, 94,
+ 92, 91, 89, 86, 86, 83, 82, 80, 77, 76,
+ 74, 73, 71, 69, 67, 66, 64, 63, 61, 59,
+ 57, 55, 54, 52, 51, 49, 47, 46, 44, 43,
+ 41, 40, 38, 36, 35, 33, 32, 30, 29, 27,
+ 25, 24, 22, 21, 19, 18, 16, 15, 13, 12,
+ 10, 9, 7, 6, 4, 3
+};
+
+//------------------------------------------------------------------------------
+// Level cost tables
+
+// For each given level, the following table gives the pattern of contexts to
+// use for coding it (in [][0]) as well as the bit value to use for each
+// context (in [][1]).
+const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2] = {
+ {0x001, 0x000}, {0x007, 0x001}, {0x00f, 0x005},
+ {0x00f, 0x00d}, {0x033, 0x003}, {0x033, 0x003}, {0x033, 0x023},
+ {0x033, 0x023}, {0x033, 0x023}, {0x033, 0x023}, {0x0d3, 0x013},
+ {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013},
+ {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x093},
+ {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093},
+ {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093},
+ {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093},
+ {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x153}
+};
+
+// fixed costs for coding levels, deduce from the coding tree.
+// This is only the part that doesn't depend on the probability state.
+const uint16_t VP8LevelFixedCosts[MAX_LEVEL + 1] = {
+ 0, 256, 256, 256, 256, 432, 618, 630,
+ 731, 640, 640, 828, 901, 948, 1021, 1101,
+ 1174, 1221, 1294, 1042, 1085, 1115, 1158, 1202,
+ 1245, 1275, 1318, 1337, 1380, 1410, 1453, 1497,
+ 1540, 1570, 1613, 1280, 1295, 1317, 1332, 1358,
+ 1373, 1395, 1410, 1454, 1469, 1491, 1506, 1532,
+ 1547, 1569, 1584, 1601, 1616, 1638, 1653, 1679,
+ 1694, 1716, 1731, 1775, 1790, 1812, 1827, 1853,
+ 1868, 1890, 1905, 1727, 1733, 1742, 1748, 1759,
+ 1765, 1774, 1780, 1800, 1806, 1815, 1821, 1832,
+ 1838, 1847, 1853, 1878, 1884, 1893, 1899, 1910,
+ 1916, 1925, 1931, 1951, 1957, 1966, 1972, 1983,
+ 1989, 1998, 2004, 2027, 2033, 2042, 2048, 2059,
+ 2065, 2074, 2080, 2100, 2106, 2115, 2121, 2132,
+ 2138, 2147, 2153, 2178, 2184, 2193, 2199, 2210,
+ 2216, 2225, 2231, 2251, 2257, 2266, 2272, 2283,
+ 2289, 2298, 2304, 2168, 2174, 2183, 2189, 2200,
+ 2206, 2215, 2221, 2241, 2247, 2256, 2262, 2273,
+ 2279, 2288, 2294, 2319, 2325, 2334, 2340, 2351,
+ 2357, 2366, 2372, 2392, 2398, 2407, 2413, 2424,
+ 2430, 2439, 2445, 2468, 2474, 2483, 2489, 2500,
+ 2506, 2515, 2521, 2541, 2547, 2556, 2562, 2573,
+ 2579, 2588, 2594, 2619, 2625, 2634, 2640, 2651,
+ 2657, 2666, 2672, 2692, 2698, 2707, 2713, 2724,
+ 2730, 2739, 2745, 2540, 2546, 2555, 2561, 2572,
+ 2578, 2587, 2593, 2613, 2619, 2628, 2634, 2645,
+ 2651, 2660, 2666, 2691, 2697, 2706, 2712, 2723,
+ 2729, 2738, 2744, 2764, 2770, 2779, 2785, 2796,
+ 2802, 2811, 2817, 2840, 2846, 2855, 2861, 2872,
+ 2878, 2887, 2893, 2913, 2919, 2928, 2934, 2945,
+ 2951, 2960, 2966, 2991, 2997, 3006, 3012, 3023,
+ 3029, 3038, 3044, 3064, 3070, 3079, 3085, 3096,
+ 3102, 3111, 3117, 2981, 2987, 2996, 3002, 3013,
+ 3019, 3028, 3034, 3054, 3060, 3069, 3075, 3086,
+ 3092, 3101, 3107, 3132, 3138, 3147, 3153, 3164,
+ 3170, 3179, 3185, 3205, 3211, 3220, 3226, 3237,
+ 3243, 3252, 3258, 3281, 3287, 3296, 3302, 3313,
+ 3319, 3328, 3334, 3354, 3360, 3369, 3375, 3386,
+ 3392, 3401, 3407, 3432, 3438, 3447, 3453, 3464,
+ 3470, 3479, 3485, 3505, 3511, 3520, 3526, 3537,
+ 3543, 3552, 3558, 2816, 2822, 2831, 2837, 2848,
+ 2854, 2863, 2869, 2889, 2895, 2904, 2910, 2921,
+ 2927, 2936, 2942, 2967, 2973, 2982, 2988, 2999,
+ 3005, 3014, 3020, 3040, 3046, 3055, 3061, 3072,
+ 3078, 3087, 3093, 3116, 3122, 3131, 3137, 3148,
+ 3154, 3163, 3169, 3189, 3195, 3204, 3210, 3221,
+ 3227, 3236, 3242, 3267, 3273, 3282, 3288, 3299,
+ 3305, 3314, 3320, 3340, 3346, 3355, 3361, 3372,
+ 3378, 3387, 3393, 3257, 3263, 3272, 3278, 3289,
+ 3295, 3304, 3310, 3330, 3336, 3345, 3351, 3362,
+ 3368, 3377, 3383, 3408, 3414, 3423, 3429, 3440,
+ 3446, 3455, 3461, 3481, 3487, 3496, 3502, 3513,
+ 3519, 3528, 3534, 3557, 3563, 3572, 3578, 3589,
+ 3595, 3604, 3610, 3630, 3636, 3645, 3651, 3662,
+ 3668, 3677, 3683, 3708, 3714, 3723, 3729, 3740,
+ 3746, 3755, 3761, 3781, 3787, 3796, 3802, 3813,
+ 3819, 3828, 3834, 3629, 3635, 3644, 3650, 3661,
+ 3667, 3676, 3682, 3702, 3708, 3717, 3723, 3734,
+ 3740, 3749, 3755, 3780, 3786, 3795, 3801, 3812,
+ 3818, 3827, 3833, 3853, 3859, 3868, 3874, 3885,
+ 3891, 3900, 3906, 3929, 3935, 3944, 3950, 3961,
+ 3967, 3976, 3982, 4002, 4008, 4017, 4023, 4034,
+ 4040, 4049, 4055, 4080, 4086, 4095, 4101, 4112,
+ 4118, 4127, 4133, 4153, 4159, 4168, 4174, 4185,
+ 4191, 4200, 4206, 4070, 4076, 4085, 4091, 4102,
+ 4108, 4117, 4123, 4143, 4149, 4158, 4164, 4175,
+ 4181, 4190, 4196, 4221, 4227, 4236, 4242, 4253,
+ 4259, 4268, 4274, 4294, 4300, 4309, 4315, 4326,
+ 4332, 4341, 4347, 4370, 4376, 4385, 4391, 4402,
+ 4408, 4417, 4423, 4443, 4449, 4458, 4464, 4475,
+ 4481, 4490, 4496, 4521, 4527, 4536, 4542, 4553,
+ 4559, 4568, 4574, 4594, 4600, 4609, 4615, 4626,
+ 4632, 4641, 4647, 3515, 3521, 3530, 3536, 3547,
+ 3553, 3562, 3568, 3588, 3594, 3603, 3609, 3620,
+ 3626, 3635, 3641, 3666, 3672, 3681, 3687, 3698,
+ 3704, 3713, 3719, 3739, 3745, 3754, 3760, 3771,
+ 3777, 3786, 3792, 3815, 3821, 3830, 3836, 3847,
+ 3853, 3862, 3868, 3888, 3894, 3903, 3909, 3920,
+ 3926, 3935, 3941, 3966, 3972, 3981, 3987, 3998,
+ 4004, 4013, 4019, 4039, 4045, 4054, 4060, 4071,
+ 4077, 4086, 4092, 3956, 3962, 3971, 3977, 3988,
+ 3994, 4003, 4009, 4029, 4035, 4044, 4050, 4061,
+ 4067, 4076, 4082, 4107, 4113, 4122, 4128, 4139,
+ 4145, 4154, 4160, 4180, 4186, 4195, 4201, 4212,
+ 4218, 4227, 4233, 4256, 4262, 4271, 4277, 4288,
+ 4294, 4303, 4309, 4329, 4335, 4344, 4350, 4361,
+ 4367, 4376, 4382, 4407, 4413, 4422, 4428, 4439,
+ 4445, 4454, 4460, 4480, 4486, 4495, 4501, 4512,
+ 4518, 4527, 4533, 4328, 4334, 4343, 4349, 4360,
+ 4366, 4375, 4381, 4401, 4407, 4416, 4422, 4433,
+ 4439, 4448, 4454, 4479, 4485, 4494, 4500, 4511,
+ 4517, 4526, 4532, 4552, 4558, 4567, 4573, 4584,
+ 4590, 4599, 4605, 4628, 4634, 4643, 4649, 4660,
+ 4666, 4675, 4681, 4701, 4707, 4716, 4722, 4733,
+ 4739, 4748, 4754, 4779, 4785, 4794, 4800, 4811,
+ 4817, 4826, 4832, 4852, 4858, 4867, 4873, 4884,
+ 4890, 4899, 4905, 4769, 4775, 4784, 4790, 4801,
+ 4807, 4816, 4822, 4842, 4848, 4857, 4863, 4874,
+ 4880, 4889, 4895, 4920, 4926, 4935, 4941, 4952,
+ 4958, 4967, 4973, 4993, 4999, 5008, 5014, 5025,
+ 5031, 5040, 5046, 5069, 5075, 5084, 5090, 5101,
+ 5107, 5116, 5122, 5142, 5148, 5157, 5163, 5174,
+ 5180, 5189, 5195, 5220, 5226, 5235, 5241, 5252,
+ 5258, 5267, 5273, 5293, 5299, 5308, 5314, 5325,
+ 5331, 5340, 5346, 4604, 4610, 4619, 4625, 4636,
+ 4642, 4651, 4657, 4677, 4683, 4692, 4698, 4709,
+ 4715, 4724, 4730, 4755, 4761, 4770, 4776, 4787,
+ 4793, 4802, 4808, 4828, 4834, 4843, 4849, 4860,
+ 4866, 4875, 4881, 4904, 4910, 4919, 4925, 4936,
+ 4942, 4951, 4957, 4977, 4983, 4992, 4998, 5009,
+ 5015, 5024, 5030, 5055, 5061, 5070, 5076, 5087,
+ 5093, 5102, 5108, 5128, 5134, 5143, 5149, 5160,
+ 5166, 5175, 5181, 5045, 5051, 5060, 5066, 5077,
+ 5083, 5092, 5098, 5118, 5124, 5133, 5139, 5150,
+ 5156, 5165, 5171, 5196, 5202, 5211, 5217, 5228,
+ 5234, 5243, 5249, 5269, 5275, 5284, 5290, 5301,
+ 5307, 5316, 5322, 5345, 5351, 5360, 5366, 5377,
+ 5383, 5392, 5398, 5418, 5424, 5433, 5439, 5450,
+ 5456, 5465, 5471, 5496, 5502, 5511, 5517, 5528,
+ 5534, 5543, 5549, 5569, 5575, 5584, 5590, 5601,
+ 5607, 5616, 5622, 5417, 5423, 5432, 5438, 5449,
+ 5455, 5464, 5470, 5490, 5496, 5505, 5511, 5522,
+ 5528, 5537, 5543, 5568, 5574, 5583, 5589, 5600,
+ 5606, 5615, 5621, 5641, 5647, 5656, 5662, 5673,
+ 5679, 5688, 5694, 5717, 5723, 5732, 5738, 5749,
+ 5755, 5764, 5770, 5790, 5796, 5805, 5811, 5822,
+ 5828, 5837, 5843, 5868, 5874, 5883, 5889, 5900,
+ 5906, 5915, 5921, 5941, 5947, 5956, 5962, 5973,
+ 5979, 5988, 5994, 5858, 5864, 5873, 5879, 5890,
+ 5896, 5905, 5911, 5931, 5937, 5946, 5952, 5963,
+ 5969, 5978, 5984, 6009, 6015, 6024, 6030, 6041,
+ 6047, 6056, 6062, 6082, 6088, 6097, 6103, 6114,
+ 6120, 6129, 6135, 6158, 6164, 6173, 6179, 6190,
+ 6196, 6205, 6211, 6231, 6237, 6246, 6252, 6263,
+ 6269, 6278, 6284, 6309, 6315, 6324, 6330, 6341,
+ 6347, 6356, 6362, 6382, 6388, 6397, 6403, 6414,
+ 6420, 6429, 6435, 3515, 3521, 3530, 3536, 3547,
+ 3553, 3562, 3568, 3588, 3594, 3603, 3609, 3620,
+ 3626, 3635, 3641, 3666, 3672, 3681, 3687, 3698,
+ 3704, 3713, 3719, 3739, 3745, 3754, 3760, 3771,
+ 3777, 3786, 3792, 3815, 3821, 3830, 3836, 3847,
+ 3853, 3862, 3868, 3888, 3894, 3903, 3909, 3920,
+ 3926, 3935, 3941, 3966, 3972, 3981, 3987, 3998,
+ 4004, 4013, 4019, 4039, 4045, 4054, 4060, 4071,
+ 4077, 4086, 4092, 3956, 3962, 3971, 3977, 3988,
+ 3994, 4003, 4009, 4029, 4035, 4044, 4050, 4061,
+ 4067, 4076, 4082, 4107, 4113, 4122, 4128, 4139,
+ 4145, 4154, 4160, 4180, 4186, 4195, 4201, 4212,
+ 4218, 4227, 4233, 4256, 4262, 4271, 4277, 4288,
+ 4294, 4303, 4309, 4329, 4335, 4344, 4350, 4361,
+ 4367, 4376, 4382, 4407, 4413, 4422, 4428, 4439,
+ 4445, 4454, 4460, 4480, 4486, 4495, 4501, 4512,
+ 4518, 4527, 4533, 4328, 4334, 4343, 4349, 4360,
+ 4366, 4375, 4381, 4401, 4407, 4416, 4422, 4433,
+ 4439, 4448, 4454, 4479, 4485, 4494, 4500, 4511,
+ 4517, 4526, 4532, 4552, 4558, 4567, 4573, 4584,
+ 4590, 4599, 4605, 4628, 4634, 4643, 4649, 4660,
+ 4666, 4675, 4681, 4701, 4707, 4716, 4722, 4733,
+ 4739, 4748, 4754, 4779, 4785, 4794, 4800, 4811,
+ 4817, 4826, 4832, 4852, 4858, 4867, 4873, 4884,
+ 4890, 4899, 4905, 4769, 4775, 4784, 4790, 4801,
+ 4807, 4816, 4822, 4842, 4848, 4857, 4863, 4874,
+ 4880, 4889, 4895, 4920, 4926, 4935, 4941, 4952,
+ 4958, 4967, 4973, 4993, 4999, 5008, 5014, 5025,
+ 5031, 5040, 5046, 5069, 5075, 5084, 5090, 5101,
+ 5107, 5116, 5122, 5142, 5148, 5157, 5163, 5174,
+ 5180, 5189, 5195, 5220, 5226, 5235, 5241, 5252,
+ 5258, 5267, 5273, 5293, 5299, 5308, 5314, 5325,
+ 5331, 5340, 5346, 4604, 4610, 4619, 4625, 4636,
+ 4642, 4651, 4657, 4677, 4683, 4692, 4698, 4709,
+ 4715, 4724, 4730, 4755, 4761, 4770, 4776, 4787,
+ 4793, 4802, 4808, 4828, 4834, 4843, 4849, 4860,
+ 4866, 4875, 4881, 4904, 4910, 4919, 4925, 4936,
+ 4942, 4951, 4957, 4977, 4983, 4992, 4998, 5009,
+ 5015, 5024, 5030, 5055, 5061, 5070, 5076, 5087,
+ 5093, 5102, 5108, 5128, 5134, 5143, 5149, 5160,
+ 5166, 5175, 5181, 5045, 5051, 5060, 5066, 5077,
+ 5083, 5092, 5098, 5118, 5124, 5133, 5139, 5150,
+ 5156, 5165, 5171, 5196, 5202, 5211, 5217, 5228,
+ 5234, 5243, 5249, 5269, 5275, 5284, 5290, 5301,
+ 5307, 5316, 5322, 5345, 5351, 5360, 5366, 5377,
+ 5383, 5392, 5398, 5418, 5424, 5433, 5439, 5450,
+ 5456, 5465, 5471, 5496, 5502, 5511, 5517, 5528,
+ 5534, 5543, 5549, 5569, 5575, 5584, 5590, 5601,
+ 5607, 5616, 5622, 5417, 5423, 5432, 5438, 5449,
+ 5455, 5464, 5470, 5490, 5496, 5505, 5511, 5522,
+ 5528, 5537, 5543, 5568, 5574, 5583, 5589, 5600,
+ 5606, 5615, 5621, 5641, 5647, 5656, 5662, 5673,
+ 5679, 5688, 5694, 5717, 5723, 5732, 5738, 5749,
+ 5755, 5764, 5770, 5790, 5796, 5805, 5811, 5822,
+ 5828, 5837, 5843, 5868, 5874, 5883, 5889, 5900,
+ 5906, 5915, 5921, 5941, 5947, 5956, 5962, 5973,
+ 5979, 5988, 5994, 5858, 5864, 5873, 5879, 5890,
+ 5896, 5905, 5911, 5931, 5937, 5946, 5952, 5963,
+ 5969, 5978, 5984, 6009, 6015, 6024, 6030, 6041,
+ 6047, 6056, 6062, 6082, 6088, 6097, 6103, 6114,
+ 6120, 6129, 6135, 6158, 6164, 6173, 6179, 6190,
+ 6196, 6205, 6211, 6231, 6237, 6246, 6252, 6263,
+ 6269, 6278, 6284, 6309, 6315, 6324, 6330, 6341,
+ 6347, 6356, 6362, 6382, 6388, 6397, 6403, 6414,
+ 6420, 6429, 6435, 5303, 5309, 5318, 5324, 5335,
+ 5341, 5350, 5356, 5376, 5382, 5391, 5397, 5408,
+ 5414, 5423, 5429, 5454, 5460, 5469, 5475, 5486,
+ 5492, 5501, 5507, 5527, 5533, 5542, 5548, 5559,
+ 5565, 5574, 5580, 5603, 5609, 5618, 5624, 5635,
+ 5641, 5650, 5656, 5676, 5682, 5691, 5697, 5708,
+ 5714, 5723, 5729, 5754, 5760, 5769, 5775, 5786,
+ 5792, 5801, 5807, 5827, 5833, 5842, 5848, 5859,
+ 5865, 5874, 5880, 5744, 5750, 5759, 5765, 5776,
+ 5782, 5791, 5797, 5817, 5823, 5832, 5838, 5849,
+ 5855, 5864, 5870, 5895, 5901, 5910, 5916, 5927,
+ 5933, 5942, 5948, 5968, 5974, 5983, 5989, 6000,
+ 6006, 6015, 6021, 6044, 6050, 6059, 6065, 6076,
+ 6082, 6091, 6097, 6117, 6123, 6132, 6138, 6149,
+ 6155, 6164, 6170, 6195, 6201, 6210, 6216, 6227,
+ 6233, 6242, 6248, 6268, 6274, 6283, 6289, 6300,
+ 6306, 6315, 6321, 6116, 6122, 6131, 6137, 6148,
+ 6154, 6163, 6169, 6189, 6195, 6204, 6210, 6221,
+ 6227, 6236, 6242, 6267, 6273, 6282, 6288, 6299,
+ 6305, 6314, 6320, 6340, 6346, 6355, 6361, 6372,
+ 6378, 6387, 6393, 6416, 6422, 6431, 6437, 6448,
+ 6454, 6463, 6469, 6489, 6495, 6504, 6510, 6521,
+ 6527, 6536, 6542, 6567, 6573, 6582, 6588, 6599,
+ 6605, 6614, 6620, 6640, 6646, 6655, 6661, 6672,
+ 6678, 6687, 6693, 6557, 6563, 6572, 6578, 6589,
+ 6595, 6604, 6610, 6630, 6636, 6645, 6651, 6662,
+ 6668, 6677, 6683, 6708, 6714, 6723, 6729, 6740,
+ 6746, 6755, 6761, 6781, 6787, 6796, 6802, 6813,
+ 6819, 6828, 6834, 6857, 6863, 6872, 6878, 6889,
+ 6895, 6904, 6910, 6930, 6936, 6945, 6951, 6962,
+ 6968, 6977, 6983, 7008, 7014, 7023, 7029, 7040,
+ 7046, 7055, 7061, 7081, 7087, 7096, 7102, 7113,
+ 7119, 7128, 7134, 6392, 6398, 6407, 6413, 6424,
+ 6430, 6439, 6445, 6465, 6471, 6480, 6486, 6497,
+ 6503, 6512, 6518, 6543, 6549, 6558, 6564, 6575,
+ 6581, 6590, 6596, 6616, 6622, 6631, 6637, 6648,
+ 6654, 6663, 6669, 6692, 6698, 6707, 6713, 6724,
+ 6730, 6739, 6745, 6765, 6771, 6780, 6786, 6797,
+ 6803, 6812, 6818, 6843, 6849, 6858, 6864, 6875,
+ 6881, 6890, 6896, 6916, 6922, 6931, 6937, 6948,
+ 6954, 6963, 6969, 6833, 6839, 6848, 6854, 6865,
+ 6871, 6880, 6886, 6906, 6912, 6921, 6927, 6938,
+ 6944, 6953, 6959, 6984, 6990, 6999, 7005, 7016,
+ 7022, 7031, 7037, 7057, 7063, 7072, 7078, 7089,
+ 7095, 7104, 7110, 7133, 7139, 7148, 7154, 7165,
+ 7171, 7180, 7186, 7206, 7212, 7221, 7227, 7238,
+ 7244, 7253, 7259, 7284, 7290, 7299, 7305, 7316,
+ 7322, 7331, 7337, 7357, 7363, 7372, 7378, 7389,
+ 7395, 7404, 7410, 7205, 7211, 7220, 7226, 7237,
+ 7243, 7252, 7258, 7278, 7284, 7293, 7299, 7310,
+ 7316, 7325, 7331, 7356, 7362, 7371, 7377, 7388,
+ 7394, 7403, 7409, 7429, 7435, 7444, 7450, 7461,
+ 7467, 7476, 7482, 7505, 7511, 7520, 7526, 7537,
+ 7543, 7552, 7558, 7578, 7584, 7593, 7599, 7610,
+ 7616, 7625, 7631, 7656, 7662, 7671, 7677, 7688,
+ 7694, 7703, 7709, 7729, 7735, 7744, 7750, 7761
+};
+
+static int VariableLevelCost(int level, const uint8_t probas[NUM_PROBAS]) {
+ int pattern = VP8LevelCodes[level - 1][0];
+ int bits = VP8LevelCodes[level - 1][1];
+ int cost = 0;
+ int i;
+ for (i = 2; pattern; ++i) {
+ if (pattern & 1) {
+ cost += VP8BitCost(bits & 1, probas[i]);
+ }
+ bits >>= 1;
+ pattern >>= 1;
+ }
+ return cost;
+}
+
+//------------------------------------------------------------------------------
+// Pre-calc level costs once for all
+
+void VP8CalculateLevelCosts(VP8Proba* const proba) {
+ int ctype, band, ctx;
+
+ if (!proba->dirty_) return; // nothing to do.
+
+ for (ctype = 0; ctype < NUM_TYPES; ++ctype) {
+ for (band = 0; band < NUM_BANDS; ++band) {
+ for (ctx = 0; ctx < NUM_CTX; ++ctx) {
+ const uint8_t* const p = proba->coeffs_[ctype][band][ctx];
+ uint16_t* const table = proba->level_cost_[ctype][band][ctx];
+ const int cost_base = VP8BitCost(1, p[1]);
+ int v;
+ table[0] = VP8BitCost(0, p[1]);
+ for (v = 1; v <= MAX_VARIABLE_LEVEL; ++v) {
+ table[v] = cost_base + VariableLevelCost(v, p);
+ }
+ // Starting at level 67 and up, the variable part of the cost is
+ // actually constant.
+ }
+ }
+ }
+ proba->dirty_ = 0;
+}
+
+//------------------------------------------------------------------------------
+// Mode cost tables.
+
+// These are the fixed probabilities (in the coding trees) turned into bit-cost
+// by calling VP8BitCost().
+const uint16_t VP8FixedCostsUV[4] = { 302, 984, 439, 642 };
+// note: these values include the fixed VP8BitCost(1, 145) mode selection cost.
+const uint16_t VP8FixedCostsI16[4] = { 663, 919, 872, 919 };
+const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES] = {
+ { { 40, 1151, 1723, 1874, 2103, 2019, 1628, 1777, 2226, 2137 },
+ { 192, 469, 1296, 1308, 1849, 1794, 1781, 1703, 1713, 1522 },
+ { 142, 910, 762, 1684, 1849, 1576, 1460, 1305, 1801, 1657 },
+ { 559, 641, 1370, 421, 1182, 1569, 1612, 1725, 863, 1007 },
+ { 299, 1059, 1256, 1108, 636, 1068, 1581, 1883, 869, 1142 },
+ { 277, 1111, 707, 1362, 1089, 672, 1603, 1541, 1545, 1291 },
+ { 214, 781, 1609, 1303, 1632, 2229, 726, 1560, 1713, 918 },
+ { 152, 1037, 1046, 1759, 1983, 2174, 1358, 742, 1740, 1390 },
+ { 512, 1046, 1420, 753, 752, 1297, 1486, 1613, 460, 1207 },
+ { 424, 827, 1362, 719, 1462, 1202, 1199, 1476, 1199, 538 } },
+ { { 240, 402, 1134, 1491, 1659, 1505, 1517, 1555, 1979, 2099 },
+ { 467, 242, 960, 1232, 1714, 1620, 1834, 1570, 1676, 1391 },
+ { 500, 455, 463, 1507, 1699, 1282, 1564, 982, 2114, 2114 },
+ { 672, 643, 1372, 331, 1589, 1667, 1453, 1938, 996, 876 },
+ { 458, 783, 1037, 911, 738, 968, 1165, 1518, 859, 1033 },
+ { 504, 815, 504, 1139, 1219, 719, 1506, 1085, 1268, 1268 },
+ { 333, 630, 1445, 1239, 1883, 3672, 799, 1548, 1865, 598 },
+ { 399, 644, 746, 1342, 1856, 1350, 1493, 613, 1855, 1015 },
+ { 622, 749, 1205, 608, 1066, 1408, 1290, 1406, 546, 971 },
+ { 500, 753, 1041, 668, 1230, 1617, 1297, 1425, 1383, 523 } },
+ { { 394, 553, 523, 1502, 1536, 981, 1608, 1142, 1666, 2181 },
+ { 655, 430, 375, 1411, 1861, 1220, 1677, 1135, 1978, 1553 },
+ { 690, 640, 245, 1954, 2070, 1194, 1528, 982, 1972, 2232 },
+ { 559, 834, 741, 867, 1131, 980, 1225, 852, 1092, 784 },
+ { 690, 875, 516, 959, 673, 894, 1056, 1190, 1528, 1126 },
+ { 740, 951, 384, 1277, 1177, 492, 1579, 1155, 1846, 1513 },
+ { 323, 775, 1062, 1776, 3062, 1274, 813, 1188, 1372, 655 },
+ { 488, 971, 484, 1767, 1515, 1775, 1115, 503, 1539, 1461 },
+ { 740, 1006, 998, 709, 851, 1230, 1337, 788, 741, 721 },
+ { 522, 1073, 573, 1045, 1346, 887, 1046, 1146, 1203, 697 } },
+ { { 105, 864, 1442, 1009, 1934, 1840, 1519, 1920, 1673, 1579 },
+ { 534, 305, 1193, 683, 1388, 2164, 1802, 1894, 1264, 1170 },
+ { 305, 518, 877, 1108, 1426, 3215, 1425, 1064, 1320, 1242 },
+ { 683, 732, 1927, 257, 1493, 2048, 1858, 1552, 1055, 947 },
+ { 394, 814, 1024, 660, 959, 1556, 1282, 1289, 893, 1047 },
+ { 528, 615, 996, 940, 1201, 635, 1094, 2515, 803, 1358 },
+ { 347, 614, 1609, 1187, 3133, 1345, 1007, 1339, 1017, 667 },
+ { 218, 740, 878, 1605, 3650, 3650, 1345, 758, 1357, 1617 },
+ { 672, 750, 1541, 558, 1257, 1599, 1870, 2135, 402, 1087 },
+ { 592, 684, 1161, 430, 1092, 1497, 1475, 1489, 1095, 822 } },
+ { { 228, 1056, 1059, 1368, 752, 982, 1512, 1518, 987, 1782 },
+ { 494, 514, 818, 942, 965, 892, 1610, 1356, 1048, 1363 },
+ { 512, 648, 591, 1042, 761, 991, 1196, 1454, 1309, 1463 },
+ { 683, 749, 1043, 676, 841, 1396, 1133, 1138, 654, 939 },
+ { 622, 1101, 1126, 994, 361, 1077, 1203, 1318, 877, 1219 },
+ { 631, 1068, 857, 1650, 651, 477, 1650, 1419, 828, 1170 },
+ { 555, 727, 1068, 1335, 3127, 1339, 820, 1331, 1077, 429 },
+ { 504, 879, 624, 1398, 889, 889, 1392, 808, 891, 1406 },
+ { 683, 1602, 1289, 977, 578, 983, 1280, 1708, 406, 1122 },
+ { 399, 865, 1433, 1070, 1072, 764, 968, 1477, 1223, 678 } },
+ { { 333, 760, 935, 1638, 1010, 529, 1646, 1410, 1472, 2219 },
+ { 512, 494, 750, 1160, 1215, 610, 1870, 1868, 1628, 1169 },
+ { 572, 646, 492, 1934, 1208, 603, 1580, 1099, 1398, 1995 },
+ { 786, 789, 942, 581, 1018, 951, 1599, 1207, 731, 768 },
+ { 690, 1015, 672, 1078, 582, 504, 1693, 1438, 1108, 2897 },
+ { 768, 1267, 571, 2005, 1243, 244, 2881, 1380, 1786, 1453 },
+ { 452, 899, 1293, 903, 1311, 3100, 465, 1311, 1319, 813 },
+ { 394, 927, 942, 1103, 1358, 1104, 946, 593, 1363, 1109 },
+ { 559, 1005, 1007, 1016, 658, 1173, 1021, 1164, 623, 1028 },
+ { 564, 796, 632, 1005, 1014, 863, 2316, 1268, 938, 764 } },
+ { { 266, 606, 1098, 1228, 1497, 1243, 948, 1030, 1734, 1461 },
+ { 366, 585, 901, 1060, 1407, 1247, 876, 1134, 1620, 1054 },
+ { 452, 565, 542, 1729, 1479, 1479, 1016, 886, 2938, 1150 },
+ { 555, 1088, 1533, 950, 1354, 895, 834, 1019, 1021, 496 },
+ { 704, 815, 1193, 971, 973, 640, 1217, 2214, 832, 578 },
+ { 672, 1245, 579, 871, 875, 774, 872, 1273, 1027, 949 },
+ { 296, 1134, 2050, 1784, 1636, 3425, 442, 1550, 2076, 722 },
+ { 342, 982, 1259, 1846, 1848, 1848, 622, 568, 1847, 1052 },
+ { 555, 1064, 1304, 828, 746, 1343, 1075, 1329, 1078, 494 },
+ { 288, 1167, 1285, 1174, 1639, 1639, 833, 2254, 1304, 509 } },
+ { { 342, 719, 767, 1866, 1757, 1270, 1246, 550, 1746, 2151 },
+ { 483, 653, 694, 1509, 1459, 1410, 1218, 507, 1914, 1266 },
+ { 488, 757, 447, 2979, 1813, 1268, 1654, 539, 1849, 2109 },
+ { 522, 1097, 1085, 851, 1365, 1111, 851, 901, 961, 605 },
+ { 709, 716, 841, 728, 736, 945, 941, 862, 2845, 1057 },
+ { 512, 1323, 500, 1336, 1083, 681, 1342, 717, 1604, 1350 },
+ { 452, 1155, 1372, 1900, 1501, 3290, 311, 944, 1919, 922 },
+ { 403, 1520, 977, 2132, 1733, 3522, 1076, 276, 3335, 1547 },
+ { 559, 1374, 1101, 615, 673, 2462, 974, 795, 984, 984 },
+ { 547, 1122, 1062, 812, 1410, 951, 1140, 622, 1268, 651 } },
+ { { 165, 982, 1235, 938, 1334, 1366, 1659, 1578, 964, 1612 },
+ { 592, 422, 925, 847, 1139, 1112, 1387, 2036, 861, 1041 },
+ { 403, 837, 732, 770, 941, 1658, 1250, 809, 1407, 1407 },
+ { 896, 874, 1071, 381, 1568, 1722, 1437, 2192, 480, 1035 },
+ { 640, 1098, 1012, 1032, 684, 1382, 1581, 2106, 416, 865 },
+ { 559, 1005, 819, 914, 710, 770, 1418, 920, 838, 1435 },
+ { 415, 1258, 1245, 870, 1278, 3067, 770, 1021, 1287, 522 },
+ { 406, 990, 601, 1009, 1265, 1265, 1267, 759, 1017, 1277 },
+ { 968, 1182, 1329, 788, 1032, 1292, 1705, 1714, 203, 1403 },
+ { 732, 877, 1279, 471, 901, 1161, 1545, 1294, 755, 755 } },
+ { { 111, 931, 1378, 1185, 1933, 1648, 1148, 1714, 1873, 1307 },
+ { 406, 414, 1030, 1023, 1910, 1404, 1313, 1647, 1509, 793 },
+ { 342, 640, 575, 1088, 1241, 1349, 1161, 1350, 1756, 1502 },
+ { 559, 766, 1185, 357, 1682, 1428, 1329, 1897, 1219, 802 },
+ { 473, 909, 1164, 771, 719, 2508, 1427, 1432, 722, 782 },
+ { 342, 892, 785, 1145, 1150, 794, 1296, 1550, 973, 1057 },
+ { 208, 1036, 1326, 1343, 1606, 3395, 815, 1455, 1618, 712 },
+ { 228, 928, 890, 1046, 3499, 1711, 994, 829, 1720, 1318 },
+ { 768, 724, 1058, 636, 991, 1075, 1319, 1324, 616, 825 },
+ { 305, 1167, 1358, 899, 1587, 1587, 987, 1988, 1332, 501 } }
+};
+
+//------------------------------------------------------------------------------
+
diff --git a/src/3rdparty/libwebp/src/enc/cost.h b/src/3rdparty/libwebp/src/enc/cost.h
new file mode 100644
index 0000000..3cbad1a
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/cost.h
@@ -0,0 +1,51 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Cost tables for level and modes.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_ENC_COST_H_
+#define WEBP_ENC_COST_H_
+
+#include "./vp8enci.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// approximate cost per level:
+extern const uint16_t VP8LevelFixedCosts[MAX_LEVEL + 1];
+extern const uint16_t VP8EntropyCost[256]; // 8bit fixed-point log(p)
+
+// Cost of coding one event with probability 'proba'.
+static WEBP_INLINE int VP8BitCost(int bit, uint8_t proba) {
+ return !bit ? VP8EntropyCost[proba] : VP8EntropyCost[255 - proba];
+}
+
+// Level cost calculations
+extern const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2];
+void VP8CalculateLevelCosts(VP8Proba* const proba);
+static WEBP_INLINE int VP8LevelCost(const uint16_t* const table, int level) {
+ return VP8LevelFixedCosts[level]
+ + table[(level > MAX_VARIABLE_LEVEL) ? MAX_VARIABLE_LEVEL : level];
+}
+
+// Mode costs
+extern const uint16_t VP8FixedCostsUV[4];
+extern const uint16_t VP8FixedCostsI16[4];
+extern const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES];
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_ENC_COST_H_ */
diff --git a/src/3rdparty/libwebp/src/enc/filter.c b/src/3rdparty/libwebp/src/enc/filter.c
new file mode 100644
index 0000000..dd27804
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/filter.c
@@ -0,0 +1,471 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Selecting filter level
+//
+// Author: somnath@google.com (Somnath Banerjee)
+
+#include <assert.h>
+#include "./vp8enci.h"
+
+// This table gives, for a given sharpness, the filtering strength to be
+// used (at least) in order to filter a given edge step delta.
+// This is constructed by brute force inspection: for all delta, we iterate
+// over all possible filtering strength / thresh until needs_filter() returns
+// true.
+#define MAX_DELTA_SIZE 64
+static const uint8_t kLevelsFromDelta[8][MAX_DELTA_SIZE] = {
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 },
+ { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 17, 18,
+ 20, 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42,
+ 44, 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
+ { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 19,
+ 20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43,
+ 44, 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
+ { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19,
+ 21, 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43,
+ 45, 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
+ { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20,
+ 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44,
+ 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
+ { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 17, 19, 20,
+ 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44,
+ 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
+ { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19, 21,
+ 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43, 45,
+ 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
+ { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20, 21,
+ 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45,
+ 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }
+};
+
+int VP8FilterStrengthFromDelta(int sharpness, int delta) {
+ const int pos = (delta < MAX_DELTA_SIZE) ? delta : MAX_DELTA_SIZE - 1;
+ assert(sharpness >= 0 && sharpness <= 7);
+ return kLevelsFromDelta[sharpness][pos];
+}
+
+// -----------------------------------------------------------------------------
+// NOTE: clip1, tables and InitTables are repeated entries of dsp.c
+static uint8_t abs0[255 + 255 + 1]; // abs(i)
+static uint8_t abs1[255 + 255 + 1]; // abs(i)>>1
+static int8_t sclip1[1020 + 1020 + 1]; // clips [-1020, 1020] to [-128, 127]
+static int8_t sclip2[112 + 112 + 1]; // clips [-112, 112] to [-16, 15]
+static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255]
+
+static int tables_ok = 0;
+
+static void InitTables(void) {
+ if (!tables_ok) {
+ int i;
+ for (i = -255; i <= 255; ++i) {
+ abs0[255 + i] = (i < 0) ? -i : i;
+ abs1[255 + i] = abs0[255 + i] >> 1;
+ }
+ for (i = -1020; i <= 1020; ++i) {
+ sclip1[1020 + i] = (i < -128) ? -128 : (i > 127) ? 127 : i;
+ }
+ for (i = -112; i <= 112; ++i) {
+ sclip2[112 + i] = (i < -16) ? -16 : (i > 15) ? 15 : i;
+ }
+ for (i = -255; i <= 255 + 255; ++i) {
+ clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i;
+ }
+ tables_ok = 1;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Edge filtering functions
+
+// 4 pixels in, 2 pixels out
+static WEBP_INLINE void do_filter2(uint8_t* p, int step) {
+ const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
+ const int a = 3 * (q0 - p0) + sclip1[1020 + p1 - q1];
+ const int a1 = sclip2[112 + ((a + 4) >> 3)];
+ const int a2 = sclip2[112 + ((a + 3) >> 3)];
+ p[-step] = clip1[255 + p0 + a2];
+ p[ 0] = clip1[255 + q0 - a1];
+}
+
+// 4 pixels in, 4 pixels out
+static WEBP_INLINE void do_filter4(uint8_t* p, int step) {
+ const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
+ const int a = 3 * (q0 - p0);
+ const int a1 = sclip2[112 + ((a + 4) >> 3)];
+ const int a2 = sclip2[112 + ((a + 3) >> 3)];
+ const int a3 = (a1 + 1) >> 1;
+ p[-2*step] = clip1[255 + p1 + a3];
+ p[- step] = clip1[255 + p0 + a2];
+ p[ 0] = clip1[255 + q0 - a1];
+ p[ step] = clip1[255 + q1 - a3];
+}
+
+// high edge-variance
+static WEBP_INLINE int hev(const uint8_t* p, int step, int thresh) {
+ const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
+ return (abs0[255 + p1 - p0] > thresh) || (abs0[255 + q1 - q0] > thresh);
+}
+
+static WEBP_INLINE int needs_filter(const uint8_t* p, int step, int thresh) {
+ const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
+ return (2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) <= thresh;
+}
+
+static WEBP_INLINE int needs_filter2(const uint8_t* p,
+ int step, int t, int it) {
+ const int p3 = p[-4*step], p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step];
+ const int q0 = p[0], q1 = p[step], q2 = p[2*step], q3 = p[3*step];
+ if ((2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) > t)
+ return 0;
+ return abs0[255 + p3 - p2] <= it && abs0[255 + p2 - p1] <= it &&
+ abs0[255 + p1 - p0] <= it && abs0[255 + q3 - q2] <= it &&
+ abs0[255 + q2 - q1] <= it && abs0[255 + q1 - q0] <= it;
+}
+
+//------------------------------------------------------------------------------
+// Simple In-loop filtering (Paragraph 15.2)
+
+static void SimpleVFilter16(uint8_t* p, int stride, int thresh) {
+ int i;
+ for (i = 0; i < 16; ++i) {
+ if (needs_filter(p + i, stride, thresh)) {
+ do_filter2(p + i, stride);
+ }
+ }
+}
+
+static void SimpleHFilter16(uint8_t* p, int stride, int thresh) {
+ int i;
+ for (i = 0; i < 16; ++i) {
+ if (needs_filter(p + i * stride, 1, thresh)) {
+ do_filter2(p + i * stride, 1);
+ }
+ }
+}
+
+static void SimpleVFilter16i(uint8_t* p, int stride, int thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4 * stride;
+ SimpleVFilter16(p, stride, thresh);
+ }
+}
+
+static void SimpleHFilter16i(uint8_t* p, int stride, int thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4;
+ SimpleHFilter16(p, stride, thresh);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Complex In-loop filtering (Paragraph 15.3)
+
+static WEBP_INLINE void FilterLoop24(uint8_t* p,
+ int hstride, int vstride, int size,
+ int thresh, int ithresh, int hev_thresh) {
+ while (size-- > 0) {
+ if (needs_filter2(p, hstride, thresh, ithresh)) {
+ if (hev(p, hstride, hev_thresh)) {
+ do_filter2(p, hstride);
+ } else {
+ do_filter4(p, hstride);
+ }
+ }
+ p += vstride;
+ }
+}
+
+// on three inner edges
+static void VFilter16i(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4 * stride;
+ FilterLoop24(p, stride, 1, 16, thresh, ithresh, hev_thresh);
+ }
+}
+
+static void HFilter16i(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4;
+ FilterLoop24(p, 1, stride, 16, thresh, ithresh, hev_thresh);
+ }
+}
+
+static void VFilter8i(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop24(u + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
+ FilterLoop24(v + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
+}
+
+static void HFilter8i(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop24(u + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
+ FilterLoop24(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
+}
+
+//------------------------------------------------------------------------------
+
+void (*VP8EncVFilter16i)(uint8_t*, int, int, int, int) = VFilter16i;
+void (*VP8EncHFilter16i)(uint8_t*, int, int, int, int) = HFilter16i;
+void (*VP8EncVFilter8i)(uint8_t*, uint8_t*, int, int, int, int) = VFilter8i;
+void (*VP8EncHFilter8i)(uint8_t*, uint8_t*, int, int, int, int) = HFilter8i;
+
+void (*VP8EncSimpleVFilter16i)(uint8_t*, int, int) = SimpleVFilter16i;
+void (*VP8EncSimpleHFilter16i)(uint8_t*, int, int) = SimpleHFilter16i;
+
+//------------------------------------------------------------------------------
+// Paragraph 15.4: compute the inner-edge filtering strength
+
+static int GetILevel(int sharpness, int level) {
+ if (sharpness > 0) {
+ if (sharpness > 4) {
+ level >>= 2;
+ } else {
+ level >>= 1;
+ }
+ if (level > 9 - sharpness) {
+ level = 9 - sharpness;
+ }
+ }
+ if (level < 1) level = 1;
+ return level;
+}
+
+static void DoFilter(const VP8EncIterator* const it, int level) {
+ const VP8Encoder* const enc = it->enc_;
+ const int ilevel = GetILevel(enc->config_->filter_sharpness, level);
+ const int limit = 2 * level + ilevel;
+
+ uint8_t* const y_dst = it->yuv_out2_ + Y_OFF;
+ uint8_t* const u_dst = it->yuv_out2_ + U_OFF;
+ uint8_t* const v_dst = it->yuv_out2_ + V_OFF;
+
+ // copy current block to yuv_out2_
+ memcpy(y_dst, it->yuv_out_, YUV_SIZE * sizeof(uint8_t));
+
+ if (enc->filter_hdr_.simple_ == 1) { // simple
+ VP8EncSimpleHFilter16i(y_dst, BPS, limit);
+ VP8EncSimpleVFilter16i(y_dst, BPS, limit);
+ } else { // complex
+ const int hev_thresh = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
+ VP8EncHFilter16i(y_dst, BPS, limit, ilevel, hev_thresh);
+ VP8EncHFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh);
+ VP8EncVFilter16i(y_dst, BPS, limit, ilevel, hev_thresh);
+ VP8EncVFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh);
+ }
+}
+
+//------------------------------------------------------------------------------
+// SSIM metric
+
+enum { KERNEL = 3 };
+static const double kMinValue = 1.e-10; // minimal threshold
+
+void VP8SSIMAddStats(const DistoStats* const src, DistoStats* const dst) {
+ dst->w += src->w;
+ dst->xm += src->xm;
+ dst->ym += src->ym;
+ dst->xxm += src->xxm;
+ dst->xym += src->xym;
+ dst->yym += src->yym;
+}
+
+static void VP8SSIMAccumulate(const uint8_t* src1, int stride1,
+ const uint8_t* src2, int stride2,
+ int xo, int yo, int W, int H,
+ DistoStats* const stats) {
+ const int ymin = (yo - KERNEL < 0) ? 0 : yo - KERNEL;
+ const int ymax = (yo + KERNEL > H - 1) ? H - 1 : yo + KERNEL;
+ const int xmin = (xo - KERNEL < 0) ? 0 : xo - KERNEL;
+ const int xmax = (xo + KERNEL > W - 1) ? W - 1 : xo + KERNEL;
+ int x, y;
+ src1 += ymin * stride1;
+ src2 += ymin * stride2;
+ for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) {
+ for (x = xmin; x <= xmax; ++x) {
+ const int s1 = src1[x];
+ const int s2 = src2[x];
+ stats->w += 1;
+ stats->xm += s1;
+ stats->ym += s2;
+ stats->xxm += s1 * s1;
+ stats->xym += s1 * s2;
+ stats->yym += s2 * s2;
+ }
+ }
+}
+
+double VP8SSIMGet(const DistoStats* const stats) {
+ const double xmxm = stats->xm * stats->xm;
+ const double ymym = stats->ym * stats->ym;
+ const double xmym = stats->xm * stats->ym;
+ const double w2 = stats->w * stats->w;
+ double sxx = stats->xxm * stats->w - xmxm;
+ double syy = stats->yym * stats->w - ymym;
+ double sxy = stats->xym * stats->w - xmym;
+ double C1, C2;
+ double fnum;
+ double fden;
+ // small errors are possible, due to rounding. Clamp to zero.
+ if (sxx < 0.) sxx = 0.;
+ if (syy < 0.) syy = 0.;
+ C1 = 6.5025 * w2;
+ C2 = 58.5225 * w2;
+ fnum = (2 * xmym + C1) * (2 * sxy + C2);
+ fden = (xmxm + ymym + C1) * (sxx + syy + C2);
+ return (fden != 0.) ? fnum / fden : kMinValue;
+}
+
+double VP8SSIMGetSquaredError(const DistoStats* const s) {
+ if (s->w > 0.) {
+ const double iw2 = 1. / (s->w * s->w);
+ const double sxx = s->xxm * s->w - s->xm * s->xm;
+ const double syy = s->yym * s->w - s->ym * s->ym;
+ const double sxy = s->xym * s->w - s->xm * s->ym;
+ const double SSE = iw2 * (sxx + syy - 2. * sxy);
+ if (SSE > kMinValue) return SSE;
+ }
+ return kMinValue;
+}
+
+void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1,
+ const uint8_t* src2, int stride2,
+ int W, int H, DistoStats* const stats) {
+ int x, y;
+ for (y = 0; y < H; ++y) {
+ for (x = 0; x < W; ++x) {
+ VP8SSIMAccumulate(src1, stride1, src2, stride2, x, y, W, H, stats);
+ }
+ }
+}
+
+static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) {
+ int x, y;
+ DistoStats s = { .0, .0, .0, .0, .0, .0 };
+
+ // compute SSIM in a 10 x 10 window
+ for (x = 3; x < 13; x++) {
+ for (y = 3; y < 13; y++) {
+ VP8SSIMAccumulate(yuv1 + Y_OFF, BPS, yuv2 + Y_OFF, BPS, x, y, 16, 16, &s);
+ }
+ }
+ for (x = 1; x < 7; x++) {
+ for (y = 1; y < 7; y++) {
+ VP8SSIMAccumulate(yuv1 + U_OFF, BPS, yuv2 + U_OFF, BPS, x, y, 8, 8, &s);
+ VP8SSIMAccumulate(yuv1 + V_OFF, BPS, yuv2 + V_OFF, BPS, x, y, 8, 8, &s);
+ }
+ }
+ return VP8SSIMGet(&s);
+}
+
+//------------------------------------------------------------------------------
+// Exposed APIs: Encoder should call the following 3 functions to adjust
+// loop filter strength
+
+void VP8InitFilter(VP8EncIterator* const it) {
+ if (it->lf_stats_ != NULL) {
+ int s, i;
+ InitTables();
+ for (s = 0; s < NUM_MB_SEGMENTS; s++) {
+ for (i = 0; i < MAX_LF_LEVELS; i++) {
+ (*it->lf_stats_)[s][i] = 0;
+ }
+ }
+ }
+}
+
+void VP8StoreFilterStats(VP8EncIterator* const it) {
+ int d;
+ VP8Encoder* const enc = it->enc_;
+ const int s = it->mb_->segment_;
+ const int level0 = enc->dqm_[s].fstrength_; // TODO: ref_lf_delta[]
+
+ // explore +/-quant range of values around level0
+ const int delta_min = -enc->dqm_[s].quant_;
+ const int delta_max = enc->dqm_[s].quant_;
+ const int step_size = (delta_max - delta_min >= 4) ? 4 : 1;
+
+ if (it->lf_stats_ == NULL) return;
+
+ // NOTE: Currently we are applying filter only across the sublock edges
+ // There are two reasons for that.
+ // 1. Applying filter on macro block edges will change the pixels in
+ // the left and top macro blocks. That will be hard to restore
+ // 2. Macro Blocks on the bottom and right are not yet compressed. So we
+ // cannot apply filter on the right and bottom macro block edges.
+ if (it->mb_->type_ == 1 && it->mb_->skip_) return;
+
+ // Always try filter level zero
+ (*it->lf_stats_)[s][0] += GetMBSSIM(it->yuv_in_, it->yuv_out_);
+
+ for (d = delta_min; d <= delta_max; d += step_size) {
+ const int level = level0 + d;
+ if (level <= 0 || level >= MAX_LF_LEVELS) {
+ continue;
+ }
+ DoFilter(it, level);
+ (*it->lf_stats_)[s][level] += GetMBSSIM(it->yuv_in_, it->yuv_out2_);
+ }
+}
+
+void VP8AdjustFilterStrength(VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+ if (it->lf_stats_ != NULL) {
+ int s;
+ for (s = 0; s < NUM_MB_SEGMENTS; s++) {
+ int i, best_level = 0;
+ // Improvement over filter level 0 should be at least 1e-5 (relatively)
+ double best_v = 1.00001 * (*it->lf_stats_)[s][0];
+ for (i = 1; i < MAX_LF_LEVELS; i++) {
+ const double v = (*it->lf_stats_)[s][i];
+ if (v > best_v) {
+ best_v = v;
+ best_level = i;
+ }
+ }
+ enc->dqm_[s].fstrength_ = best_level;
+ }
+ } else if (enc->config_->filter_strength > 0) {
+ int max_level = 0;
+ int s;
+ for (s = 0; s < NUM_MB_SEGMENTS; s++) {
+ VP8SegmentInfo* const dqm = &enc->dqm_[s];
+ // this '>> 3' accounts for some inverse WHT scaling
+ const int delta = (dqm->max_edge_ * dqm->y2_.q_[1]) >> 3;
+ const int level =
+ VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, delta);
+ if (level > dqm->fstrength_) {
+ dqm->fstrength_ = level;
+ }
+ if (max_level < dqm->fstrength_) {
+ max_level = dqm->fstrength_;
+ }
+ }
+ enc->filter_hdr_.level_ = max_level;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
diff --git a/src/3rdparty/libwebp/src/enc/frame.c b/src/3rdparty/libwebp/src/enc/frame.c
new file mode 100644
index 0000000..2582244
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/frame.c
@@ -0,0 +1,1068 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// frame coding and analysis
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "./vp8enci.h"
+#include "./cost.h"
+#include "../webp/format_constants.h" // RIFF constants
+
+#define SEGMENT_VISU 0
+#define DEBUG_SEARCH 0 // useful to track search convergence
+
+// On-the-fly info about the current set of residuals. Handy to avoid
+// passing zillions of params.
+typedef struct {
+ int first;
+ int last;
+ const int16_t* coeffs;
+
+ int coeff_type;
+ ProbaArray* prob;
+ StatsArray* stats;
+ CostArray* cost;
+} VP8Residual;
+
+//------------------------------------------------------------------------------
+// multi-pass convergence
+
+#define HEADER_SIZE_ESTIMATE (RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + \
+ VP8_FRAME_HEADER_SIZE)
+#define DQ_LIMIT 0.4 // convergence is considered reached if dq < DQ_LIMIT
+// we allow 2k of extra head-room in PARTITION0 limit.
+#define PARTITION0_SIZE_LIMIT ((VP8_MAX_PARTITION0_SIZE - 2048ULL) << 11)
+
+typedef struct { // struct for organizing convergence in either size or PSNR
+ int is_first;
+ float dq;
+ float q, last_q;
+ double value, last_value; // PSNR or size
+ double target;
+ int do_size_search;
+} PassStats;
+
+static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) {
+ const uint64_t target_size = (uint64_t)enc->config_->target_size;
+ const int do_size_search = (target_size != 0);
+ const float target_PSNR = enc->config_->target_PSNR;
+
+ s->is_first = 1;
+ s->dq = 10.f;
+ s->q = s->last_q = enc->config_->quality;
+ s->target = do_size_search ? (double)target_size
+ : (target_PSNR > 0.) ? target_PSNR
+ : 40.; // default, just in case
+ s->value = s->last_value = 0.;
+ s->do_size_search = do_size_search;
+ return do_size_search;
+}
+
+static float Clamp(float v, float min, float max) {
+ return (v < min) ? min : (v > max) ? max : v;
+}
+
+static float ComputeNextQ(PassStats* const s) {
+ float dq;
+ if (s->is_first) {
+ dq = (s->value > s->target) ? -s->dq : s->dq;
+ s->is_first = 0;
+ } else if (s->value != s->last_value) {
+ const double slope = (s->target - s->value) / (s->last_value - s->value);
+ dq = (float)(slope * (s->last_q - s->q));
+ } else {
+ dq = 0.; // we're done?!
+ }
+ // Limit variable to avoid large swings.
+ s->dq = Clamp(dq, -30.f, 30.f);
+ s->last_q = s->q;
+ s->last_value = s->value;
+ s->q = Clamp(s->q + s->dq, 0.f, 100.f);
+ return s->q;
+}
+
+//------------------------------------------------------------------------------
+// Tables for level coding
+
+const uint8_t VP8EncBands[16 + 1] = {
+ 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
+ 0 // sentinel
+};
+
+const uint8_t VP8Cat3[] = { 173, 148, 140 };
+const uint8_t VP8Cat4[] = { 176, 155, 140, 135 };
+const uint8_t VP8Cat5[] = { 180, 157, 141, 134, 130 };
+const uint8_t VP8Cat6[] =
+ { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129 };
+
+//------------------------------------------------------------------------------
+// Reset the statistics about: number of skips, token proba, level cost,...
+
+static void ResetStats(VP8Encoder* const enc) {
+ VP8Proba* const proba = &enc->proba_;
+ VP8CalculateLevelCosts(proba);
+ proba->nb_skip_ = 0;
+}
+
+//------------------------------------------------------------------------------
+// Skip decision probability
+
+#define SKIP_PROBA_THRESHOLD 250 // value below which using skip_proba is OK.
+
+static int CalcSkipProba(uint64_t nb, uint64_t total) {
+ return (int)(total ? (total - nb) * 255 / total : 255);
+}
+
+// Returns the bit-cost for coding the skip probability.
+static int FinalizeSkipProba(VP8Encoder* const enc) {
+ VP8Proba* const proba = &enc->proba_;
+ const int nb_mbs = enc->mb_w_ * enc->mb_h_;
+ const int nb_events = proba->nb_skip_;
+ int size;
+ proba->skip_proba_ = CalcSkipProba(nb_events, nb_mbs);
+ proba->use_skip_proba_ = (proba->skip_proba_ < SKIP_PROBA_THRESHOLD);
+ size = 256; // 'use_skip_proba' bit
+ if (proba->use_skip_proba_) {
+ size += nb_events * VP8BitCost(1, proba->skip_proba_)
+ + (nb_mbs - nb_events) * VP8BitCost(0, proba->skip_proba_);
+ size += 8 * 256; // cost of signaling the skip_proba_ itself.
+ }
+ return size;
+}
+
+//------------------------------------------------------------------------------
+// Recording of token probabilities.
+
+static void ResetTokenStats(VP8Encoder* const enc) {
+ VP8Proba* const proba = &enc->proba_;
+ memset(proba->stats_, 0, sizeof(proba->stats_));
+}
+
+// Record proba context used
+static int Record(int bit, proba_t* const stats) {
+ proba_t p = *stats;
+ if (p >= 0xffff0000u) { // an overflow is inbound.
+ p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2.
+ }
+ // record bit count (lower 16 bits) and increment total count (upper 16 bits).
+ p += 0x00010000u + bit;
+ *stats = p;
+ return bit;
+}
+
+// We keep the table free variant around for reference, in case.
+#define USE_LEVEL_CODE_TABLE
+
+// Simulate block coding, but only record statistics.
+// Note: no need to record the fixed probas.
+static int RecordCoeffs(int ctx, const VP8Residual* const res) {
+ int n = res->first;
+ // should be stats[VP8EncBands[n]], but it's equivalent for n=0 or 1
+ proba_t* s = res->stats[n][ctx];
+ if (res->last < 0) {
+ Record(0, s + 0);
+ return 0;
+ }
+ while (n <= res->last) {
+ int v;
+ Record(1, s + 0); // order of record doesn't matter
+ while ((v = res->coeffs[n++]) == 0) {
+ Record(0, s + 1);
+ s = res->stats[VP8EncBands[n]][0];
+ }
+ Record(1, s + 1);
+ if (!Record(2u < (unsigned int)(v + 1), s + 2)) { // v = -1 or 1
+ s = res->stats[VP8EncBands[n]][1];
+ } else {
+ v = abs(v);
+#if !defined(USE_LEVEL_CODE_TABLE)
+ if (!Record(v > 4, s + 3)) {
+ if (Record(v != 2, s + 4))
+ Record(v == 4, s + 5);
+ } else if (!Record(v > 10, s + 6)) {
+ Record(v > 6, s + 7);
+ } else if (!Record((v >= 3 + (8 << 2)), s + 8)) {
+ Record((v >= 3 + (8 << 1)), s + 9);
+ } else {
+ Record((v >= 3 + (8 << 3)), s + 10);
+ }
+#else
+ if (v > MAX_VARIABLE_LEVEL)
+ v = MAX_VARIABLE_LEVEL;
+
+ {
+ const int bits = VP8LevelCodes[v - 1][1];
+ int pattern = VP8LevelCodes[v - 1][0];
+ int i;
+ for (i = 0; (pattern >>= 1) != 0; ++i) {
+ const int mask = 2 << i;
+ if (pattern & 1) Record(!!(bits & mask), s + 3 + i);
+ }
+ }
+#endif
+ s = res->stats[VP8EncBands[n]][2];
+ }
+ }
+ if (n < 16) Record(0, s + 0);
+ return 1;
+}
+
+// Collect statistics and deduce probabilities for next coding pass.
+// Return the total bit-cost for coding the probability updates.
+static int CalcTokenProba(int nb, int total) {
+ assert(nb <= total);
+ return nb ? (255 - nb * 255 / total) : 255;
+}
+
+// Cost of coding 'nb' 1's and 'total-nb' 0's using 'proba' probability.
+static int BranchCost(int nb, int total, int proba) {
+ return nb * VP8BitCost(1, proba) + (total - nb) * VP8BitCost(0, proba);
+}
+
+static int FinalizeTokenProbas(VP8Proba* const proba) {
+ int has_changed = 0;
+ int size = 0;
+ int t, b, c, p;
+ for (t = 0; t < NUM_TYPES; ++t) {
+ for (b = 0; b < NUM_BANDS; ++b) {
+ for (c = 0; c < NUM_CTX; ++c) {
+ for (p = 0; p < NUM_PROBAS; ++p) {
+ const proba_t stats = proba->stats_[t][b][c][p];
+ const int nb = (stats >> 0) & 0xffff;
+ const int total = (stats >> 16) & 0xffff;
+ const int update_proba = VP8CoeffsUpdateProba[t][b][c][p];
+ const int old_p = VP8CoeffsProba0[t][b][c][p];
+ const int new_p = CalcTokenProba(nb, total);
+ const int old_cost = BranchCost(nb, total, old_p)
+ + VP8BitCost(0, update_proba);
+ const int new_cost = BranchCost(nb, total, new_p)
+ + VP8BitCost(1, update_proba)
+ + 8 * 256;
+ const int use_new_p = (old_cost > new_cost);
+ size += VP8BitCost(use_new_p, update_proba);
+ if (use_new_p) { // only use proba that seem meaningful enough.
+ proba->coeffs_[t][b][c][p] = new_p;
+ has_changed |= (new_p != old_p);
+ size += 8 * 256;
+ } else {
+ proba->coeffs_[t][b][c][p] = old_p;
+ }
+ }
+ }
+ }
+ }
+ proba->dirty_ = has_changed;
+ return size;
+}
+
+//------------------------------------------------------------------------------
+// Finalize Segment probability based on the coding tree
+
+static int GetProba(int a, int b) {
+ const int total = a + b;
+ return (total == 0) ? 255 // that's the default probability.
+ : (255 * a + total / 2) / total; // rounded proba
+}
+
+static void SetSegmentProbas(VP8Encoder* const enc) {
+ int p[NUM_MB_SEGMENTS] = { 0 };
+ int n;
+
+ for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
+ const VP8MBInfo* const mb = &enc->mb_info_[n];
+ p[mb->segment_]++;
+ }
+ if (enc->pic_->stats != NULL) {
+ for (n = 0; n < NUM_MB_SEGMENTS; ++n) {
+ enc->pic_->stats->segment_size[n] = p[n];
+ }
+ }
+ if (enc->segment_hdr_.num_segments_ > 1) {
+ uint8_t* const probas = enc->proba_.segments_;
+ probas[0] = GetProba(p[0] + p[1], p[2] + p[3]);
+ probas[1] = GetProba(p[0], p[1]);
+ probas[2] = GetProba(p[2], p[3]);
+
+ enc->segment_hdr_.update_map_ =
+ (probas[0] != 255) || (probas[1] != 255) || (probas[2] != 255);
+ enc->segment_hdr_.size_ =
+ p[0] * (VP8BitCost(0, probas[0]) + VP8BitCost(0, probas[1])) +
+ p[1] * (VP8BitCost(0, probas[0]) + VP8BitCost(1, probas[1])) +
+ p[2] * (VP8BitCost(1, probas[0]) + VP8BitCost(0, probas[2])) +
+ p[3] * (VP8BitCost(1, probas[0]) + VP8BitCost(1, probas[2]));
+ } else {
+ enc->segment_hdr_.update_map_ = 0;
+ enc->segment_hdr_.size_ = 0;
+ }
+}
+
+//------------------------------------------------------------------------------
+// helper functions for residuals struct VP8Residual.
+
+static void InitResidual(int first, int coeff_type,
+ VP8Encoder* const enc, VP8Residual* const res) {
+ res->coeff_type = coeff_type;
+ res->prob = enc->proba_.coeffs_[coeff_type];
+ res->stats = enc->proba_.stats_[coeff_type];
+ res->cost = enc->proba_.level_cost_[coeff_type];
+ res->first = first;
+}
+
+static void SetResidualCoeffs(const int16_t* const coeffs,
+ VP8Residual* const res) {
+ int n;
+ res->last = -1;
+ for (n = 15; n >= res->first; --n) {
+ if (coeffs[n]) {
+ res->last = n;
+ break;
+ }
+ }
+ res->coeffs = coeffs;
+}
+
+//------------------------------------------------------------------------------
+// Mode costs
+
+static int GetResidualCost(int ctx0, const VP8Residual* const res) {
+ int n = res->first;
+ // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1
+ int p0 = res->prob[n][ctx0][0];
+ const uint16_t* t = res->cost[n][ctx0];
+ int cost;
+
+ if (res->last < 0) {
+ return VP8BitCost(0, p0);
+ }
+ cost = VP8BitCost(1, p0);
+ for (; n < res->last; ++n) {
+ const int v = abs(res->coeffs[n]);
+ const int b = VP8EncBands[n + 1];
+ const int ctx = (v >= 2) ? 2 : v;
+ cost += VP8LevelCost(t, v);
+ t = res->cost[b][ctx];
+ // the masking trick is faster than "if (v) cost += ..." with clang
+ cost += (v ? ~0U : 0) & VP8BitCost(1, res->prob[b][ctx][0]);
+ }
+ // Last coefficient is always non-zero
+ {
+ const int v = abs(res->coeffs[n]);
+ assert(v != 0);
+ cost += VP8LevelCost(t, v);
+ if (n < 15) {
+ const int b = VP8EncBands[n + 1];
+ const int ctx = (v == 1) ? 1 : 2;
+ const int last_p0 = res->prob[b][ctx][0];
+ cost += VP8BitCost(0, last_p0);
+ }
+ }
+ return cost;
+}
+
+int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]) {
+ const int x = (it->i4_ & 3), y = (it->i4_ >> 2);
+ VP8Residual res;
+ VP8Encoder* const enc = it->enc_;
+ int R = 0;
+ int ctx;
+
+ InitResidual(0, 3, enc, &res);
+ ctx = it->top_nz_[x] + it->left_nz_[y];
+ SetResidualCoeffs(levels, &res);
+ R += GetResidualCost(ctx, &res);
+ return R;
+}
+
+int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd) {
+ VP8Residual res;
+ VP8Encoder* const enc = it->enc_;
+ int x, y;
+ int R = 0;
+
+ VP8IteratorNzToBytes(it); // re-import the non-zero context
+
+ // DC
+ InitResidual(0, 1, enc, &res);
+ SetResidualCoeffs(rd->y_dc_levels, &res);
+ R += GetResidualCost(it->top_nz_[8] + it->left_nz_[8], &res);
+
+ // AC
+ InitResidual(1, 0, enc, &res);
+ for (y = 0; y < 4; ++y) {
+ for (x = 0; x < 4; ++x) {
+ const int ctx = it->top_nz_[x] + it->left_nz_[y];
+ SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
+ R += GetResidualCost(ctx, &res);
+ it->top_nz_[x] = it->left_nz_[y] = (res.last >= 0);
+ }
+ }
+ return R;
+}
+
+int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd) {
+ VP8Residual res;
+ VP8Encoder* const enc = it->enc_;
+ int ch, x, y;
+ int R = 0;
+
+ VP8IteratorNzToBytes(it); // re-import the non-zero context
+
+ InitResidual(0, 2, enc, &res);
+ for (ch = 0; ch <= 2; ch += 2) {
+ for (y = 0; y < 2; ++y) {
+ for (x = 0; x < 2; ++x) {
+ const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
+ SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
+ R += GetResidualCost(ctx, &res);
+ it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = (res.last >= 0);
+ }
+ }
+ }
+ return R;
+}
+
+//------------------------------------------------------------------------------
+// Coefficient coding
+
+static int PutCoeffs(VP8BitWriter* const bw, int ctx, const VP8Residual* res) {
+ int n = res->first;
+ // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1
+ const uint8_t* p = res->prob[n][ctx];
+ if (!VP8PutBit(bw, res->last >= 0, p[0])) {
+ return 0;
+ }
+
+ while (n < 16) {
+ const int c = res->coeffs[n++];
+ const int sign = c < 0;
+ int v = sign ? -c : c;
+ if (!VP8PutBit(bw, v != 0, p[1])) {
+ p = res->prob[VP8EncBands[n]][0];
+ continue;
+ }
+ if (!VP8PutBit(bw, v > 1, p[2])) {
+ p = res->prob[VP8EncBands[n]][1];
+ } else {
+ if (!VP8PutBit(bw, v > 4, p[3])) {
+ if (VP8PutBit(bw, v != 2, p[4]))
+ VP8PutBit(bw, v == 4, p[5]);
+ } else if (!VP8PutBit(bw, v > 10, p[6])) {
+ if (!VP8PutBit(bw, v > 6, p[7])) {
+ VP8PutBit(bw, v == 6, 159);
+ } else {
+ VP8PutBit(bw, v >= 9, 165);
+ VP8PutBit(bw, !(v & 1), 145);
+ }
+ } else {
+ int mask;
+ const uint8_t* tab;
+ if (v < 3 + (8 << 1)) { // VP8Cat3 (3b)
+ VP8PutBit(bw, 0, p[8]);
+ VP8PutBit(bw, 0, p[9]);
+ v -= 3 + (8 << 0);
+ mask = 1 << 2;
+ tab = VP8Cat3;
+ } else if (v < 3 + (8 << 2)) { // VP8Cat4 (4b)
+ VP8PutBit(bw, 0, p[8]);
+ VP8PutBit(bw, 1, p[9]);
+ v -= 3 + (8 << 1);
+ mask = 1 << 3;
+ tab = VP8Cat4;
+ } else if (v < 3 + (8 << 3)) { // VP8Cat5 (5b)
+ VP8PutBit(bw, 1, p[8]);
+ VP8PutBit(bw, 0, p[10]);
+ v -= 3 + (8 << 2);
+ mask = 1 << 4;
+ tab = VP8Cat5;
+ } else { // VP8Cat6 (11b)
+ VP8PutBit(bw, 1, p[8]);
+ VP8PutBit(bw, 1, p[10]);
+ v -= 3 + (8 << 3);
+ mask = 1 << 10;
+ tab = VP8Cat6;
+ }
+ while (mask) {
+ VP8PutBit(bw, !!(v & mask), *tab++);
+ mask >>= 1;
+ }
+ }
+ p = res->prob[VP8EncBands[n]][2];
+ }
+ VP8PutBitUniform(bw, sign);
+ if (n == 16 || !VP8PutBit(bw, n <= res->last, p[0])) {
+ return 1; // EOB
+ }
+ }
+ return 1;
+}
+
+static void CodeResiduals(VP8BitWriter* const bw, VP8EncIterator* const it,
+ const VP8ModeScore* const rd) {
+ int x, y, ch;
+ VP8Residual res;
+ uint64_t pos1, pos2, pos3;
+ const int i16 = (it->mb_->type_ == 1);
+ const int segment = it->mb_->segment_;
+ VP8Encoder* const enc = it->enc_;
+
+ VP8IteratorNzToBytes(it);
+
+ pos1 = VP8BitWriterPos(bw);
+ if (i16) {
+ InitResidual(0, 1, enc, &res);
+ SetResidualCoeffs(rd->y_dc_levels, &res);
+ it->top_nz_[8] = it->left_nz_[8] =
+ PutCoeffs(bw, it->top_nz_[8] + it->left_nz_[8], &res);
+ InitResidual(1, 0, enc, &res);
+ } else {
+ InitResidual(0, 3, enc, &res);
+ }
+
+ // luma-AC
+ for (y = 0; y < 4; ++y) {
+ for (x = 0; x < 4; ++x) {
+ const int ctx = it->top_nz_[x] + it->left_nz_[y];
+ SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
+ it->top_nz_[x] = it->left_nz_[y] = PutCoeffs(bw, ctx, &res);
+ }
+ }
+ pos2 = VP8BitWriterPos(bw);
+
+ // U/V
+ InitResidual(0, 2, enc, &res);
+ for (ch = 0; ch <= 2; ch += 2) {
+ for (y = 0; y < 2; ++y) {
+ for (x = 0; x < 2; ++x) {
+ const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
+ SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
+ it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] =
+ PutCoeffs(bw, ctx, &res);
+ }
+ }
+ }
+ pos3 = VP8BitWriterPos(bw);
+ it->luma_bits_ = pos2 - pos1;
+ it->uv_bits_ = pos3 - pos2;
+ it->bit_count_[segment][i16] += it->luma_bits_;
+ it->bit_count_[segment][2] += it->uv_bits_;
+ VP8IteratorBytesToNz(it);
+}
+
+// Same as CodeResiduals, but doesn't actually write anything.
+// Instead, it just records the event distribution.
+static void RecordResiduals(VP8EncIterator* const it,
+ const VP8ModeScore* const rd) {
+ int x, y, ch;
+ VP8Residual res;
+ VP8Encoder* const enc = it->enc_;
+
+ VP8IteratorNzToBytes(it);
+
+ if (it->mb_->type_ == 1) { // i16x16
+ InitResidual(0, 1, enc, &res);
+ SetResidualCoeffs(rd->y_dc_levels, &res);
+ it->top_nz_[8] = it->left_nz_[8] =
+ RecordCoeffs(it->top_nz_[8] + it->left_nz_[8], &res);
+ InitResidual(1, 0, enc, &res);
+ } else {
+ InitResidual(0, 3, enc, &res);
+ }
+
+ // luma-AC
+ for (y = 0; y < 4; ++y) {
+ for (x = 0; x < 4; ++x) {
+ const int ctx = it->top_nz_[x] + it->left_nz_[y];
+ SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
+ it->top_nz_[x] = it->left_nz_[y] = RecordCoeffs(ctx, &res);
+ }
+ }
+
+ // U/V
+ InitResidual(0, 2, enc, &res);
+ for (ch = 0; ch <= 2; ch += 2) {
+ for (y = 0; y < 2; ++y) {
+ for (x = 0; x < 2; ++x) {
+ const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
+ SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
+ it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] =
+ RecordCoeffs(ctx, &res);
+ }
+ }
+ }
+
+ VP8IteratorBytesToNz(it);
+}
+
+//------------------------------------------------------------------------------
+// Token buffer
+
+#if !defined(DISABLE_TOKEN_BUFFER)
+
+static void RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd,
+ VP8TBuffer* const tokens) {
+ int x, y, ch;
+ VP8Residual res;
+ VP8Encoder* const enc = it->enc_;
+
+ VP8IteratorNzToBytes(it);
+ if (it->mb_->type_ == 1) { // i16x16
+ const int ctx = it->top_nz_[8] + it->left_nz_[8];
+ InitResidual(0, 1, enc, &res);
+ SetResidualCoeffs(rd->y_dc_levels, &res);
+ it->top_nz_[8] = it->left_nz_[8] =
+ VP8RecordCoeffTokens(ctx, 1,
+ res.first, res.last, res.coeffs, tokens);
+ RecordCoeffs(ctx, &res);
+ InitResidual(1, 0, enc, &res);
+ } else {
+ InitResidual(0, 3, enc, &res);
+ }
+
+ // luma-AC
+ for (y = 0; y < 4; ++y) {
+ for (x = 0; x < 4; ++x) {
+ const int ctx = it->top_nz_[x] + it->left_nz_[y];
+ SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
+ it->top_nz_[x] = it->left_nz_[y] =
+ VP8RecordCoeffTokens(ctx, res.coeff_type,
+ res.first, res.last, res.coeffs, tokens);
+ RecordCoeffs(ctx, &res);
+ }
+ }
+
+ // U/V
+ InitResidual(0, 2, enc, &res);
+ for (ch = 0; ch <= 2; ch += 2) {
+ for (y = 0; y < 2; ++y) {
+ for (x = 0; x < 2; ++x) {
+ const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
+ SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
+ it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] =
+ VP8RecordCoeffTokens(ctx, 2,
+ res.first, res.last, res.coeffs, tokens);
+ RecordCoeffs(ctx, &res);
+ }
+ }
+ }
+ VP8IteratorBytesToNz(it);
+}
+
+#endif // !DISABLE_TOKEN_BUFFER
+
+//------------------------------------------------------------------------------
+// ExtraInfo map / Debug function
+
+#if SEGMENT_VISU
+static void SetBlock(uint8_t* p, int value, int size) {
+ int y;
+ for (y = 0; y < size; ++y) {
+ memset(p, value, size);
+ p += BPS;
+ }
+}
+#endif
+
+static void ResetSSE(VP8Encoder* const enc) {
+ enc->sse_[0] = 0;
+ enc->sse_[1] = 0;
+ enc->sse_[2] = 0;
+ // Note: enc->sse_[3] is managed by alpha.c
+ enc->sse_count_ = 0;
+}
+
+static void StoreSSE(const VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+ const uint8_t* const in = it->yuv_in_;
+ const uint8_t* const out = it->yuv_out_;
+ // Note: not totally accurate at boundary. And doesn't include in-loop filter.
+ enc->sse_[0] += VP8SSE16x16(in + Y_OFF, out + Y_OFF);
+ enc->sse_[1] += VP8SSE8x8(in + U_OFF, out + U_OFF);
+ enc->sse_[2] += VP8SSE8x8(in + V_OFF, out + V_OFF);
+ enc->sse_count_ += 16 * 16;
+}
+
+static void StoreSideInfo(const VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+ const VP8MBInfo* const mb = it->mb_;
+ WebPPicture* const pic = enc->pic_;
+
+ if (pic->stats != NULL) {
+ StoreSSE(it);
+ enc->block_count_[0] += (mb->type_ == 0);
+ enc->block_count_[1] += (mb->type_ == 1);
+ enc->block_count_[2] += (mb->skip_ != 0);
+ }
+
+ if (pic->extra_info != NULL) {
+ uint8_t* const info = &pic->extra_info[it->x_ + it->y_ * enc->mb_w_];
+ switch (pic->extra_info_type) {
+ case 1: *info = mb->type_; break;
+ case 2: *info = mb->segment_; break;
+ case 3: *info = enc->dqm_[mb->segment_].quant_; break;
+ case 4: *info = (mb->type_ == 1) ? it->preds_[0] : 0xff; break;
+ case 5: *info = mb->uv_mode_; break;
+ case 6: {
+ const int b = (int)((it->luma_bits_ + it->uv_bits_ + 7) >> 3);
+ *info = (b > 255) ? 255 : b; break;
+ }
+ case 7: *info = mb->alpha_; break;
+ default: *info = 0; break;
+ };
+ }
+#if SEGMENT_VISU // visualize segments and prediction modes
+ SetBlock(it->yuv_out_ + Y_OFF, mb->segment_ * 64, 16);
+ SetBlock(it->yuv_out_ + U_OFF, it->preds_[0] * 64, 8);
+ SetBlock(it->yuv_out_ + V_OFF, mb->uv_mode_ * 64, 8);
+#endif
+}
+
+static double GetPSNR(uint64_t mse, uint64_t size) {
+ return (mse > 0 && size > 0) ? 10. * log10(255. * 255. * size / mse) : 99;
+}
+
+//------------------------------------------------------------------------------
+// StatLoop(): only collect statistics (number of skips, token usage, ...).
+// This is used for deciding optimal probabilities. It also modifies the
+// quantizer value if some target (size, PSNR) was specified.
+
+static void SetLoopParams(VP8Encoder* const enc, float q) {
+ // Make sure the quality parameter is inside valid bounds
+ q = Clamp(q, 0.f, 100.f);
+
+ VP8SetSegmentParams(enc, q); // setup segment quantizations and filters
+ SetSegmentProbas(enc); // compute segment probabilities
+
+ ResetStats(enc);
+ ResetSSE(enc);
+}
+
+static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt,
+ int nb_mbs, int percent_delta,
+ PassStats* const s) {
+ VP8EncIterator it;
+ uint64_t size = 0;
+ uint64_t size_p0 = 0;
+ uint64_t distortion = 0;
+ const uint64_t pixel_count = nb_mbs * 384;
+
+ VP8IteratorInit(enc, &it);
+ SetLoopParams(enc, s->q);
+ do {
+ VP8ModeScore info;
+ VP8IteratorImport(&it, NULL);
+ if (VP8Decimate(&it, &info, rd_opt)) {
+ // Just record the number of skips and act like skip_proba is not used.
+ enc->proba_.nb_skip_++;
+ }
+ RecordResiduals(&it, &info);
+ size += info.R + info.H;
+ size_p0 += info.H;
+ distortion += info.D;
+ if (percent_delta && !VP8IteratorProgress(&it, percent_delta))
+ return 0;
+ VP8IteratorSaveBoundary(&it);
+ } while (VP8IteratorNext(&it) && --nb_mbs > 0);
+
+ size_p0 += enc->segment_hdr_.size_;
+ if (s->do_size_search) {
+ size += FinalizeSkipProba(enc);
+ size += FinalizeTokenProbas(&enc->proba_);
+ size = ((size + size_p0 + 1024) >> 11) + HEADER_SIZE_ESTIMATE;
+ s->value = (double)size;
+ } else {
+ s->value = GetPSNR(distortion, pixel_count);
+ }
+ return size_p0;
+}
+
+static int StatLoop(VP8Encoder* const enc) {
+ const int method = enc->method_;
+ const int do_search = enc->do_search_;
+ const int fast_probe = ((method == 0 || method == 3) && !do_search);
+ int num_pass_left = enc->config_->pass;
+ const int task_percent = 20;
+ const int percent_per_pass =
+ (task_percent + num_pass_left / 2) / num_pass_left;
+ const int final_percent = enc->percent_ + task_percent;
+ const VP8RDLevel rd_opt =
+ (method >= 3 || do_search) ? RD_OPT_BASIC : RD_OPT_NONE;
+ int nb_mbs = enc->mb_w_ * enc->mb_h_;
+ PassStats stats;
+
+ InitPassStats(enc, &stats);
+ ResetTokenStats(enc);
+
+ // Fast mode: quick analysis pass over few mbs. Better than nothing.
+ if (fast_probe) {
+ if (method == 3) { // we need more stats for method 3 to be reliable.
+ nb_mbs = (nb_mbs > 200) ? nb_mbs >> 1 : 100;
+ } else {
+ nb_mbs = (nb_mbs > 200) ? nb_mbs >> 2 : 50;
+ }
+ }
+
+ while (num_pass_left-- > 0) {
+ const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) ||
+ (num_pass_left == 0) ||
+ (enc->max_i4_header_bits_ == 0);
+ const uint64_t size_p0 =
+ OneStatPass(enc, rd_opt, nb_mbs, percent_per_pass, &stats);
+ if (size_p0 == 0) return 0;
+#if (DEBUG_SEARCH > 0)
+ printf("#%d value:%.1lf -> %.1lf q:%.2f -> %.2f\n",
+ num_pass_left, stats.last_value, stats.value, stats.last_q, stats.q);
+#endif
+ if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) {
+ ++num_pass_left;
+ enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation...
+ continue; // ...and start over
+ }
+ if (is_last_pass) {
+ break;
+ }
+ // If no target size: just do several pass without changing 'q'
+ if (do_search) {
+ ComputeNextQ(&stats);
+ if (fabs(stats.dq) <= DQ_LIMIT) break;
+ }
+ }
+ if (!do_search || !stats.do_size_search) {
+ // Need to finalize probas now, since it wasn't done during the search.
+ FinalizeSkipProba(enc);
+ FinalizeTokenProbas(&enc->proba_);
+ }
+ VP8CalculateLevelCosts(&enc->proba_); // finalize costs
+ return WebPReportProgress(enc->pic_, final_percent, &enc->percent_);
+}
+
+//------------------------------------------------------------------------------
+// Main loops
+//
+
+static const int kAverageBytesPerMB[8] = { 50, 24, 16, 9, 7, 5, 3, 2 };
+
+static int PreLoopInitialize(VP8Encoder* const enc) {
+ int p;
+ int ok = 1;
+ const int average_bytes_per_MB = kAverageBytesPerMB[enc->base_quant_ >> 4];
+ const int bytes_per_parts =
+ enc->mb_w_ * enc->mb_h_ * average_bytes_per_MB / enc->num_parts_;
+ // Initialize the bit-writers
+ for (p = 0; ok && p < enc->num_parts_; ++p) {
+ ok = VP8BitWriterInit(enc->parts_ + p, bytes_per_parts);
+ }
+ if (!ok) VP8EncFreeBitWriters(enc); // malloc error occurred
+ return ok;
+}
+
+static int PostLoopFinalize(VP8EncIterator* const it, int ok) {
+ VP8Encoder* const enc = it->enc_;
+ if (ok) { // Finalize the partitions, check for extra errors.
+ int p;
+ for (p = 0; p < enc->num_parts_; ++p) {
+ VP8BitWriterFinish(enc->parts_ + p);
+ ok &= !enc->parts_[p].error_;
+ }
+ }
+
+ if (ok) { // All good. Finish up.
+ if (enc->pic_->stats != NULL) { // finalize byte counters...
+ int i, s;
+ for (i = 0; i <= 2; ++i) {
+ for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
+ enc->residual_bytes_[i][s] = (int)((it->bit_count_[s][i] + 7) >> 3);
+ }
+ }
+ }
+ VP8AdjustFilterStrength(it); // ...and store filter stats.
+ } else {
+ // Something bad happened -> need to do some memory cleanup.
+ VP8EncFreeBitWriters(enc);
+ }
+ return ok;
+}
+
+//------------------------------------------------------------------------------
+// VP8EncLoop(): does the final bitstream coding.
+
+static void ResetAfterSkip(VP8EncIterator* const it) {
+ if (it->mb_->type_ == 1) {
+ *it->nz_ = 0; // reset all predictors
+ it->left_nz_[8] = 0;
+ } else {
+ *it->nz_ &= (1 << 24); // preserve the dc_nz bit
+ }
+}
+
+int VP8EncLoop(VP8Encoder* const enc) {
+ VP8EncIterator it;
+ int ok = PreLoopInitialize(enc);
+ if (!ok) return 0;
+
+ StatLoop(enc); // stats-collection loop
+
+ VP8IteratorInit(enc, &it);
+ VP8InitFilter(&it);
+ do {
+ VP8ModeScore info;
+ const int dont_use_skip = !enc->proba_.use_skip_proba_;
+ const VP8RDLevel rd_opt = enc->rd_opt_level_;
+
+ VP8IteratorImport(&it, NULL);
+ // Warning! order is important: first call VP8Decimate() and
+ // *then* decide how to code the skip decision if there's one.
+ if (!VP8Decimate(&it, &info, rd_opt) || dont_use_skip) {
+ CodeResiduals(it.bw_, &it, &info);
+ } else { // reset predictors after a skip
+ ResetAfterSkip(&it);
+ }
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+ if (enc->use_layer_) {
+ VP8EncCodeLayerBlock(&it);
+ }
+#endif
+ StoreSideInfo(&it);
+ VP8StoreFilterStats(&it);
+ VP8IteratorExport(&it);
+ ok = VP8IteratorProgress(&it, 20);
+ VP8IteratorSaveBoundary(&it);
+ } while (ok && VP8IteratorNext(&it));
+
+ return PostLoopFinalize(&it, ok);
+}
+
+//------------------------------------------------------------------------------
+// Single pass using Token Buffer.
+
+#if !defined(DISABLE_TOKEN_BUFFER)
+
+#define MIN_COUNT 96 // minimum number of macroblocks before updating stats
+
+int VP8EncTokenLoop(VP8Encoder* const enc) {
+ // Roughly refresh the proba eight times per pass
+ int max_count = (enc->mb_w_ * enc->mb_h_) >> 3;
+ int num_pass_left = enc->config_->pass;
+ const int do_search = enc->do_search_;
+ VP8EncIterator it;
+ VP8Proba* const proba = &enc->proba_;
+ const VP8RDLevel rd_opt = enc->rd_opt_level_;
+ const uint64_t pixel_count = enc->mb_w_ * enc->mb_h_ * 384;
+ PassStats stats;
+ int ok;
+
+ InitPassStats(enc, &stats);
+ ok = PreLoopInitialize(enc);
+ if (!ok) return 0;
+
+ if (max_count < MIN_COUNT) max_count = MIN_COUNT;
+
+ assert(enc->num_parts_ == 1);
+ assert(enc->use_tokens_);
+ assert(proba->use_skip_proba_ == 0);
+ assert(rd_opt >= RD_OPT_BASIC); // otherwise, token-buffer won't be useful
+ assert(num_pass_left > 0);
+
+ while (ok && num_pass_left-- > 0) {
+ const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) ||
+ (num_pass_left == 0) ||
+ (enc->max_i4_header_bits_ == 0);
+ uint64_t size_p0 = 0;
+ uint64_t distortion = 0;
+ int cnt = max_count;
+ VP8IteratorInit(enc, &it);
+ SetLoopParams(enc, stats.q);
+ if (is_last_pass) {
+ ResetTokenStats(enc);
+ VP8InitFilter(&it); // don't collect stats until last pass (too costly)
+ }
+ VP8TBufferClear(&enc->tokens_);
+ do {
+ VP8ModeScore info;
+ VP8IteratorImport(&it, NULL);
+ if (--cnt < 0) {
+ FinalizeTokenProbas(proba);
+ VP8CalculateLevelCosts(proba); // refresh cost tables for rd-opt
+ cnt = max_count;
+ }
+ VP8Decimate(&it, &info, rd_opt);
+ RecordTokens(&it, &info, &enc->tokens_);
+ size_p0 += info.H;
+ distortion += info.D;
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+ if (enc->use_layer_) {
+ VP8EncCodeLayerBlock(&it);
+ }
+#endif
+ if (is_last_pass) {
+ StoreSideInfo(&it);
+ VP8StoreFilterStats(&it);
+ VP8IteratorExport(&it);
+ ok = VP8IteratorProgress(&it, 20);
+ }
+ VP8IteratorSaveBoundary(&it);
+ } while (ok && VP8IteratorNext(&it));
+ if (!ok) break;
+
+ size_p0 += enc->segment_hdr_.size_;
+ if (stats.do_size_search) {
+ uint64_t size = FinalizeTokenProbas(&enc->proba_);
+ size += VP8EstimateTokenSize(&enc->tokens_,
+ (const uint8_t*)proba->coeffs_);
+ size = (size + size_p0 + 1024) >> 11; // -> size in bytes
+ size += HEADER_SIZE_ESTIMATE;
+ stats.value = (double)size;
+ } else { // compute and store PSNR
+ stats.value = GetPSNR(distortion, pixel_count);
+ }
+
+#if (DEBUG_SEARCH > 0)
+ printf("#%2d metric:%.1lf -> %.1lf last_q=%.2lf q=%.2lf dq=%.2lf\n",
+ num_pass_left, stats.last_value, stats.value,
+ stats.last_q, stats.q, stats.dq);
+#endif
+ if (size_p0 > PARTITION0_SIZE_LIMIT) {
+ ++num_pass_left;
+ enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation...
+ continue; // ...and start over
+ }
+ if (is_last_pass) {
+ break; // done
+ }
+ if (do_search) {
+ ComputeNextQ(&stats); // Adjust q
+ }
+ }
+ if (ok) {
+ if (!stats.do_size_search) {
+ FinalizeTokenProbas(&enc->proba_);
+ }
+ ok = VP8EmitTokens(&enc->tokens_, enc->parts_ + 0,
+ (const uint8_t*)proba->coeffs_, 1);
+ }
+ ok = ok && WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
+ return PostLoopFinalize(&it, ok);
+}
+
+#else
+
+int VP8EncTokenLoop(VP8Encoder* const enc) {
+ (void)enc;
+ return 0; // we shouldn't be here.
+}
+
+#endif // DISABLE_TOKEN_BUFFER
+
+//------------------------------------------------------------------------------
+
diff --git a/src/3rdparty/libwebp/src/enc/histogram.c b/src/3rdparty/libwebp/src/enc/histogram.c
new file mode 100644
index 0000000..abd253b
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/histogram.c
@@ -0,0 +1,512 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Author: Jyrki Alakuijala (jyrki@google.com)
+//
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <math.h>
+#include <stdio.h>
+
+#include "./backward_references.h"
+#include "./histogram.h"
+#include "../dsp/lossless.h"
+#include "../utils/utils.h"
+
+static void HistogramClear(VP8LHistogram* const p) {
+ memset(p->literal_, 0, sizeof(p->literal_));
+ memset(p->red_, 0, sizeof(p->red_));
+ memset(p->blue_, 0, sizeof(p->blue_));
+ memset(p->alpha_, 0, sizeof(p->alpha_));
+ memset(p->distance_, 0, sizeof(p->distance_));
+ p->bit_cost_ = 0;
+}
+
+void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
+ VP8LHistogram* const histo) {
+ int i;
+ for (i = 0; i < refs->size; ++i) {
+ VP8LHistogramAddSinglePixOrCopy(histo, &refs->refs[i]);
+ }
+}
+
+void VP8LHistogramCreate(VP8LHistogram* const p,
+ const VP8LBackwardRefs* const refs,
+ int palette_code_bits) {
+ if (palette_code_bits >= 0) {
+ p->palette_code_bits_ = palette_code_bits;
+ }
+ HistogramClear(p);
+ VP8LHistogramStoreRefs(refs, p);
+}
+
+void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits) {
+ p->palette_code_bits_ = palette_code_bits;
+ HistogramClear(p);
+}
+
+VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) {
+ int i;
+ VP8LHistogramSet* set;
+ VP8LHistogram* bulk;
+ const uint64_t total_size = sizeof(*set)
+ + (uint64_t)size * sizeof(*set->histograms)
+ + (uint64_t)size * sizeof(**set->histograms);
+ uint8_t* memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory));
+ if (memory == NULL) return NULL;
+
+ set = (VP8LHistogramSet*)memory;
+ memory += sizeof(*set);
+ set->histograms = (VP8LHistogram**)memory;
+ memory += size * sizeof(*set->histograms);
+ bulk = (VP8LHistogram*)memory;
+ set->max_size = size;
+ set->size = size;
+ for (i = 0; i < size; ++i) {
+ set->histograms[i] = bulk + i;
+ VP8LHistogramInit(set->histograms[i], cache_bits);
+ }
+ return set;
+}
+
+// -----------------------------------------------------------------------------
+
+void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
+ const PixOrCopy* const v) {
+ if (PixOrCopyIsLiteral(v)) {
+ ++histo->alpha_[PixOrCopyLiteral(v, 3)];
+ ++histo->red_[PixOrCopyLiteral(v, 2)];
+ ++histo->literal_[PixOrCopyLiteral(v, 1)];
+ ++histo->blue_[PixOrCopyLiteral(v, 0)];
+ } else if (PixOrCopyIsCacheIdx(v)) {
+ int literal_ix = 256 + NUM_LENGTH_CODES + PixOrCopyCacheIdx(v);
+ ++histo->literal_[literal_ix];
+ } else {
+ int code, extra_bits;
+ VP8LPrefixEncodeBits(PixOrCopyLength(v), &code, &extra_bits);
+ ++histo->literal_[256 + code];
+ VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code, &extra_bits);
+ ++histo->distance_[code];
+ }
+}
+
+static double BitsEntropy(const int* const array, int n) {
+ double retval = 0.;
+ int sum = 0;
+ int nonzeros = 0;
+ int max_val = 0;
+ int i;
+ double mix;
+ for (i = 0; i < n; ++i) {
+ if (array[i] != 0) {
+ sum += array[i];
+ ++nonzeros;
+ retval -= VP8LFastSLog2(array[i]);
+ if (max_val < array[i]) {
+ max_val = array[i];
+ }
+ }
+ }
+ retval += VP8LFastSLog2(sum);
+
+ if (nonzeros < 5) {
+ if (nonzeros <= 1) {
+ return 0;
+ }
+ // Two symbols, they will be 0 and 1 in a Huffman code.
+ // Let's mix in a bit of entropy to favor good clustering when
+ // distributions of these are combined.
+ if (nonzeros == 2) {
+ return 0.99 * sum + 0.01 * retval;
+ }
+ // No matter what the entropy says, we cannot be better than min_limit
+ // with Huffman coding. I am mixing a bit of entropy into the
+ // min_limit since it produces much better (~0.5 %) compression results
+ // perhaps because of better entropy clustering.
+ if (nonzeros == 3) {
+ mix = 0.95;
+ } else {
+ mix = 0.7; // nonzeros == 4.
+ }
+ } else {
+ mix = 0.627;
+ }
+
+ {
+ double min_limit = 2 * sum - max_val;
+ min_limit = mix * min_limit + (1.0 - mix) * retval;
+ return (retval < min_limit) ? min_limit : retval;
+ }
+}
+
+// Returns the cost encode the rle-encoded entropy code.
+// The constants in this function are experimental.
+static double HuffmanCost(const int* const population, int length) {
+ // Small bias because Huffman code length is typically not stored in
+ // full length.
+ static const int kHuffmanCodeOfHuffmanCodeSize = CODE_LENGTH_CODES * 3;
+ static const double kSmallBias = 9.1;
+ double retval = kHuffmanCodeOfHuffmanCodeSize - kSmallBias;
+ int streak = 0;
+ int i = 0;
+ for (; i < length - 1; ++i) {
+ ++streak;
+ if (population[i] == population[i + 1]) {
+ continue;
+ }
+ last_streak_hack:
+ // population[i] points now to the symbol in the streak of same values.
+ if (streak > 3) {
+ if (population[i] == 0) {
+ retval += 1.5625 + 0.234375 * streak;
+ } else {
+ retval += 2.578125 + 0.703125 * streak;
+ }
+ } else {
+ if (population[i] == 0) {
+ retval += 1.796875 * streak;
+ } else {
+ retval += 3.28125 * streak;
+ }
+ }
+ streak = 0;
+ }
+ if (i == length - 1) {
+ ++streak;
+ goto last_streak_hack;
+ }
+ return retval;
+}
+
+static double PopulationCost(const int* const population, int length) {
+ return BitsEntropy(population, length) + HuffmanCost(population, length);
+}
+
+static double ExtraCost(const int* const population, int length) {
+ int i;
+ double cost = 0.;
+ for (i = 2; i < length - 2; ++i) cost += (i >> 1) * population[i + 2];
+ return cost;
+}
+
+// Estimates the Entropy + Huffman + other block overhead size cost.
+double VP8LHistogramEstimateBits(const VP8LHistogram* const p) {
+ return PopulationCost(p->literal_, VP8LHistogramNumCodes(p))
+ + PopulationCost(p->red_, 256)
+ + PopulationCost(p->blue_, 256)
+ + PopulationCost(p->alpha_, 256)
+ + PopulationCost(p->distance_, NUM_DISTANCE_CODES)
+ + ExtraCost(p->literal_ + 256, NUM_LENGTH_CODES)
+ + ExtraCost(p->distance_, NUM_DISTANCE_CODES);
+}
+
+double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) {
+ return BitsEntropy(p->literal_, VP8LHistogramNumCodes(p))
+ + BitsEntropy(p->red_, 256)
+ + BitsEntropy(p->blue_, 256)
+ + BitsEntropy(p->alpha_, 256)
+ + BitsEntropy(p->distance_, NUM_DISTANCE_CODES)
+ + ExtraCost(p->literal_ + 256, NUM_LENGTH_CODES)
+ + ExtraCost(p->distance_, NUM_DISTANCE_CODES);
+}
+
+// -----------------------------------------------------------------------------
+// Various histogram combine/cost-eval functions
+
+// Adds 'in' histogram to 'out'
+static void HistogramAdd(const VP8LHistogram* const in,
+ VP8LHistogram* const out) {
+ int i;
+ for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) {
+ out->literal_[i] += in->literal_[i];
+ }
+ for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
+ out->distance_[i] += in->distance_[i];
+ }
+ for (i = 0; i < 256; ++i) {
+ out->red_[i] += in->red_[i];
+ out->blue_[i] += in->blue_[i];
+ out->alpha_[i] += in->alpha_[i];
+ }
+}
+
+// Performs out = a + b, computing the cost C(a+b) - C(a) - C(b) while comparing
+// to the threshold value 'cost_threshold'. The score returned is
+// Score = C(a+b) - C(a) - C(b), where C(a) + C(b) is known and fixed.
+// Since the previous score passed is 'cost_threshold', we only need to compare
+// the partial cost against 'cost_threshold + C(a) + C(b)' to possibly bail-out
+// early.
+static double HistogramAddEval(const VP8LHistogram* const a,
+ const VP8LHistogram* const b,
+ VP8LHistogram* const out,
+ double cost_threshold) {
+ double cost = 0;
+ const double sum_cost = a->bit_cost_ + b->bit_cost_;
+ int i;
+
+ cost_threshold += sum_cost;
+
+ // palette_code_bits_ is part of the cost evaluation for literal_.
+ // TODO(skal): remove/simplify this palette_code_bits_?
+ out->palette_code_bits_ =
+ (a->palette_code_bits_ > b->palette_code_bits_) ? a->palette_code_bits_ :
+ b->palette_code_bits_;
+ for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) {
+ out->literal_[i] = a->literal_[i] + b->literal_[i];
+ }
+ cost += PopulationCost(out->literal_, VP8LHistogramNumCodes(out));
+ cost += ExtraCost(out->literal_ + 256, NUM_LENGTH_CODES);
+ if (cost > cost_threshold) return cost;
+
+ for (i = 0; i < 256; ++i) out->red_[i] = a->red_[i] + b->red_[i];
+ cost += PopulationCost(out->red_, 256);
+ if (cost > cost_threshold) return cost;
+
+ for (i = 0; i < 256; ++i) out->blue_[i] = a->blue_[i] + b->blue_[i];
+ cost += PopulationCost(out->blue_, 256);
+ if (cost > cost_threshold) return cost;
+
+ for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
+ out->distance_[i] = a->distance_[i] + b->distance_[i];
+ }
+ cost += PopulationCost(out->distance_, NUM_DISTANCE_CODES);
+ cost += ExtraCost(out->distance_, NUM_DISTANCE_CODES);
+ if (cost > cost_threshold) return cost;
+
+ for (i = 0; i < 256; ++i) out->alpha_[i] = a->alpha_[i] + b->alpha_[i];
+ cost += PopulationCost(out->alpha_, 256);
+
+ out->bit_cost_ = cost;
+ return cost - sum_cost;
+}
+
+// Same as HistogramAddEval(), except that the resulting histogram
+// is not stored. Only the cost C(a+b) - C(a) is evaluated. We omit
+// the term C(b) which is constant over all the evaluations.
+static double HistogramAddThresh(const VP8LHistogram* const a,
+ const VP8LHistogram* const b,
+ double cost_threshold) {
+ int tmp[PIX_OR_COPY_CODES_MAX]; // <= max storage we'll need
+ int i;
+ double cost = -a->bit_cost_;
+
+ for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) {
+ tmp[i] = a->literal_[i] + b->literal_[i];
+ }
+ // note that the tests are ordered so that the usually largest
+ // cost shares come first.
+ cost += PopulationCost(tmp, VP8LHistogramNumCodes(a));
+ cost += ExtraCost(tmp + 256, NUM_LENGTH_CODES);
+ if (cost > cost_threshold) return cost;
+
+ for (i = 0; i < 256; ++i) tmp[i] = a->red_[i] + b->red_[i];
+ cost += PopulationCost(tmp, 256);
+ if (cost > cost_threshold) return cost;
+
+ for (i = 0; i < 256; ++i) tmp[i] = a->blue_[i] + b->blue_[i];
+ cost += PopulationCost(tmp, 256);
+ if (cost > cost_threshold) return cost;
+
+ for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
+ tmp[i] = a->distance_[i] + b->distance_[i];
+ }
+ cost += PopulationCost(tmp, NUM_DISTANCE_CODES);
+ cost += ExtraCost(tmp, NUM_DISTANCE_CODES);
+ if (cost > cost_threshold) return cost;
+
+ for (i = 0; i < 256; ++i) tmp[i] = a->alpha_[i] + b->alpha_[i];
+ cost += PopulationCost(tmp, 256);
+
+ return cost;
+}
+
+// -----------------------------------------------------------------------------
+
+static void HistogramBuildImage(int xsize, int histo_bits,
+ const VP8LBackwardRefs* const backward_refs,
+ VP8LHistogramSet* const image) {
+ int i;
+ int x = 0, y = 0;
+ const int histo_xsize = VP8LSubSampleSize(xsize, histo_bits);
+ VP8LHistogram** const histograms = image->histograms;
+ assert(histo_bits > 0);
+ for (i = 0; i < backward_refs->size; ++i) {
+ const PixOrCopy* const v = &backward_refs->refs[i];
+ const int ix = (y >> histo_bits) * histo_xsize + (x >> histo_bits);
+ VP8LHistogramAddSinglePixOrCopy(histograms[ix], v);
+ x += PixOrCopyLength(v);
+ while (x >= xsize) {
+ x -= xsize;
+ ++y;
+ }
+ }
+}
+
+static uint32_t MyRand(uint32_t *seed) {
+ *seed *= 16807U;
+ if (*seed == 0) {
+ *seed = 1;
+ }
+ return *seed;
+}
+
+static int HistogramCombine(const VP8LHistogramSet* const in,
+ VP8LHistogramSet* const out, int iter_mult,
+ int num_pairs, int num_tries_no_success) {
+ int ok = 0;
+ int i, iter;
+ uint32_t seed = 0;
+ int tries_with_no_success = 0;
+ int out_size = in->size;
+ const int outer_iters = in->size * iter_mult;
+ const int min_cluster_size = 2;
+ VP8LHistogram* const histos = (VP8LHistogram*)malloc(2 * sizeof(*histos));
+ VP8LHistogram* cur_combo = histos + 0; // trial merged histogram
+ VP8LHistogram* best_combo = histos + 1; // best merged histogram so far
+ if (histos == NULL) goto End;
+
+ // Copy histograms from in[] to out[].
+ assert(in->size <= out->size);
+ for (i = 0; i < in->size; ++i) {
+ in->histograms[i]->bit_cost_ = VP8LHistogramEstimateBits(in->histograms[i]);
+ *out->histograms[i] = *in->histograms[i];
+ }
+
+ // Collapse similar histograms in 'out'.
+ for (iter = 0; iter < outer_iters && out_size >= min_cluster_size; ++iter) {
+ double best_cost_diff = 0.;
+ int best_idx1 = -1, best_idx2 = 1;
+ int j;
+ const int num_tries = (num_pairs < out_size) ? num_pairs : out_size;
+ seed += iter;
+ for (j = 0; j < num_tries; ++j) {
+ double curr_cost_diff;
+ // Choose two histograms at random and try to combine them.
+ const uint32_t idx1 = MyRand(&seed) % out_size;
+ const uint32_t tmp = (j & 7) + 1;
+ const uint32_t diff = (tmp < 3) ? tmp : MyRand(&seed) % (out_size - 1);
+ const uint32_t idx2 = (idx1 + diff + 1) % out_size;
+ if (idx1 == idx2) {
+ continue;
+ }
+ // Calculate cost reduction on combining.
+ curr_cost_diff = HistogramAddEval(out->histograms[idx1],
+ out->histograms[idx2],
+ cur_combo, best_cost_diff);
+ if (curr_cost_diff < best_cost_diff) { // found a better pair?
+ { // swap cur/best combo histograms
+ VP8LHistogram* const tmp_histo = cur_combo;
+ cur_combo = best_combo;
+ best_combo = tmp_histo;
+ }
+ best_cost_diff = curr_cost_diff;
+ best_idx1 = idx1;
+ best_idx2 = idx2;
+ }
+ }
+
+ if (best_idx1 >= 0) {
+ *out->histograms[best_idx1] = *best_combo;
+ // swap best_idx2 slot with last one (which is now unused)
+ --out_size;
+ if (best_idx2 != out_size) {
+ out->histograms[best_idx2] = out->histograms[out_size];
+ out->histograms[out_size] = NULL; // just for sanity check.
+ }
+ tries_with_no_success = 0;
+ }
+ if (++tries_with_no_success >= num_tries_no_success) {
+ break;
+ }
+ }
+ out->size = out_size;
+ ok = 1;
+
+ End:
+ free(histos);
+ return ok;
+}
+
+// -----------------------------------------------------------------------------
+// Histogram refinement
+
+// What is the bit cost of moving square_histogram from cur_symbol to candidate.
+static double HistogramDistance(const VP8LHistogram* const square_histogram,
+ const VP8LHistogram* const candidate,
+ double cost_threshold) {
+ return HistogramAddThresh(candidate, square_histogram, cost_threshold);
+}
+
+// Find the best 'out' histogram for each of the 'in' histograms.
+// Note: we assume that out[]->bit_cost_ is already up-to-date.
+static void HistogramRemap(const VP8LHistogramSet* const in,
+ const VP8LHistogramSet* const out,
+ uint16_t* const symbols) {
+ int i;
+ for (i = 0; i < in->size; ++i) {
+ int best_out = 0;
+ double best_bits =
+ HistogramDistance(in->histograms[i], out->histograms[0], 1.e38);
+ int k;
+ for (k = 1; k < out->size; ++k) {
+ const double cur_bits =
+ HistogramDistance(in->histograms[i], out->histograms[k], best_bits);
+ if (cur_bits < best_bits) {
+ best_bits = cur_bits;
+ best_out = k;
+ }
+ }
+ symbols[i] = best_out;
+ }
+
+ // Recompute each out based on raw and symbols.
+ for (i = 0; i < out->size; ++i) {
+ HistogramClear(out->histograms[i]);
+ }
+ for (i = 0; i < in->size; ++i) {
+ HistogramAdd(in->histograms[i], out->histograms[symbols[i]]);
+ }
+}
+
+int VP8LGetHistoImageSymbols(int xsize, int ysize,
+ const VP8LBackwardRefs* const refs,
+ int quality, int histo_bits, int cache_bits,
+ VP8LHistogramSet* const image_in,
+ uint16_t* const histogram_symbols) {
+ int ok = 0;
+ const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1;
+ const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1;
+ const int histo_image_raw_size = histo_xsize * histo_ysize;
+
+ // Heuristic params for HistogramCombine().
+ const int num_tries_no_success = 8 + (quality >> 1);
+ const int iter_mult = (quality < 27) ? 1 : 1 + ((quality - 27) >> 4);
+ const int num_pairs = (quality < 25) ? 10 : (5 * quality) >> 3;
+
+ VP8LHistogramSet* const image_out =
+ VP8LAllocateHistogramSet(histo_image_raw_size, cache_bits);
+ if (image_out == NULL) return 0;
+
+ // Build histogram image.
+ HistogramBuildImage(xsize, histo_bits, refs, image_out);
+ // Collapse similar histograms.
+ if (!HistogramCombine(image_out, image_in, iter_mult, num_pairs,
+ num_tries_no_success)) {
+ goto Error;
+ }
+ // Find the optimal map from original histograms to the final ones.
+ HistogramRemap(image_out, image_in, histogram_symbols);
+ ok = 1;
+
+Error:
+ free(image_out);
+ return ok;
+}
diff --git a/src/3rdparty/libwebp/src/enc/histogram.h b/src/3rdparty/libwebp/src/enc/histogram.h
new file mode 100644
index 0000000..4d346a8
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/histogram.h
@@ -0,0 +1,101 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Author: Jyrki Alakuijala (jyrki@google.com)
+//
+// Models the histograms of literal and distance codes.
+
+#ifndef WEBP_ENC_HISTOGRAM_H_
+#define WEBP_ENC_HISTOGRAM_H_
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "./backward_references.h"
+#include "../webp/format_constants.h"
+#include "../webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// A simple container for histograms of data.
+typedef struct {
+ // literal_ contains green literal, palette-code and
+ // copy-length-prefix histogram
+ int literal_[PIX_OR_COPY_CODES_MAX];
+ int red_[256];
+ int blue_[256];
+ int alpha_[256];
+ // Backward reference prefix-code histogram.
+ int distance_[NUM_DISTANCE_CODES];
+ int palette_code_bits_;
+ double bit_cost_; // cached value of VP8LHistogramEstimateBits(this)
+} VP8LHistogram;
+
+// Collection of histograms with fixed capacity, allocated as one
+// big memory chunk. Can be destroyed by simply calling 'free()'.
+typedef struct {
+ int size; // number of slots currently in use
+ int max_size; // maximum capacity
+ VP8LHistogram** histograms;
+} VP8LHistogramSet;
+
+// Create the histogram.
+//
+// The input data is the PixOrCopy data, which models the literals, stop
+// codes and backward references (both distances and lengths). Also: if
+// palette_code_bits is >= 0, initialize the histogram with this value.
+void VP8LHistogramCreate(VP8LHistogram* const p,
+ const VP8LBackwardRefs* const refs,
+ int palette_code_bits);
+
+// Set the palette_code_bits and reset the stats.
+void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits);
+
+// Collect all the references into a histogram (without reset)
+void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
+ VP8LHistogram* const histo);
+
+// Allocate an array of pointer to histograms, allocated and initialized
+// using 'cache_bits'. Return NULL in case of memory error.
+VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits);
+
+// Accumulate a token 'v' into a histogram.
+void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
+ const PixOrCopy* const v);
+
+// Estimate how many bits the combined entropy of literals and distance
+// approximately maps to.
+double VP8LHistogramEstimateBits(const VP8LHistogram* const p);
+
+// This function estimates the cost in bits excluding the bits needed to
+// represent the entropy code itself.
+double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p);
+
+static WEBP_INLINE int VP8LHistogramNumCodes(const VP8LHistogram* const p) {
+ return 256 + NUM_LENGTH_CODES +
+ ((p->palette_code_bits_ > 0) ? (1 << p->palette_code_bits_) : 0);
+}
+
+// Builds the histogram image.
+int VP8LGetHistoImageSymbols(int xsize, int ysize,
+ const VP8LBackwardRefs* const refs,
+ int quality, int histogram_bits, int cache_bits,
+ VP8LHistogramSet* const image_in,
+ uint16_t* const histogram_symbols);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // WEBP_ENC_HISTOGRAM_H_
diff --git a/src/3rdparty/libwebp/src/enc/iterator.c b/src/3rdparty/libwebp/src/enc/iterator.c
new file mode 100644
index 0000000..e42ad00
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/iterator.c
@@ -0,0 +1,456 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// VP8Iterator: block iterator
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <string.h>
+
+#include "./vp8enci.h"
+
+//------------------------------------------------------------------------------
+// VP8Iterator
+//------------------------------------------------------------------------------
+
+static void InitLeft(VP8EncIterator* const it) {
+ it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] =
+ (it->y_ > 0) ? 129 : 127;
+ memset(it->y_left_, 129, 16);
+ memset(it->u_left_, 129, 8);
+ memset(it->v_left_, 129, 8);
+ it->left_nz_[8] = 0;
+}
+
+static void InitTop(VP8EncIterator* const it) {
+ const VP8Encoder* const enc = it->enc_;
+ const size_t top_size = enc->mb_w_ * 16;
+ memset(enc->y_top_, 127, 2 * top_size);
+ memset(enc->nz_, 0, enc->mb_w_ * sizeof(*enc->nz_));
+}
+
+void VP8IteratorSetRow(VP8EncIterator* const it, int y) {
+ VP8Encoder* const enc = it->enc_;
+ it->x_ = 0;
+ it->y_ = y;
+ it->bw_ = &enc->parts_[y & (enc->num_parts_ - 1)];
+ it->preds_ = enc->preds_ + y * 4 * enc->preds_w_;
+ it->nz_ = enc->nz_;
+ it->mb_ = enc->mb_info_ + y * enc->mb_w_;
+ it->y_top_ = enc->y_top_;
+ it->uv_top_ = enc->uv_top_;
+ InitLeft(it);
+}
+
+void VP8IteratorReset(VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+ VP8IteratorSetRow(it, 0);
+ VP8IteratorSetCountDown(it, enc->mb_w_ * enc->mb_h_); // default
+ InitTop(it);
+ InitLeft(it);
+ memset(it->bit_count_, 0, sizeof(it->bit_count_));
+ it->do_trellis_ = 0;
+}
+
+void VP8IteratorSetCountDown(VP8EncIterator* const it, int count_down) {
+ it->count_down_ = it->count_down0_ = count_down;
+}
+
+int VP8IteratorIsDone(const VP8EncIterator* const it) {
+ return (it->count_down_ <= 0);
+}
+
+void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it) {
+ it->enc_ = enc;
+ it->y_stride_ = enc->pic_->y_stride;
+ it->uv_stride_ = enc->pic_->uv_stride;
+ it->yuv_in_ = (uint8_t*)DO_ALIGN(it->yuv_mem_);
+ it->yuv_out_ = it->yuv_in_ + YUV_SIZE;
+ it->yuv_out2_ = it->yuv_out_ + YUV_SIZE;
+ it->yuv_p_ = it->yuv_out2_ + YUV_SIZE;
+ it->lf_stats_ = enc->lf_stats_;
+ it->percent0_ = enc->percent_;
+ it->y_left_ = (uint8_t*)DO_ALIGN(it->yuv_left_mem_ + 1);
+ it->u_left_ = it->y_left_ + 16 + 16;
+ it->v_left_ = it->u_left_ + 16;
+ VP8IteratorReset(it);
+}
+
+int VP8IteratorProgress(const VP8EncIterator* const it, int delta) {
+ VP8Encoder* const enc = it->enc_;
+ if (delta && enc->pic_->progress_hook != NULL) {
+ const int done = it->count_down0_ - it->count_down_;
+ const int percent = (it->count_down0_ <= 0)
+ ? it->percent0_
+ : it->percent0_ + delta * done / it->count_down0_;
+ return WebPReportProgress(enc->pic_, percent, &enc->percent_);
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// Import the source samples into the cache. Takes care of replicating
+// boundary pixels if necessary.
+
+static WEBP_INLINE int MinSize(int a, int b) { return (a < b) ? a : b; }
+
+static void ImportBlock(const uint8_t* src, int src_stride,
+ uint8_t* dst, int w, int h, int size) {
+ int i;
+ for (i = 0; i < h; ++i) {
+ memcpy(dst, src, w);
+ if (w < size) {
+ memset(dst + w, dst[w - 1], size - w);
+ }
+ dst += BPS;
+ src += src_stride;
+ }
+ for (i = h; i < size; ++i) {
+ memcpy(dst, dst - BPS, size);
+ dst += BPS;
+ }
+}
+
+static void ImportLine(const uint8_t* src, int src_stride,
+ uint8_t* dst, int len, int total_len) {
+ int i;
+ for (i = 0; i < len; ++i, src += src_stride) dst[i] = *src;
+ for (; i < total_len; ++i) dst[i] = dst[len - 1];
+}
+
+void VP8IteratorImport(VP8EncIterator* const it, uint8_t* tmp_32) {
+ const VP8Encoder* const enc = it->enc_;
+ const int x = it->x_, y = it->y_;
+ const WebPPicture* const pic = enc->pic_;
+ const uint8_t* const ysrc = pic->y + (y * pic->y_stride + x) * 16;
+ const uint8_t* const usrc = pic->u + (y * pic->uv_stride + x) * 8;
+ const uint8_t* const vsrc = pic->v + (y * pic->uv_stride + x) * 8;
+ const int w = MinSize(pic->width - x * 16, 16);
+ const int h = MinSize(pic->height - y * 16, 16);
+ const int uv_w = (w + 1) >> 1;
+ const int uv_h = (h + 1) >> 1;
+
+ ImportBlock(ysrc, pic->y_stride, it->yuv_in_ + Y_OFF, w, h, 16);
+ ImportBlock(usrc, pic->uv_stride, it->yuv_in_ + U_OFF, uv_w, uv_h, 8);
+ ImportBlock(vsrc, pic->uv_stride, it->yuv_in_ + V_OFF, uv_w, uv_h, 8);
+
+ if (tmp_32 == NULL) return;
+
+ // Import source (uncompressed) samples into boundary.
+ if (x == 0) {
+ InitLeft(it);
+ } else {
+ if (y == 0) {
+ it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] = 127;
+ } else {
+ it->y_left_[-1] = ysrc[- 1 - pic->y_stride];
+ it->u_left_[-1] = usrc[- 1 - pic->uv_stride];
+ it->v_left_[-1] = vsrc[- 1 - pic->uv_stride];
+ }
+ ImportLine(ysrc - 1, pic->y_stride, it->y_left_, h, 16);
+ ImportLine(usrc - 1, pic->uv_stride, it->u_left_, uv_h, 8);
+ ImportLine(vsrc - 1, pic->uv_stride, it->v_left_, uv_h, 8);
+ }
+
+ it->y_top_ = tmp_32 + 0;
+ it->uv_top_ = tmp_32 + 16;
+ if (y == 0) {
+ memset(tmp_32, 127, 32 * sizeof(*tmp_32));
+ } else {
+ ImportLine(ysrc - pic->y_stride, 1, tmp_32, w, 16);
+ ImportLine(usrc - pic->uv_stride, 1, tmp_32 + 16, uv_w, 8);
+ ImportLine(vsrc - pic->uv_stride, 1, tmp_32 + 16 + 8, uv_w, 8);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Copy back the compressed samples into user space if requested.
+
+static void ExportBlock(const uint8_t* src, uint8_t* dst, int dst_stride,
+ int w, int h) {
+ while (h-- > 0) {
+ memcpy(dst, src, w);
+ dst += dst_stride;
+ src += BPS;
+ }
+}
+
+void VP8IteratorExport(const VP8EncIterator* const it) {
+ const VP8Encoder* const enc = it->enc_;
+ if (enc->config_->show_compressed) {
+ const int x = it->x_, y = it->y_;
+ const uint8_t* const ysrc = it->yuv_out_ + Y_OFF;
+ const uint8_t* const usrc = it->yuv_out_ + U_OFF;
+ const uint8_t* const vsrc = it->yuv_out_ + V_OFF;
+ const WebPPicture* const pic = enc->pic_;
+ uint8_t* const ydst = pic->y + (y * pic->y_stride + x) * 16;
+ uint8_t* const udst = pic->u + (y * pic->uv_stride + x) * 8;
+ uint8_t* const vdst = pic->v + (y * pic->uv_stride + x) * 8;
+ int w = (pic->width - x * 16);
+ int h = (pic->height - y * 16);
+
+ if (w > 16) w = 16;
+ if (h > 16) h = 16;
+
+ // Luma plane
+ ExportBlock(ysrc, ydst, pic->y_stride, w, h);
+
+ { // U/V planes
+ const int uv_w = (w + 1) >> 1;
+ const int uv_h = (h + 1) >> 1;
+ ExportBlock(usrc, udst, pic->uv_stride, uv_w, uv_h);
+ ExportBlock(vsrc, vdst, pic->uv_stride, uv_w, uv_h);
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Non-zero contexts setup/teardown
+
+// Nz bits:
+// 0 1 2 3 Y
+// 4 5 6 7
+// 8 9 10 11
+// 12 13 14 15
+// 16 17 U
+// 18 19
+// 20 21 V
+// 22 23
+// 24 DC-intra16
+
+// Convert packed context to byte array
+#define BIT(nz, n) (!!((nz) & (1 << (n))))
+
+void VP8IteratorNzToBytes(VP8EncIterator* const it) {
+ const int tnz = it->nz_[0], lnz = it->nz_[-1];
+ int* const top_nz = it->top_nz_;
+ int* const left_nz = it->left_nz_;
+
+ // Top-Y
+ top_nz[0] = BIT(tnz, 12);
+ top_nz[1] = BIT(tnz, 13);
+ top_nz[2] = BIT(tnz, 14);
+ top_nz[3] = BIT(tnz, 15);
+ // Top-U
+ top_nz[4] = BIT(tnz, 18);
+ top_nz[5] = BIT(tnz, 19);
+ // Top-V
+ top_nz[6] = BIT(tnz, 22);
+ top_nz[7] = BIT(tnz, 23);
+ // DC
+ top_nz[8] = BIT(tnz, 24);
+
+ // left-Y
+ left_nz[0] = BIT(lnz, 3);
+ left_nz[1] = BIT(lnz, 7);
+ left_nz[2] = BIT(lnz, 11);
+ left_nz[3] = BIT(lnz, 15);
+ // left-U
+ left_nz[4] = BIT(lnz, 17);
+ left_nz[5] = BIT(lnz, 19);
+ // left-V
+ left_nz[6] = BIT(lnz, 21);
+ left_nz[7] = BIT(lnz, 23);
+ // left-DC is special, iterated separately
+}
+
+void VP8IteratorBytesToNz(VP8EncIterator* const it) {
+ uint32_t nz = 0;
+ const int* const top_nz = it->top_nz_;
+ const int* const left_nz = it->left_nz_;
+ // top
+ nz |= (top_nz[0] << 12) | (top_nz[1] << 13);
+ nz |= (top_nz[2] << 14) | (top_nz[3] << 15);
+ nz |= (top_nz[4] << 18) | (top_nz[5] << 19);
+ nz |= (top_nz[6] << 22) | (top_nz[7] << 23);
+ nz |= (top_nz[8] << 24); // we propagate the _top_ bit, esp. for intra4
+ // left
+ nz |= (left_nz[0] << 3) | (left_nz[1] << 7);
+ nz |= (left_nz[2] << 11);
+ nz |= (left_nz[4] << 17) | (left_nz[6] << 21);
+
+ *it->nz_ = nz;
+}
+
+#undef BIT
+
+//------------------------------------------------------------------------------
+// Advance to the next position, doing the bookkeeping.
+
+void VP8IteratorSaveBoundary(VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+ const int x = it->x_, y = it->y_;
+ const uint8_t* const ysrc = it->yuv_out_ + Y_OFF;
+ const uint8_t* const uvsrc = it->yuv_out_ + U_OFF;
+ if (x < enc->mb_w_ - 1) { // left
+ int i;
+ for (i = 0; i < 16; ++i) {
+ it->y_left_[i] = ysrc[15 + i * BPS];
+ }
+ for (i = 0; i < 8; ++i) {
+ it->u_left_[i] = uvsrc[7 + i * BPS];
+ it->v_left_[i] = uvsrc[15 + i * BPS];
+ }
+ // top-left (before 'top'!)
+ it->y_left_[-1] = it->y_top_[15];
+ it->u_left_[-1] = it->uv_top_[0 + 7];
+ it->v_left_[-1] = it->uv_top_[8 + 7];
+ }
+ if (y < enc->mb_h_ - 1) { // top
+ memcpy(it->y_top_, ysrc + 15 * BPS, 16);
+ memcpy(it->uv_top_, uvsrc + 7 * BPS, 8 + 8);
+ }
+}
+
+int VP8IteratorNext(VP8EncIterator* const it) {
+ it->preds_ += 4;
+ it->mb_ += 1;
+ it->nz_ += 1;
+ it->y_top_ += 16;
+ it->uv_top_ += 16;
+ it->x_ += 1;
+ if (it->x_ == it->enc_->mb_w_) {
+ VP8IteratorSetRow(it, ++it->y_);
+ }
+ return (0 < --it->count_down_);
+}
+
+//------------------------------------------------------------------------------
+// Helper function to set mode properties
+
+void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode) {
+ uint8_t* preds = it->preds_;
+ int y;
+ for (y = 0; y < 4; ++y) {
+ memset(preds, mode, 4);
+ preds += it->enc_->preds_w_;
+ }
+ it->mb_->type_ = 1;
+}
+
+void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes) {
+ uint8_t* preds = it->preds_;
+ int y;
+ for (y = 4; y > 0; --y) {
+ memcpy(preds, modes, 4 * sizeof(*modes));
+ preds += it->enc_->preds_w_;
+ modes += 4;
+ }
+ it->mb_->type_ = 0;
+}
+
+void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode) {
+ it->mb_->uv_mode_ = mode;
+}
+
+void VP8SetSkip(const VP8EncIterator* const it, int skip) {
+ it->mb_->skip_ = skip;
+}
+
+void VP8SetSegment(const VP8EncIterator* const it, int segment) {
+ it->mb_->segment_ = segment;
+}
+
+//------------------------------------------------------------------------------
+// Intra4x4 sub-blocks iteration
+//
+// We store and update the boundary samples into an array of 37 pixels. They
+// are updated as we iterate and reconstructs each intra4x4 blocks in turn.
+// The position of the samples has the following snake pattern:
+//
+// 16|17 18 19 20|21 22 23 24|25 26 27 28|29 30 31 32|33 34 35 36 <- Top-right
+// --+-----------+-----------+-----------+-----------+
+// 15| 19| 23| 27| 31|
+// 14| 18| 22| 26| 30|
+// 13| 17| 21| 25| 29|
+// 12|13 14 15 16|17 18 19 20|21 22 23 24|25 26 27 28|
+// --+-----------+-----------+-----------+-----------+
+// 11| 15| 19| 23| 27|
+// 10| 14| 18| 22| 26|
+// 9| 13| 17| 21| 25|
+// 8| 9 10 11 12|13 14 15 16|17 18 19 20|21 22 23 24|
+// --+-----------+-----------+-----------+-----------+
+// 7| 11| 15| 19| 23|
+// 6| 10| 14| 18| 22|
+// 5| 9| 13| 17| 21|
+// 4| 5 6 7 8| 9 10 11 12|13 14 15 16|17 18 19 20|
+// --+-----------+-----------+-----------+-----------+
+// 3| 7| 11| 15| 19|
+// 2| 6| 10| 14| 18|
+// 1| 5| 9| 13| 17|
+// 0| 1 2 3 4| 5 6 7 8| 9 10 11 12|13 14 15 16|
+// --+-----------+-----------+-----------+-----------+
+
+// Array to record the position of the top sample to pass to the prediction
+// functions in dsp.c.
+static const uint8_t VP8TopLeftI4[16] = {
+ 17, 21, 25, 29,
+ 13, 17, 21, 25,
+ 9, 13, 17, 21,
+ 5, 9, 13, 17
+};
+
+void VP8IteratorStartI4(VP8EncIterator* const it) {
+ const VP8Encoder* const enc = it->enc_;
+ int i;
+
+ it->i4_ = 0; // first 4x4 sub-block
+ it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[0];
+
+ // Import the boundary samples
+ for (i = 0; i < 17; ++i) { // left
+ it->i4_boundary_[i] = it->y_left_[15 - i];
+ }
+ for (i = 0; i < 16; ++i) { // top
+ it->i4_boundary_[17 + i] = it->y_top_[i];
+ }
+ // top-right samples have a special case on the far right of the picture
+ if (it->x_ < enc->mb_w_ - 1) {
+ for (i = 16; i < 16 + 4; ++i) {
+ it->i4_boundary_[17 + i] = it->y_top_[i];
+ }
+ } else { // else, replicate the last valid pixel four times
+ for (i = 16; i < 16 + 4; ++i) {
+ it->i4_boundary_[17 + i] = it->i4_boundary_[17 + 15];
+ }
+ }
+ VP8IteratorNzToBytes(it); // import the non-zero context
+}
+
+int VP8IteratorRotateI4(VP8EncIterator* const it,
+ const uint8_t* const yuv_out) {
+ const uint8_t* const blk = yuv_out + VP8Scan[it->i4_];
+ uint8_t* const top = it->i4_top_;
+ int i;
+
+ // Update the cache with 7 fresh samples
+ for (i = 0; i <= 3; ++i) {
+ top[-4 + i] = blk[i + 3 * BPS]; // store future top samples
+ }
+ if ((it->i4_ & 3) != 3) { // if not on the right sub-blocks #3, #7, #11, #15
+ for (i = 0; i <= 2; ++i) { // store future left samples
+ top[i] = blk[3 + (2 - i) * BPS];
+ }
+ } else { // else replicate top-right samples, as says the specs.
+ for (i = 0; i <= 3; ++i) {
+ top[i] = top[i + 4];
+ }
+ }
+ // move pointers to next sub-block
+ ++it->i4_;
+ if (it->i4_ == 16) { // we're done
+ return 0;
+ }
+
+ it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[it->i4_];
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+
diff --git a/src/3rdparty/libwebp/src/enc/layer.c b/src/3rdparty/libwebp/src/enc/layer.c
new file mode 100644
index 0000000..2402362
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/layer.c
@@ -0,0 +1,44 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Enhancement layer (for YUV444/422)
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <stdlib.h>
+
+#include "./vp8enci.h"
+
+//------------------------------------------------------------------------------
+
+void VP8EncInitLayer(VP8Encoder* const enc) {
+ enc->use_layer_ = (enc->pic_->u0 != NULL);
+ enc->layer_data_size_ = 0;
+ enc->layer_data_ = NULL;
+ if (enc->use_layer_) {
+ VP8BitWriterInit(&enc->layer_bw_, enc->mb_w_ * enc->mb_h_ * 3);
+ }
+}
+
+void VP8EncCodeLayerBlock(VP8EncIterator* it) {
+ (void)it; // remove a warning
+}
+
+int VP8EncFinishLayer(VP8Encoder* const enc) {
+ if (enc->use_layer_) {
+ enc->layer_data_ = VP8BitWriterFinish(&enc->layer_bw_);
+ enc->layer_data_size_ = VP8BitWriterSize(&enc->layer_bw_);
+ }
+ return 1;
+}
+
+void VP8EncDeleteLayer(VP8Encoder* enc) {
+ free(enc->layer_data_);
+}
+
diff --git a/src/3rdparty/libwebp/src/enc/picture.c b/src/3rdparty/libwebp/src/enc/picture.c
new file mode 100644
index 0000000..011690d
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/picture.c
@@ -0,0 +1,1324 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebPPicture utils: colorspace conversion, crop, ...
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "./vp8enci.h"
+#include "../utils/alpha_processing.h"
+#include "../utils/random.h"
+#include "../utils/rescaler.h"
+#include "../utils/utils.h"
+#include "../dsp/dsp.h"
+#include "../dsp/yuv.h"
+
+// Uncomment to disable gamma-compression during RGB->U/V averaging
+#define USE_GAMMA_COMPRESSION
+
+#define HALVE(x) (((x) + 1) >> 1)
+#define IS_YUV_CSP(csp, YUV_CSP) (((csp) & WEBP_CSP_UV_MASK) == (YUV_CSP))
+
+static const union {
+ uint32_t argb;
+ uint8_t bytes[4];
+} test_endian = { 0xff000000u };
+#define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff)
+
+static WEBP_INLINE uint32_t MakeARGB32(int r, int g, int b) {
+ return (0xff000000u | (r << 16) | (g << 8) | b);
+}
+
+//------------------------------------------------------------------------------
+// WebPPicture
+//------------------------------------------------------------------------------
+
+int WebPPictureAlloc(WebPPicture* picture) {
+ if (picture != NULL) {
+ const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK;
+ const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT;
+ const int width = picture->width;
+ const int height = picture->height;
+
+ if (!picture->use_argb) {
+ const int y_stride = width;
+ const int uv_width = HALVE(width);
+ const int uv_height = HALVE(height);
+ const int uv_stride = uv_width;
+ int uv0_stride = 0;
+ int a_width, a_stride;
+ uint64_t y_size, uv_size, uv0_size, a_size, total_size;
+ uint8_t* mem;
+
+ // U/V
+ switch (uv_csp) {
+ case WEBP_YUV420:
+ break;
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+ case WEBP_YUV400: // for now, we'll just reset the U/V samples
+ break;
+ case WEBP_YUV422:
+ uv0_stride = uv_width;
+ break;
+ case WEBP_YUV444:
+ uv0_stride = width;
+ break;
+#endif
+ default:
+ return 0;
+ }
+ uv0_size = height * uv0_stride;
+
+ // alpha
+ a_width = has_alpha ? width : 0;
+ a_stride = a_width;
+ y_size = (uint64_t)y_stride * height;
+ uv_size = (uint64_t)uv_stride * uv_height;
+ a_size = (uint64_t)a_stride * height;
+
+ total_size = y_size + a_size + 2 * uv_size + 2 * uv0_size;
+
+ // Security and validation checks
+ if (width <= 0 || height <= 0 || // luma/alpha param error
+ uv_width < 0 || uv_height < 0) { // u/v param error
+ return 0;
+ }
+ // Clear previous buffer and allocate a new one.
+ WebPPictureFree(picture); // erase previous buffer
+ mem = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*mem));
+ if (mem == NULL) return 0;
+
+ // From now on, we're in the clear, we can no longer fail...
+ picture->memory_ = (void*)mem;
+ picture->y_stride = y_stride;
+ picture->uv_stride = uv_stride;
+ picture->a_stride = a_stride;
+ picture->uv0_stride = uv0_stride;
+ // TODO(skal): we could align the y/u/v planes and adjust stride.
+ picture->y = mem;
+ mem += y_size;
+
+ picture->u = mem;
+ mem += uv_size;
+ picture->v = mem;
+ mem += uv_size;
+
+ if (a_size) {
+ picture->a = mem;
+ mem += a_size;
+ }
+ if (uv0_size) {
+ picture->u0 = mem;
+ mem += uv0_size;
+ picture->v0 = mem;
+ mem += uv0_size;
+ }
+ (void)mem; // makes the static analyzer happy
+ } else {
+ void* memory;
+ const uint64_t argb_size = (uint64_t)width * height;
+ if (width <= 0 || height <= 0) {
+ return 0;
+ }
+ // Clear previous buffer and allocate a new one.
+ WebPPictureFree(picture); // erase previous buffer
+ memory = WebPSafeMalloc(argb_size, sizeof(*picture->argb));
+ if (memory == NULL) return 0;
+
+ // TODO(skal): align plane to cache line?
+ picture->memory_argb_ = memory;
+ picture->argb = (uint32_t*)memory;
+ picture->argb_stride = width;
+ }
+ }
+ return 1;
+}
+
+// Remove reference to the ARGB buffer (doesn't free anything).
+static void PictureResetARGB(WebPPicture* const picture) {
+ picture->memory_argb_ = NULL;
+ picture->argb = NULL;
+ picture->argb_stride = 0;
+}
+
+// Remove reference to the YUVA buffer (doesn't free anything).
+static void PictureResetYUVA(WebPPicture* const picture) {
+ picture->memory_ = NULL;
+ picture->y = picture->u = picture->v = picture->a = NULL;
+ picture->u0 = picture->v0 = NULL;
+ picture->y_stride = picture->uv_stride = 0;
+ picture->a_stride = 0;
+ picture->uv0_stride = 0;
+}
+
+// Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them
+// into 'dst'. Mark 'dst' as not owning any memory.
+static void WebPPictureGrabSpecs(const WebPPicture* const src,
+ WebPPicture* const dst) {
+ assert(src != NULL && dst != NULL);
+ *dst = *src;
+ PictureResetYUVA(dst);
+ PictureResetARGB(dst);
+}
+
+// Allocate a new argb buffer, discarding any existing one and preserving
+// the other YUV(A) buffer.
+static int PictureAllocARGB(WebPPicture* const picture) {
+ WebPPicture tmp;
+ free(picture->memory_argb_);
+ PictureResetARGB(picture);
+ picture->use_argb = 1;
+ WebPPictureGrabSpecs(picture, &tmp);
+ if (!WebPPictureAlloc(&tmp)) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
+ picture->memory_argb_ = tmp.memory_argb_;
+ picture->argb = tmp.argb;
+ picture->argb_stride = tmp.argb_stride;
+ return 1;
+}
+
+// Release memory owned by 'picture' (both YUV and ARGB buffers).
+void WebPPictureFree(WebPPicture* picture) {
+ if (picture != NULL) {
+ free(picture->memory_);
+ free(picture->memory_argb_);
+ PictureResetYUVA(picture);
+ PictureResetARGB(picture);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Picture copying
+
+// Not worth moving to dsp/enc.c (only used here).
+static void CopyPlane(const uint8_t* src, int src_stride,
+ uint8_t* dst, int dst_stride, int width, int height) {
+ while (height-- > 0) {
+ memcpy(dst, src, width);
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+// Adjust top-left corner to chroma sample position.
+static void SnapTopLeftPosition(const WebPPicture* const pic,
+ int* const left, int* const top) {
+ if (!pic->use_argb) {
+ const int is_yuv422 = IS_YUV_CSP(pic->colorspace, WEBP_YUV422);
+ if (IS_YUV_CSP(pic->colorspace, WEBP_YUV420) || is_yuv422) {
+ *left &= ~1;
+ if (!is_yuv422) *top &= ~1;
+ }
+ }
+}
+
+// Adjust top-left corner and verify that the sub-rectangle is valid.
+static int AdjustAndCheckRectangle(const WebPPicture* const pic,
+ int* const left, int* const top,
+ int width, int height) {
+ SnapTopLeftPosition(pic, left, top);
+ if ((*left) < 0 || (*top) < 0) return 0;
+ if (width <= 0 || height <= 0) return 0;
+ if ((*left) + width > pic->width) return 0;
+ if ((*top) + height > pic->height) return 0;
+ return 1;
+}
+
+int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
+ if (src == NULL || dst == NULL) return 0;
+ if (src == dst) return 1;
+
+ WebPPictureGrabSpecs(src, dst);
+ if (!WebPPictureAlloc(dst)) return 0;
+
+ if (!src->use_argb) {
+ CopyPlane(src->y, src->y_stride,
+ dst->y, dst->y_stride, dst->width, dst->height);
+ CopyPlane(src->u, src->uv_stride,
+ dst->u, dst->uv_stride, HALVE(dst->width), HALVE(dst->height));
+ CopyPlane(src->v, src->uv_stride,
+ dst->v, dst->uv_stride, HALVE(dst->width), HALVE(dst->height));
+ if (dst->a != NULL) {
+ CopyPlane(src->a, src->a_stride,
+ dst->a, dst->a_stride, dst->width, dst->height);
+ }
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+ if (dst->u0 != NULL) {
+ int uv0_width = src->width;
+ if (IS_YUV_CSP(dst->colorspace, WEBP_YUV422)) {
+ uv0_width = HALVE(uv0_width);
+ }
+ CopyPlane(src->u0, src->uv0_stride,
+ dst->u0, dst->uv0_stride, uv0_width, dst->height);
+ CopyPlane(src->v0, src->uv0_stride,
+ dst->v0, dst->uv0_stride, uv0_width, dst->height);
+ }
+#endif
+ } else {
+ CopyPlane((const uint8_t*)src->argb, 4 * src->argb_stride,
+ (uint8_t*)dst->argb, 4 * dst->argb_stride,
+ 4 * dst->width, dst->height);
+ }
+ return 1;
+}
+
+int WebPPictureIsView(const WebPPicture* picture) {
+ if (picture == NULL) return 0;
+ if (picture->use_argb) {
+ return (picture->memory_argb_ == NULL);
+ }
+ return (picture->memory_ == NULL);
+}
+
+int WebPPictureView(const WebPPicture* src,
+ int left, int top, int width, int height,
+ WebPPicture* dst) {
+ if (src == NULL || dst == NULL) return 0;
+
+ // verify rectangle position.
+ if (!AdjustAndCheckRectangle(src, &left, &top, width, height)) return 0;
+
+ if (src != dst) { // beware of aliasing! We don't want to leak 'memory_'.
+ WebPPictureGrabSpecs(src, dst);
+ }
+ dst->width = width;
+ dst->height = height;
+ if (!src->use_argb) {
+ dst->y = src->y + top * src->y_stride + left;
+ dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1);
+ dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1);
+ dst->y_stride = src->y_stride;
+ dst->uv_stride = src->uv_stride;
+ if (src->a != NULL) {
+ dst->a = src->a + top * src->a_stride + left;
+ dst->a_stride = src->a_stride;
+ }
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+ if (src->u0 != NULL) {
+ const int left_pos =
+ IS_YUV_CSP(dst->colorspace, WEBP_YUV422) ? (left >> 1) : left;
+ dst->u0 = src->u0 + top * src->uv0_stride + left_pos;
+ dst->v0 = src->v0 + top * src->uv0_stride + left_pos;
+ dst->uv0_stride = src->uv0_stride;
+ }
+#endif
+ } else {
+ dst->argb = src->argb + top * src->argb_stride + left;
+ dst->argb_stride = src->argb_stride;
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// Picture cropping
+
+int WebPPictureCrop(WebPPicture* pic,
+ int left, int top, int width, int height) {
+ WebPPicture tmp;
+
+ if (pic == NULL) return 0;
+ if (!AdjustAndCheckRectangle(pic, &left, &top, width, height)) return 0;
+
+ WebPPictureGrabSpecs(pic, &tmp);
+ tmp.width = width;
+ tmp.height = height;
+ if (!WebPPictureAlloc(&tmp)) return 0;
+
+ if (!pic->use_argb) {
+ const int y_offset = top * pic->y_stride + left;
+ const int uv_offset = (top / 2) * pic->uv_stride + left / 2;
+ CopyPlane(pic->y + y_offset, pic->y_stride,
+ tmp.y, tmp.y_stride, width, height);
+ CopyPlane(pic->u + uv_offset, pic->uv_stride,
+ tmp.u, tmp.uv_stride, HALVE(width), HALVE(height));
+ CopyPlane(pic->v + uv_offset, pic->uv_stride,
+ tmp.v, tmp.uv_stride, HALVE(width), HALVE(height));
+
+ if (tmp.a != NULL) {
+ const int a_offset = top * pic->a_stride + left;
+ CopyPlane(pic->a + a_offset, pic->a_stride,
+ tmp.a, tmp.a_stride, width, height);
+ }
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+ if (tmp.u0 != NULL) {
+ int w = width;
+ int left_pos = left;
+ if (IS_YUV_CSP(tmp.colorspace, WEBP_YUV422)) {
+ w = HALVE(w);
+ left_pos = HALVE(left_pos);
+ }
+ CopyPlane(pic->u0 + top * pic->uv0_stride + left_pos, pic->uv0_stride,
+ tmp.u0, tmp.uv0_stride, w, height);
+ CopyPlane(pic->v0 + top * pic->uv0_stride + left_pos, pic->uv0_stride,
+ tmp.v0, tmp.uv0_stride, w, height);
+ }
+#endif
+ } else {
+ const uint8_t* const src =
+ (const uint8_t*)(pic->argb + top * pic->argb_stride + left);
+ CopyPlane(src, pic->argb_stride * 4,
+ (uint8_t*)tmp.argb, tmp.argb_stride * 4,
+ width * 4, height);
+ }
+ WebPPictureFree(pic);
+ *pic = tmp;
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// Simple picture rescaler
+
+static void RescalePlane(const uint8_t* src,
+ int src_width, int src_height, int src_stride,
+ uint8_t* dst,
+ int dst_width, int dst_height, int dst_stride,
+ int32_t* const work,
+ int num_channels) {
+ WebPRescaler rescaler;
+ int y = 0;
+ WebPRescalerInit(&rescaler, src_width, src_height,
+ dst, dst_width, dst_height, dst_stride,
+ num_channels,
+ src_width, dst_width,
+ src_height, dst_height,
+ work);
+ memset(work, 0, 2 * dst_width * num_channels * sizeof(*work));
+ while (y < src_height) {
+ y += WebPRescalerImport(&rescaler, src_height - y,
+ src + y * src_stride, src_stride);
+ WebPRescalerExport(&rescaler);
+ }
+}
+
+static void AlphaMultiplyARGB(WebPPicture* const pic, int inverse) {
+ uint32_t* ptr = pic->argb;
+ int y;
+ for (y = 0; y < pic->height; ++y) {
+ WebPMultARGBRow(ptr, pic->width, inverse);
+ ptr += pic->argb_stride;
+ }
+}
+
+static void AlphaMultiplyY(WebPPicture* const pic, int inverse) {
+ const uint8_t* ptr_a = pic->a;
+ if (ptr_a != NULL) {
+ uint8_t* ptr_y = pic->y;
+ int y;
+ for (y = 0; y < pic->height; ++y) {
+ WebPMultRow(ptr_y, ptr_a, pic->width, inverse);
+ ptr_y += pic->y_stride;
+ ptr_a += pic->a_stride;
+ }
+ }
+}
+
+int WebPPictureRescale(WebPPicture* pic, int width, int height) {
+ WebPPicture tmp;
+ int prev_width, prev_height;
+ int32_t* work;
+
+ if (pic == NULL) return 0;
+ prev_width = pic->width;
+ prev_height = pic->height;
+ // if width is unspecified, scale original proportionally to height ratio.
+ if (width == 0) {
+ width = (prev_width * height + prev_height / 2) / prev_height;
+ }
+ // if height is unspecified, scale original proportionally to width ratio.
+ if (height == 0) {
+ height = (prev_height * width + prev_width / 2) / prev_width;
+ }
+ // Check if the overall dimensions still make sense.
+ if (width <= 0 || height <= 0) return 0;
+
+ WebPPictureGrabSpecs(pic, &tmp);
+ tmp.width = width;
+ tmp.height = height;
+ if (!WebPPictureAlloc(&tmp)) return 0;
+
+ if (!pic->use_argb) {
+ work = (int32_t*)WebPSafeMalloc(2ULL * width, sizeof(*work));
+ if (work == NULL) {
+ WebPPictureFree(&tmp);
+ return 0;
+ }
+ // If present, we need to rescale alpha first (for AlphaMultiplyY).
+ if (pic->a != NULL) {
+ RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
+ tmp.a, width, height, tmp.a_stride, work, 1);
+ }
+
+ // We take transparency into account on the luma plane only. That's not
+ // totally exact blending, but still is a good approximation.
+ AlphaMultiplyY(pic, 0);
+ RescalePlane(pic->y, prev_width, prev_height, pic->y_stride,
+ tmp.y, width, height, tmp.y_stride, work, 1);
+ AlphaMultiplyY(&tmp, 1);
+
+ RescalePlane(pic->u,
+ HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
+ tmp.u,
+ HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
+ RescalePlane(pic->v,
+ HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
+ tmp.v,
+ HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
+
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+ if (tmp.u0 != NULL) {
+ const int s = IS_YUV_CSP(tmp.colorspace, WEBP_YUV422) ? 2 : 1;
+ RescalePlane(
+ pic->u0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride,
+ tmp.u0, (width + s / 2) / s, height, tmp.uv0_stride, work, 1);
+ RescalePlane(
+ pic->v0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride,
+ tmp.v0, (width + s / 2) / s, height, tmp.uv0_stride, work, 1);
+ }
+#endif
+ } else {
+ work = (int32_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work));
+ if (work == NULL) {
+ WebPPictureFree(&tmp);
+ return 0;
+ }
+ // In order to correctly interpolate colors, we need to apply the alpha
+ // weighting first (black-matting), scale the RGB values, and remove
+ // the premultiplication afterward (while preserving the alpha channel).
+ AlphaMultiplyARGB(pic, 0);
+ RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height,
+ pic->argb_stride * 4,
+ (uint8_t*)tmp.argb, width, height,
+ tmp.argb_stride * 4,
+ work, 4);
+ AlphaMultiplyARGB(&tmp, 1);
+ }
+ WebPPictureFree(pic);
+ free(work);
+ *pic = tmp;
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// WebPMemoryWriter: Write-to-memory
+
+void WebPMemoryWriterInit(WebPMemoryWriter* writer) {
+ writer->mem = NULL;
+ writer->size = 0;
+ writer->max_size = 0;
+}
+
+int WebPMemoryWrite(const uint8_t* data, size_t data_size,
+ const WebPPicture* picture) {
+ WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr;
+ uint64_t next_size;
+ if (w == NULL) {
+ return 1;
+ }
+ next_size = (uint64_t)w->size + data_size;
+ if (next_size > w->max_size) {
+ uint8_t* new_mem;
+ uint64_t next_max_size = 2ULL * w->max_size;
+ if (next_max_size < next_size) next_max_size = next_size;
+ if (next_max_size < 8192ULL) next_max_size = 8192ULL;
+ new_mem = (uint8_t*)WebPSafeMalloc(next_max_size, 1);
+ if (new_mem == NULL) {
+ return 0;
+ }
+ if (w->size > 0) {
+ memcpy(new_mem, w->mem, w->size);
+ }
+ free(w->mem);
+ w->mem = new_mem;
+ // down-cast is ok, thanks to WebPSafeMalloc
+ w->max_size = (size_t)next_max_size;
+ }
+ if (data_size > 0) {
+ memcpy(w->mem + w->size, data, data_size);
+ w->size += data_size;
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// Detection of non-trivial transparency
+
+// Returns true if alpha[] has non-0xff values.
+static int CheckNonOpaque(const uint8_t* alpha, int width, int height,
+ int x_step, int y_step) {
+ if (alpha == NULL) return 0;
+ while (height-- > 0) {
+ int x;
+ for (x = 0; x < width * x_step; x += x_step) {
+ if (alpha[x] != 0xff) return 1; // TODO(skal): check 4/8 bytes at a time.
+ }
+ alpha += y_step;
+ }
+ return 0;
+}
+
+// Checking for the presence of non-opaque alpha.
+int WebPPictureHasTransparency(const WebPPicture* picture) {
+ if (picture == NULL) return 0;
+ if (!picture->use_argb) {
+ return CheckNonOpaque(picture->a, picture->width, picture->height,
+ 1, picture->a_stride);
+ } else {
+ int x, y;
+ const uint32_t* argb = picture->argb;
+ if (argb == NULL) return 0;
+ for (y = 0; y < picture->height; ++y) {
+ for (x = 0; x < picture->width; ++x) {
+ if (argb[x] < 0xff000000u) return 1; // test any alpha values != 0xff
+ }
+ argb += picture->argb_stride;
+ }
+ }
+ return 0;
+}
+
+//------------------------------------------------------------------------------
+// RGB -> YUV conversion
+
+static int RGBToY(int r, int g, int b, VP8Random* const rg) {
+ return VP8RGBToY(r, g, b, VP8RandomBits(rg, YUV_FIX));
+}
+
+static int RGBToU(int r, int g, int b, VP8Random* const rg) {
+ return VP8RGBToU(r, g, b, VP8RandomBits(rg, YUV_FIX + 2));
+}
+
+static int RGBToV(int r, int g, int b, VP8Random* const rg) {
+ return VP8RGBToV(r, g, b, VP8RandomBits(rg, YUV_FIX + 2));
+}
+
+//------------------------------------------------------------------------------
+
+#if defined(USE_GAMMA_COMPRESSION)
+
+// gamma-compensates loss of resolution during chroma subsampling
+#define kGamma 0.80
+#define kGammaFix 12 // fixed-point precision for linear values
+#define kGammaScale ((1 << kGammaFix) - 1)
+#define kGammaTabFix 7 // fixed-point fractional bits precision
+#define kGammaTabScale (1 << kGammaTabFix)
+#define kGammaTabRounder (kGammaTabScale >> 1)
+#define kGammaTabSize (1 << (kGammaFix - kGammaTabFix))
+
+static int kLinearToGammaTab[kGammaTabSize + 1];
+static uint16_t kGammaToLinearTab[256];
+static int kGammaTablesOk = 0;
+
+static void InitGammaTables(void) {
+ if (!kGammaTablesOk) {
+ int v;
+ const double scale = 1. / kGammaScale;
+ for (v = 0; v <= 255; ++v) {
+ kGammaToLinearTab[v] =
+ (uint16_t)(pow(v / 255., kGamma) * kGammaScale + .5);
+ }
+ for (v = 0; v <= kGammaTabSize; ++v) {
+ const double x = scale * (v << kGammaTabFix);
+ kLinearToGammaTab[v] = (int)(pow(x, 1. / kGamma) * 255. + .5);
+ }
+ kGammaTablesOk = 1;
+ }
+}
+
+static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) {
+ return kGammaToLinearTab[v];
+}
+
+// Convert a linear value 'v' to YUV_FIX+2 fixed-point precision
+// U/V value, suitable for RGBToU/V calls.
+static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) {
+ const int v = base_value << shift; // final uplifted value
+ const int tab_pos = v >> (kGammaTabFix + 2); // integer part
+ const int x = v & ((kGammaTabScale << 2) - 1); // fractional part
+ const int v0 = kLinearToGammaTab[tab_pos];
+ const int v1 = kLinearToGammaTab[tab_pos + 1];
+ const int y = v1 * x + v0 * ((kGammaTabScale << 2) - x); // interpolate
+ return (y + kGammaTabRounder) >> kGammaTabFix; // descale
+}
+
+#else
+
+static void InitGammaTables(void) {}
+static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { return v; }
+static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) {
+ (void)shift;
+ return v;
+}
+
+#endif // USE_GAMMA_COMPRESSION
+
+//------------------------------------------------------------------------------
+
+#define SUM4(ptr) LinearToGamma( \
+ GammaToLinear((ptr)[0]) + \
+ GammaToLinear((ptr)[step]) + \
+ GammaToLinear((ptr)[rgb_stride]) + \
+ GammaToLinear((ptr)[rgb_stride + step]), 0) \
+
+#define SUM2H(ptr) \
+ LinearToGamma(GammaToLinear((ptr)[0]) + GammaToLinear((ptr)[step]), 1)
+#define SUM2V(ptr) \
+ LinearToGamma(GammaToLinear((ptr)[0]) + GammaToLinear((ptr)[rgb_stride]), 1)
+#define SUM1(ptr) \
+ LinearToGamma(GammaToLinear((ptr)[0]), 2)
+
+#define RGB_TO_UV(x, y, SUM) { \
+ const int src = (2 * (step * (x) + (y) * rgb_stride)); \
+ const int dst = (x) + (y) * picture->uv_stride; \
+ const int r = SUM(r_ptr + src); \
+ const int g = SUM(g_ptr + src); \
+ const int b = SUM(b_ptr + src); \
+ picture->u[dst] = RGBToU(r, g, b, &rg); \
+ picture->v[dst] = RGBToV(r, g, b, &rg); \
+}
+
+#define RGB_TO_UV0(x_in, x_out, y, SUM) { \
+ const int src = (step * (x_in) + (y) * rgb_stride); \
+ const int dst = (x_out) + (y) * picture->uv0_stride; \
+ const int r = SUM(r_ptr + src); \
+ const int g = SUM(g_ptr + src); \
+ const int b = SUM(b_ptr + src); \
+ picture->u0[dst] = RGBToU(r, g, b, &rg); \
+ picture->v0[dst] = RGBToV(r, g, b, &rg); \
+}
+
+static void MakeGray(WebPPicture* const picture) {
+ int y;
+ const int uv_width = HALVE(picture->width);
+ const int uv_height = HALVE(picture->height);
+ for (y = 0; y < uv_height; ++y) {
+ memset(picture->u + y * picture->uv_stride, 128, uv_width);
+ memset(picture->v + y * picture->uv_stride, 128, uv_width);
+ }
+}
+
+static int ImportYUVAFromRGBA(const uint8_t* const r_ptr,
+ const uint8_t* const g_ptr,
+ const uint8_t* const b_ptr,
+ const uint8_t* const a_ptr,
+ int step, // bytes per pixel
+ int rgb_stride, // bytes per scanline
+ float dithering,
+ WebPPicture* const picture) {
+ const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK;
+ int x, y;
+ const int width = picture->width;
+ const int height = picture->height;
+ const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride);
+ VP8Random rg;
+
+ picture->colorspace = uv_csp;
+ picture->use_argb = 0;
+ if (has_alpha) {
+ picture->colorspace |= WEBP_CSP_ALPHA_BIT;
+ }
+ if (!WebPPictureAlloc(picture)) return 0;
+
+ VP8InitRandom(&rg, dithering);
+ InitGammaTables();
+
+ // Import luma plane
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ const int offset = step * x + y * rgb_stride;
+ picture->y[x + y * picture->y_stride] =
+ RGBToY(r_ptr[offset], g_ptr[offset], b_ptr[offset], &rg);
+ }
+ }
+
+ // Downsample U/V plane
+ if (uv_csp != WEBP_YUV400) {
+ for (y = 0; y < (height >> 1); ++y) {
+ for (x = 0; x < (width >> 1); ++x) {
+ RGB_TO_UV(x, y, SUM4);
+ }
+ if (width & 1) {
+ RGB_TO_UV(x, y, SUM2V);
+ }
+ }
+ if (height & 1) {
+ for (x = 0; x < (width >> 1); ++x) {
+ RGB_TO_UV(x, y, SUM2H);
+ }
+ if (width & 1) {
+ RGB_TO_UV(x, y, SUM1);
+ }
+ }
+
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+ // Store original U/V samples too
+ if (uv_csp == WEBP_YUV422) {
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < (width >> 1); ++x) {
+ RGB_TO_UV0(2 * x, x, y, SUM2H);
+ }
+ if (width & 1) {
+ RGB_TO_UV0(2 * x, x, y, SUM1);
+ }
+ }
+ } else if (uv_csp == WEBP_YUV444) {
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ RGB_TO_UV0(x, x, y, SUM1);
+ }
+ }
+ }
+#endif
+ } else {
+ MakeGray(picture);
+ }
+
+ if (has_alpha) {
+ assert(step >= 4);
+ assert(picture->a != NULL);
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ picture->a[x + y * picture->a_stride] =
+ a_ptr[step * x + y * rgb_stride];
+ }
+ }
+ }
+ return 1;
+}
+
+static int Import(WebPPicture* const picture,
+ const uint8_t* const rgb, int rgb_stride,
+ int step, int swap_rb, int import_alpha) {
+ const uint8_t* const r_ptr = rgb + (swap_rb ? 2 : 0);
+ const uint8_t* const g_ptr = rgb + 1;
+ const uint8_t* const b_ptr = rgb + (swap_rb ? 0 : 2);
+ const uint8_t* const a_ptr = import_alpha ? rgb + 3 : NULL;
+ const int width = picture->width;
+ const int height = picture->height;
+
+ if (!picture->use_argb) {
+ return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride,
+ 0.f /* no dithering */, picture);
+ }
+ if (import_alpha) {
+ picture->colorspace |= WEBP_CSP_ALPHA_BIT;
+ } else {
+ picture->colorspace &= ~WEBP_CSP_ALPHA_BIT;
+ }
+ if (!WebPPictureAlloc(picture)) return 0;
+
+ if (!import_alpha) {
+ int x, y;
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ const int offset = step * x + y * rgb_stride;
+ const uint32_t argb =
+ MakeARGB32(r_ptr[offset], g_ptr[offset], b_ptr[offset]);
+ picture->argb[x + y * picture->argb_stride] = argb;
+ }
+ }
+ } else {
+ int x, y;
+ assert(step >= 4);
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ const int offset = step * x + y * rgb_stride;
+ const uint32_t argb = ((uint32_t)a_ptr[offset] << 24) |
+ (r_ptr[offset] << 16) |
+ (g_ptr[offset] << 8) |
+ (b_ptr[offset]);
+ picture->argb[x + y * picture->argb_stride] = argb;
+ }
+ }
+ }
+ return 1;
+}
+#undef SUM4
+#undef SUM2V
+#undef SUM2H
+#undef SUM1
+#undef RGB_TO_UV
+
+int WebPPictureImportRGB(WebPPicture* picture,
+ const uint8_t* rgb, int rgb_stride) {
+ return Import(picture, rgb, rgb_stride, 3, 0, 0);
+}
+
+int WebPPictureImportBGR(WebPPicture* picture,
+ const uint8_t* rgb, int rgb_stride) {
+ return Import(picture, rgb, rgb_stride, 3, 1, 0);
+}
+
+int WebPPictureImportRGBA(WebPPicture* picture,
+ const uint8_t* rgba, int rgba_stride) {
+ return Import(picture, rgba, rgba_stride, 4, 0, 1);
+}
+
+int WebPPictureImportBGRA(WebPPicture* picture,
+ const uint8_t* rgba, int rgba_stride) {
+ return Import(picture, rgba, rgba_stride, 4, 1, 1);
+}
+
+int WebPPictureImportRGBX(WebPPicture* picture,
+ const uint8_t* rgba, int rgba_stride) {
+ return Import(picture, rgba, rgba_stride, 4, 0, 0);
+}
+
+int WebPPictureImportBGRX(WebPPicture* picture,
+ const uint8_t* rgba, int rgba_stride) {
+ return Import(picture, rgba, rgba_stride, 4, 1, 0);
+}
+
+//------------------------------------------------------------------------------
+// Automatic YUV <-> ARGB conversions.
+
+int WebPPictureYUVAToARGB(WebPPicture* picture) {
+ if (picture == NULL) return 0;
+ if (picture->y == NULL || picture->u == NULL || picture->v == NULL) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
+ }
+ if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
+ }
+ if ((picture->colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
+ }
+ // Allocate a new argb buffer (discarding the previous one).
+ if (!PictureAllocARGB(picture)) return 0;
+
+ // Convert
+ {
+ int y;
+ const int width = picture->width;
+ const int height = picture->height;
+ const int argb_stride = 4 * picture->argb_stride;
+ uint8_t* dst = (uint8_t*)picture->argb;
+ const uint8_t *cur_u = picture->u, *cur_v = picture->v, *cur_y = picture->y;
+ WebPUpsampleLinePairFunc upsample = WebPGetLinePairConverter(ALPHA_IS_LAST);
+
+ // First row, with replicated top samples.
+ upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width);
+ cur_y += picture->y_stride;
+ dst += argb_stride;
+ // Center rows.
+ for (y = 1; y + 1 < height; y += 2) {
+ const uint8_t* const top_u = cur_u;
+ const uint8_t* const top_v = cur_v;
+ cur_u += picture->uv_stride;
+ cur_v += picture->uv_stride;
+ upsample(cur_y, cur_y + picture->y_stride, top_u, top_v, cur_u, cur_v,
+ dst, dst + argb_stride, width);
+ cur_y += 2 * picture->y_stride;
+ dst += 2 * argb_stride;
+ }
+ // Last row (if needed), with replicated bottom samples.
+ if (height > 1 && !(height & 1)) {
+ upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width);
+ }
+ // Insert alpha values if needed, in replacement for the default 0xff ones.
+ if (picture->colorspace & WEBP_CSP_ALPHA_BIT) {
+ for (y = 0; y < height; ++y) {
+ uint32_t* const argb_dst = picture->argb + y * picture->argb_stride;
+ const uint8_t* const src = picture->a + y * picture->a_stride;
+ int x;
+ for (x = 0; x < width; ++x) {
+ argb_dst[x] = (argb_dst[x] & 0x00ffffffu) | ((uint32_t)src[x] << 24);
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace,
+ float dithering) {
+ if (picture == NULL) return 0;
+ if (picture->argb == NULL) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
+ } else {
+ const uint8_t* const argb = (const uint8_t*)picture->argb;
+ const uint8_t* const r = ALPHA_IS_LAST ? argb + 2 : argb + 1;
+ const uint8_t* const g = ALPHA_IS_LAST ? argb + 1 : argb + 2;
+ const uint8_t* const b = ALPHA_IS_LAST ? argb + 0 : argb + 3;
+ const uint8_t* const a = ALPHA_IS_LAST ? argb + 3 : argb + 0;
+ // We work on a tmp copy of 'picture', because ImportYUVAFromRGBA()
+ // would be calling WebPPictureFree(picture) otherwise.
+ WebPPicture tmp = *picture;
+ PictureResetARGB(&tmp); // reset ARGB buffer so that it's not free()'d.
+ tmp.use_argb = 0;
+ tmp.colorspace = colorspace & WEBP_CSP_UV_MASK;
+ if (!ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride, dithering,
+ &tmp)) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
+ // Copy back the YUV specs into 'picture'.
+ tmp.argb = picture->argb;
+ tmp.argb_stride = picture->argb_stride;
+ tmp.memory_argb_ = picture->memory_argb_;
+ *picture = tmp;
+ }
+ return 1;
+}
+
+int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) {
+ return WebPPictureARGBToYUVADithered(picture, colorspace, 0.f);
+}
+
+//------------------------------------------------------------------------------
+// Helper: clean up fully transparent area to help compressibility.
+
+#define SIZE 8
+#define SIZE2 (SIZE / 2)
+static int is_transparent_area(const uint8_t* ptr, int stride, int size) {
+ int y, x;
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size; ++x) {
+ if (ptr[x]) {
+ return 0;
+ }
+ }
+ ptr += stride;
+ }
+ return 1;
+}
+
+static WEBP_INLINE void flatten(uint8_t* ptr, int v, int stride, int size) {
+ int y;
+ for (y = 0; y < size; ++y) {
+ memset(ptr, v, size);
+ ptr += stride;
+ }
+}
+
+void WebPCleanupTransparentArea(WebPPicture* pic) {
+ int x, y, w, h;
+ const uint8_t* a_ptr;
+ int values[3] = { 0 };
+
+ if (pic == NULL) return;
+
+ a_ptr = pic->a;
+ if (a_ptr == NULL) return; // nothing to do
+
+ w = pic->width / SIZE;
+ h = pic->height / SIZE;
+ for (y = 0; y < h; ++y) {
+ int need_reset = 1;
+ for (x = 0; x < w; ++x) {
+ const int off_a = (y * pic->a_stride + x) * SIZE;
+ const int off_y = (y * pic->y_stride + x) * SIZE;
+ const int off_uv = (y * pic->uv_stride + x) * SIZE2;
+ if (is_transparent_area(a_ptr + off_a, pic->a_stride, SIZE)) {
+ if (need_reset) {
+ values[0] = pic->y[off_y];
+ values[1] = pic->u[off_uv];
+ values[2] = pic->v[off_uv];
+ need_reset = 0;
+ }
+ flatten(pic->y + off_y, values[0], pic->y_stride, SIZE);
+ flatten(pic->u + off_uv, values[1], pic->uv_stride, SIZE2);
+ flatten(pic->v + off_uv, values[2], pic->uv_stride, SIZE2);
+ } else {
+ need_reset = 1;
+ }
+ }
+ // ignore the left-overs on right/bottom
+ }
+}
+
+#undef SIZE
+#undef SIZE2
+
+//------------------------------------------------------------------------------
+// Blend color and remove transparency info
+
+#define BLEND(V0, V1, ALPHA) \
+ ((((V0) * (255 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 16)
+#define BLEND_10BIT(V0, V1, ALPHA) \
+ ((((V0) * (1020 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 18)
+
+void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) {
+ const int red = (background_rgb >> 16) & 0xff;
+ const int green = (background_rgb >> 8) & 0xff;
+ const int blue = (background_rgb >> 0) & 0xff;
+ VP8Random rg;
+ int x, y;
+ if (pic == NULL) return;
+ VP8InitRandom(&rg, 0.f);
+ if (!pic->use_argb) {
+ const int uv_width = (pic->width >> 1); // omit last pixel during u/v loop
+ const int Y0 = RGBToY(red, green, blue, &rg);
+ // VP8RGBToU/V expects the u/v values summed over four pixels
+ const int U0 = RGBToU(4 * red, 4 * green, 4 * blue, &rg);
+ const int V0 = RGBToV(4 * red, 4 * green, 4 * blue, &rg);
+ const int has_alpha = pic->colorspace & WEBP_CSP_ALPHA_BIT;
+ if (!has_alpha || pic->a == NULL) return; // nothing to do
+ for (y = 0; y < pic->height; ++y) {
+ // Luma blending
+ uint8_t* const y_ptr = pic->y + y * pic->y_stride;
+ uint8_t* const a_ptr = pic->a + y * pic->a_stride;
+ for (x = 0; x < pic->width; ++x) {
+ const int alpha = a_ptr[x];
+ if (alpha < 0xff) {
+ y_ptr[x] = BLEND(Y0, y_ptr[x], a_ptr[x]);
+ }
+ }
+ // Chroma blending every even line
+ if ((y & 1) == 0) {
+ uint8_t* const u = pic->u + (y >> 1) * pic->uv_stride;
+ uint8_t* const v = pic->v + (y >> 1) * pic->uv_stride;
+ uint8_t* const a_ptr2 =
+ (y + 1 == pic->height) ? a_ptr : a_ptr + pic->a_stride;
+ for (x = 0; x < uv_width; ++x) {
+ // Average four alpha values into a single blending weight.
+ // TODO(skal): might lead to visible contouring. Can we do better?
+ const int alpha =
+ a_ptr[2 * x + 0] + a_ptr[2 * x + 1] +
+ a_ptr2[2 * x + 0] + a_ptr2[2 * x + 1];
+ u[x] = BLEND_10BIT(U0, u[x], alpha);
+ v[x] = BLEND_10BIT(V0, v[x], alpha);
+ }
+ if (pic->width & 1) { // rightmost pixel
+ const int alpha = 2 * (a_ptr[2 * x + 0] + a_ptr2[2 * x + 0]);
+ u[x] = BLEND_10BIT(U0, u[x], alpha);
+ v[x] = BLEND_10BIT(V0, v[x], alpha);
+ }
+ }
+ memset(a_ptr, 0xff, pic->width);
+ }
+ } else {
+ uint32_t* argb = pic->argb;
+ const uint32_t background = MakeARGB32(red, green, blue);
+ for (y = 0; y < pic->height; ++y) {
+ for (x = 0; x < pic->width; ++x) {
+ const int alpha = (argb[x] >> 24) & 0xff;
+ if (alpha != 0xff) {
+ if (alpha > 0) {
+ int r = (argb[x] >> 16) & 0xff;
+ int g = (argb[x] >> 8) & 0xff;
+ int b = (argb[x] >> 0) & 0xff;
+ r = BLEND(red, r, alpha);
+ g = BLEND(green, g, alpha);
+ b = BLEND(blue, b, alpha);
+ argb[x] = MakeARGB32(r, g, b);
+ } else {
+ argb[x] = background;
+ }
+ }
+ }
+ argb += pic->argb_stride;
+ }
+ }
+}
+
+#undef BLEND
+#undef BLEND_10BIT
+
+//------------------------------------------------------------------------------
+// local-min distortion
+//
+// For every pixel in the *reference* picture, we search for the local best
+// match in the compressed image. This is not a symmetrical measure.
+
+// search radius. Shouldn't be too large.
+#define RADIUS 2
+
+static float AccumulateLSIM(const uint8_t* src, int src_stride,
+ const uint8_t* ref, int ref_stride,
+ int w, int h) {
+ int x, y;
+ double total_sse = 0.;
+ for (y = 0; y < h; ++y) {
+ const int y_0 = (y - RADIUS < 0) ? 0 : y - RADIUS;
+ const int y_1 = (y + RADIUS + 1 >= h) ? h : y + RADIUS + 1;
+ for (x = 0; x < w; ++x) {
+ const int x_0 = (x - RADIUS < 0) ? 0 : x - RADIUS;
+ const int x_1 = (x + RADIUS + 1 >= w) ? w : x + RADIUS + 1;
+ double best_sse = 255. * 255.;
+ const double value = (double)ref[y * ref_stride + x];
+ int i, j;
+ for (j = y_0; j < y_1; ++j) {
+ const uint8_t* s = src + j * src_stride;
+ for (i = x_0; i < x_1; ++i) {
+ const double sse = (double)(s[i] - value) * (s[i] - value);
+ if (sse < best_sse) best_sse = sse;
+ }
+ }
+ total_sse += best_sse;
+ }
+ }
+ return (float)total_sse;
+}
+#undef RADIUS
+
+//------------------------------------------------------------------------------
+// Distortion
+
+// Max value returned in case of exact similarity.
+static const double kMinDistortion_dB = 99.;
+static float GetPSNR(const double v) {
+ return (float)((v > 0.) ? -4.3429448 * log(v / (255 * 255.))
+ : kMinDistortion_dB);
+}
+
+int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,
+ int type, float result[5]) {
+ DistoStats stats[5];
+ int has_alpha;
+ int uv_w, uv_h;
+
+ if (src == NULL || ref == NULL ||
+ src->width != ref->width || src->height != ref->height ||
+ src->y == NULL || ref->y == NULL ||
+ src->u == NULL || ref->u == NULL ||
+ src->v == NULL || ref->v == NULL ||
+ result == NULL) {
+ return 0;
+ }
+ // TODO(skal): provide distortion for ARGB too.
+ if (src->use_argb == 1 || src->use_argb != ref->use_argb) {
+ return 0;
+ }
+
+ has_alpha = !!(src->colorspace & WEBP_CSP_ALPHA_BIT);
+ if (has_alpha != !!(ref->colorspace & WEBP_CSP_ALPHA_BIT) ||
+ (has_alpha && (src->a == NULL || ref->a == NULL))) {
+ return 0;
+ }
+
+ memset(stats, 0, sizeof(stats));
+
+ uv_w = HALVE(src->width);
+ uv_h = HALVE(src->height);
+ if (type >= 2) {
+ float sse[4];
+ sse[0] = AccumulateLSIM(src->y, src->y_stride,
+ ref->y, ref->y_stride, src->width, src->height);
+ sse[1] = AccumulateLSIM(src->u, src->uv_stride,
+ ref->u, ref->uv_stride, uv_w, uv_h);
+ sse[2] = AccumulateLSIM(src->v, src->uv_stride,
+ ref->v, ref->uv_stride, uv_w, uv_h);
+ sse[3] = has_alpha ? AccumulateLSIM(src->a, src->a_stride,
+ ref->a, ref->a_stride,
+ src->width, src->height)
+ : 0.f;
+ result[0] = GetPSNR(sse[0] / (src->width * src->height));
+ result[1] = GetPSNR(sse[1] / (uv_w * uv_h));
+ result[2] = GetPSNR(sse[2] / (uv_w * uv_h));
+ result[3] = GetPSNR(sse[3] / (src->width * src->height));
+ {
+ double total_sse = sse[0] + sse[1] + sse[2];
+ int total_pixels = src->width * src->height + 2 * uv_w * uv_h;
+ if (has_alpha) {
+ total_pixels += src->width * src->height;
+ total_sse += sse[3];
+ }
+ result[4] = GetPSNR(total_sse / total_pixels);
+ }
+ } else {
+ int c;
+ VP8SSIMAccumulatePlane(src->y, src->y_stride,
+ ref->y, ref->y_stride,
+ src->width, src->height, &stats[0]);
+ VP8SSIMAccumulatePlane(src->u, src->uv_stride,
+ ref->u, ref->uv_stride,
+ uv_w, uv_h, &stats[1]);
+ VP8SSIMAccumulatePlane(src->v, src->uv_stride,
+ ref->v, ref->uv_stride,
+ uv_w, uv_h, &stats[2]);
+ if (has_alpha) {
+ VP8SSIMAccumulatePlane(src->a, src->a_stride,
+ ref->a, ref->a_stride,
+ src->width, src->height, &stats[3]);
+ }
+ for (c = 0; c <= 4; ++c) {
+ if (type == 1) {
+ const double v = VP8SSIMGet(&stats[c]);
+ result[c] = (float)((v < 1.) ? -10.0 * log10(1. - v)
+ : kMinDistortion_dB);
+ } else {
+ const double v = VP8SSIMGetSquaredError(&stats[c]);
+ result[c] = GetPSNR(v);
+ }
+ // Accumulate forward
+ if (c < 4) VP8SSIMAddStats(&stats[c], &stats[4]);
+ }
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// Simplest high-level calls:
+
+typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int);
+
+static size_t Encode(const uint8_t* rgba, int width, int height, int stride,
+ Importer import, float quality_factor, int lossless,
+ uint8_t** output) {
+ WebPPicture pic;
+ WebPConfig config;
+ WebPMemoryWriter wrt;
+ int ok;
+
+ if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality_factor) ||
+ !WebPPictureInit(&pic)) {
+ return 0; // shouldn't happen, except if system installation is broken
+ }
+
+ config.lossless = !!lossless;
+ pic.use_argb = !!lossless;
+ pic.width = width;
+ pic.height = height;
+ pic.writer = WebPMemoryWrite;
+ pic.custom_ptr = &wrt;
+ WebPMemoryWriterInit(&wrt);
+
+ ok = import(&pic, rgba, stride) && WebPEncode(&config, &pic);
+ WebPPictureFree(&pic);
+ if (!ok) {
+ free(wrt.mem);
+ *output = NULL;
+ return 0;
+ }
+ *output = wrt.mem;
+ return wrt.size;
+}
+
+#define ENCODE_FUNC(NAME, IMPORTER) \
+size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \
+ uint8_t** out) { \
+ return Encode(in, w, h, bps, IMPORTER, q, 0, out); \
+}
+
+ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB)
+ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR)
+ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA)
+ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA)
+
+#undef ENCODE_FUNC
+
+#define LOSSLESS_DEFAULT_QUALITY 70.
+#define LOSSLESS_ENCODE_FUNC(NAME, IMPORTER) \
+size_t NAME(const uint8_t* in, int w, int h, int bps, uint8_t** out) { \
+ return Encode(in, w, h, bps, IMPORTER, LOSSLESS_DEFAULT_QUALITY, 1, out); \
+}
+
+LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGB, WebPPictureImportRGB)
+LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR, WebPPictureImportBGR)
+LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA, WebPPictureImportRGBA)
+LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA)
+
+#undef LOSSLESS_ENCODE_FUNC
+
+//------------------------------------------------------------------------------
+
diff --git a/src/3rdparty/libwebp/src/enc/quant.c b/src/3rdparty/libwebp/src/enc/quant.c
new file mode 100644
index 0000000..e1d202b
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/quant.c
@@ -0,0 +1,1156 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Quantization
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h> // for abs()
+
+#include "./vp8enci.h"
+#include "./cost.h"
+
+#define DO_TRELLIS_I4 1
+#define DO_TRELLIS_I16 1 // not a huge gain, but ok at low bitrate.
+#define DO_TRELLIS_UV 0 // disable trellis for UV. Risky. Not worth.
+#define USE_TDISTO 1
+
+#define MID_ALPHA 64 // neutral value for susceptibility
+#define MIN_ALPHA 30 // lowest usable value for susceptibility
+#define MAX_ALPHA 100 // higher meaningful value for susceptibility
+
+#define SNS_TO_DQ 0.9 // Scaling constant between the sns value and the QP
+ // power-law modulation. Must be strictly less than 1.
+
+#define I4_PENALTY 4000 // Rate-penalty for quick i4/i16 decision
+
+// number of non-zero coeffs below which we consider the block very flat
+// (and apply a penalty to complex predictions)
+#define FLATNESS_LIMIT_I16 10 // I16 mode
+#define FLATNESS_LIMIT_I4 3 // I4 mode
+#define FLATNESS_LIMIT_UV 2 // UV mode
+#define FLATNESS_PENALTY 140 // roughly ~1bit per block
+
+#define MULT_8B(a, b) (((a) * (b) + 128) >> 8)
+
+// #define DEBUG_BLOCK
+
+//------------------------------------------------------------------------------
+
+#if defined(DEBUG_BLOCK)
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void PrintBlockInfo(const VP8EncIterator* const it,
+ const VP8ModeScore* const rd) {
+ int i, j;
+ const int is_i16 = (it->mb_->type_ == 1);
+ printf("SOURCE / OUTPUT / ABS DELTA\n");
+ for (j = 0; j < 24; ++j) {
+ if (j == 16) printf("\n"); // newline before the U/V block
+ for (i = 0; i < 16; ++i) printf("%3d ", it->yuv_in_[i + j * BPS]);
+ printf(" ");
+ for (i = 0; i < 16; ++i) printf("%3d ", it->yuv_out_[i + j * BPS]);
+ printf(" ");
+ for (i = 0; i < 16; ++i) {
+ printf("%1d ", abs(it->yuv_out_[i + j * BPS] - it->yuv_in_[i + j * BPS]));
+ }
+ printf("\n");
+ }
+ printf("\nD:%d SD:%d R:%d H:%d nz:0x%x score:%d\n",
+ (int)rd->D, (int)rd->SD, (int)rd->R, (int)rd->H, (int)rd->nz,
+ (int)rd->score);
+ if (is_i16) {
+ printf("Mode: %d\n", rd->mode_i16);
+ printf("y_dc_levels:");
+ for (i = 0; i < 16; ++i) printf("%3d ", rd->y_dc_levels[i]);
+ printf("\n");
+ } else {
+ printf("Modes[16]: ");
+ for (i = 0; i < 16; ++i) printf("%d ", rd->modes_i4[i]);
+ printf("\n");
+ }
+ printf("y_ac_levels:\n");
+ for (j = 0; j < 16; ++j) {
+ for (i = is_i16 ? 1 : 0; i < 16; ++i) {
+ printf("%4d ", rd->y_ac_levels[j][i]);
+ }
+ printf("\n");
+ }
+ printf("\n");
+ printf("uv_levels (mode=%d):\n", rd->mode_uv);
+ for (j = 0; j < 8; ++j) {
+ for (i = 0; i < 16; ++i) {
+ printf("%4d ", rd->uv_levels[j][i]);
+ }
+ printf("\n");
+ }
+}
+
+#endif // DEBUG_BLOCK
+
+//------------------------------------------------------------------------------
+
+static WEBP_INLINE int clip(int v, int m, int M) {
+ return v < m ? m : v > M ? M : v;
+}
+
+static const uint8_t kZigzag[16] = {
+ 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
+};
+
+static const uint8_t kDcTable[128] = {
+ 4, 5, 6, 7, 8, 9, 10, 10,
+ 11, 12, 13, 14, 15, 16, 17, 17,
+ 18, 19, 20, 20, 21, 21, 22, 22,
+ 23, 23, 24, 25, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 76, 77, 78, 79, 80, 81,
+ 82, 83, 84, 85, 86, 87, 88, 89,
+ 91, 93, 95, 96, 98, 100, 101, 102,
+ 104, 106, 108, 110, 112, 114, 116, 118,
+ 122, 124, 126, 128, 130, 132, 134, 136,
+ 138, 140, 143, 145, 148, 151, 154, 157
+};
+
+static const uint16_t kAcTable[128] = {
+ 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 60,
+ 62, 64, 66, 68, 70, 72, 74, 76,
+ 78, 80, 82, 84, 86, 88, 90, 92,
+ 94, 96, 98, 100, 102, 104, 106, 108,
+ 110, 112, 114, 116, 119, 122, 125, 128,
+ 131, 134, 137, 140, 143, 146, 149, 152,
+ 155, 158, 161, 164, 167, 170, 173, 177,
+ 181, 185, 189, 193, 197, 201, 205, 209,
+ 213, 217, 221, 225, 229, 234, 239, 245,
+ 249, 254, 259, 264, 269, 274, 279, 284
+};
+
+static const uint16_t kAcTable2[128] = {
+ 8, 8, 9, 10, 12, 13, 15, 17,
+ 18, 20, 21, 23, 24, 26, 27, 29,
+ 31, 32, 34, 35, 37, 38, 40, 41,
+ 43, 44, 46, 48, 49, 51, 52, 54,
+ 55, 57, 58, 60, 62, 63, 65, 66,
+ 68, 69, 71, 72, 74, 75, 77, 79,
+ 80, 82, 83, 85, 86, 88, 89, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 124, 127, 130, 133, 136, 139, 142,
+ 145, 148, 151, 155, 158, 161, 164, 167,
+ 170, 173, 176, 179, 184, 189, 193, 198,
+ 203, 207, 212, 217, 221, 226, 230, 235,
+ 240, 244, 249, 254, 258, 263, 268, 274,
+ 280, 286, 292, 299, 305, 311, 317, 323,
+ 330, 336, 342, 348, 354, 362, 370, 379,
+ 385, 393, 401, 409, 416, 424, 432, 440
+};
+
+static const uint8_t kBiasMatrices[3][2] = { // [luma-ac,luma-dc,chroma][dc,ac]
+ { 96, 110 }, { 96, 108 }, { 110, 115 }
+};
+
+// Sharpening by (slightly) raising the hi-frequency coeffs.
+// Hack-ish but helpful for mid-bitrate range. Use with care.
+#define SHARPEN_BITS 11 // number of descaling bits for sharpening bias
+static const uint8_t kFreqSharpening[16] = {
+ 0, 30, 60, 90,
+ 30, 60, 90, 90,
+ 60, 90, 90, 90,
+ 90, 90, 90, 90
+};
+
+//------------------------------------------------------------------------------
+// Initialize quantization parameters in VP8Matrix
+
+// Returns the average quantizer
+static int ExpandMatrix(VP8Matrix* const m, int type) {
+ int i, sum;
+ for (i = 0; i < 2; ++i) {
+ const int is_ac_coeff = (i > 0);
+ const int bias = kBiasMatrices[type][is_ac_coeff];
+ m->iq_[i] = (1 << QFIX) / m->q_[i];
+ m->bias_[i] = BIAS(bias);
+ // zthresh_ is the exact value such that QUANTDIV(coeff, iQ, B) is:
+ // * zero if coeff <= zthresh
+ // * non-zero if coeff > zthresh
+ m->zthresh_[i] = ((1 << QFIX) - 1 - m->bias_[i]) / m->iq_[i];
+ }
+ for (i = 2; i < 16; ++i) {
+ m->q_[i] = m->q_[1];
+ m->iq_[i] = m->iq_[1];
+ m->bias_[i] = m->bias_[1];
+ m->zthresh_[i] = m->zthresh_[1];
+ }
+ for (sum = 0, i = 0; i < 16; ++i) {
+ if (type == 0) { // we only use sharpening for AC luma coeffs
+ m->sharpen_[i] = (kFreqSharpening[i] * m->q_[i]) >> SHARPEN_BITS;
+ } else {
+ m->sharpen_[i] = 0;
+ }
+ sum += m->q_[i];
+ }
+ return (sum + 8) >> 4;
+}
+
+static void SetupMatrices(VP8Encoder* enc) {
+ int i;
+ const int tlambda_scale =
+ (enc->method_ >= 4) ? enc->config_->sns_strength
+ : 0;
+ const int num_segments = enc->segment_hdr_.num_segments_;
+ for (i = 0; i < num_segments; ++i) {
+ VP8SegmentInfo* const m = &enc->dqm_[i];
+ const int q = m->quant_;
+ int q4, q16, quv;
+ m->y1_.q_[0] = kDcTable[clip(q + enc->dq_y1_dc_, 0, 127)];
+ m->y1_.q_[1] = kAcTable[clip(q, 0, 127)];
+
+ m->y2_.q_[0] = kDcTable[ clip(q + enc->dq_y2_dc_, 0, 127)] * 2;
+ m->y2_.q_[1] = kAcTable2[clip(q + enc->dq_y2_ac_, 0, 127)];
+
+ m->uv_.q_[0] = kDcTable[clip(q + enc->dq_uv_dc_, 0, 117)];
+ m->uv_.q_[1] = kAcTable[clip(q + enc->dq_uv_ac_, 0, 127)];
+
+ q4 = ExpandMatrix(&m->y1_, 0);
+ q16 = ExpandMatrix(&m->y2_, 1);
+ quv = ExpandMatrix(&m->uv_, 2);
+
+ m->lambda_i4_ = (3 * q4 * q4) >> 7;
+ m->lambda_i16_ = (3 * q16 * q16);
+ m->lambda_uv_ = (3 * quv * quv) >> 6;
+ m->lambda_mode_ = (1 * q4 * q4) >> 7;
+ m->lambda_trellis_i4_ = (7 * q4 * q4) >> 3;
+ m->lambda_trellis_i16_ = (q16 * q16) >> 2;
+ m->lambda_trellis_uv_ = (quv *quv) << 1;
+ m->tlambda_ = (tlambda_scale * q4) >> 5;
+
+ m->min_disto_ = 10 * m->y1_.q_[0]; // quantization-aware min disto
+ m->max_edge_ = 0;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Initialize filtering parameters
+
+// Very small filter-strength values have close to no visual effect. So we can
+// save a little decoding-CPU by turning filtering off for these.
+#define FSTRENGTH_CUTOFF 2
+
+static void SetupFilterStrength(VP8Encoder* const enc) {
+ int i;
+ // level0 is in [0..500]. Using '-f 50' as filter_strength is mid-filtering.
+ const int level0 = 5 * enc->config_->filter_strength;
+ for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
+ VP8SegmentInfo* const m = &enc->dqm_[i];
+ // We focus on the quantization of AC coeffs.
+ const int qstep = kAcTable[clip(m->quant_, 0, 127)] >> 2;
+ const int base_strength =
+ VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, qstep);
+ // Segments with lower complexity ('beta') will be less filtered.
+ const int f = base_strength * level0 / (256 + m->beta_);
+ m->fstrength_ = (f < FSTRENGTH_CUTOFF) ? 0 : (f > 63) ? 63 : f;
+ }
+ // We record the initial strength (mainly for the case of 1-segment only).
+ enc->filter_hdr_.level_ = enc->dqm_[0].fstrength_;
+ enc->filter_hdr_.simple_ = (enc->config_->filter_type == 0);
+ enc->filter_hdr_.sharpness_ = enc->config_->filter_sharpness;
+}
+
+//------------------------------------------------------------------------------
+
+// Note: if you change the values below, remember that the max range
+// allowed by the syntax for DQ_UV is [-16,16].
+#define MAX_DQ_UV (6)
+#define MIN_DQ_UV (-4)
+
+// We want to emulate jpeg-like behaviour where the expected "good" quality
+// is around q=75. Internally, our "good" middle is around c=50. So we
+// map accordingly using linear piece-wise function
+static double QualityToCompression(double c) {
+ const double linear_c = (c < 0.75) ? c * (2. / 3.) : 2. * c - 1.;
+ // The file size roughly scales as pow(quantizer, 3.). Actually, the
+ // exponent is somewhere between 2.8 and 3.2, but we're mostly interested
+ // in the mid-quant range. So we scale the compressibility inversely to
+ // this power-law: quant ~= compression ^ 1/3. This law holds well for
+ // low quant. Finer modeling for high-quant would make use of kAcTable[]
+ // more explicitly.
+ const double v = pow(linear_c, 1 / 3.);
+ return v;
+}
+
+static double QualityToJPEGCompression(double c, double alpha) {
+ // We map the complexity 'alpha' and quality setting 'c' to a compression
+ // exponent empirically matched to the compression curve of libjpeg6b.
+ // On average, the WebP output size will be roughly similar to that of a
+ // JPEG file compressed with same quality factor.
+ const double amin = 0.30;
+ const double amax = 0.85;
+ const double exp_min = 0.4;
+ const double exp_max = 0.9;
+ const double slope = (exp_min - exp_max) / (amax - amin);
+ // Linearly interpolate 'expn' from exp_min to exp_max
+ // in the [amin, amax] range.
+ const double expn = (alpha > amax) ? exp_min
+ : (alpha < amin) ? exp_max
+ : exp_max + slope * (alpha - amin);
+ const double v = pow(c, expn);
+ return v;
+}
+
+static int SegmentsAreEquivalent(const VP8SegmentInfo* const S1,
+ const VP8SegmentInfo* const S2) {
+ return (S1->quant_ == S2->quant_) && (S1->fstrength_ == S2->fstrength_);
+}
+
+static void SimplifySegments(VP8Encoder* const enc) {
+ int map[NUM_MB_SEGMENTS] = { 0, 1, 2, 3 };
+ const int num_segments = enc->segment_hdr_.num_segments_;
+ int num_final_segments = 1;
+ int s1, s2;
+ for (s1 = 1; s1 < num_segments; ++s1) { // find similar segments
+ const VP8SegmentInfo* const S1 = &enc->dqm_[s1];
+ int found = 0;
+ // check if we already have similar segment
+ for (s2 = 0; s2 < num_final_segments; ++s2) {
+ const VP8SegmentInfo* const S2 = &enc->dqm_[s2];
+ if (SegmentsAreEquivalent(S1, S2)) {
+ found = 1;
+ break;
+ }
+ }
+ map[s1] = s2;
+ if (!found) {
+ if (num_final_segments != s1) {
+ enc->dqm_[num_final_segments] = enc->dqm_[s1];
+ }
+ ++num_final_segments;
+ }
+ }
+ if (num_final_segments < num_segments) { // Remap
+ int i = enc->mb_w_ * enc->mb_h_;
+ while (i-- > 0) enc->mb_info_[i].segment_ = map[enc->mb_info_[i].segment_];
+ enc->segment_hdr_.num_segments_ = num_final_segments;
+ // Replicate the trailing segment infos (it's mostly cosmetics)
+ for (i = num_final_segments; i < num_segments; ++i) {
+ enc->dqm_[i] = enc->dqm_[num_final_segments - 1];
+ }
+ }
+}
+
+void VP8SetSegmentParams(VP8Encoder* const enc, float quality) {
+ int i;
+ int dq_uv_ac, dq_uv_dc;
+ const int num_segments = enc->segment_hdr_.num_segments_;
+ const double amp = SNS_TO_DQ * enc->config_->sns_strength / 100. / 128.;
+ const double Q = quality / 100.;
+ const double c_base = enc->config_->emulate_jpeg_size ?
+ QualityToJPEGCompression(Q, enc->alpha_ / 255.) :
+ QualityToCompression(Q);
+ for (i = 0; i < num_segments; ++i) {
+ // We modulate the base coefficient to accommodate for the quantization
+ // susceptibility and allow denser segments to be quantized more.
+ const double expn = 1. - amp * enc->dqm_[i].alpha_;
+ const double c = pow(c_base, expn);
+ const int q = (int)(127. * (1. - c));
+ assert(expn > 0.);
+ enc->dqm_[i].quant_ = clip(q, 0, 127);
+ }
+
+ // purely indicative in the bitstream (except for the 1-segment case)
+ enc->base_quant_ = enc->dqm_[0].quant_;
+
+ // fill-in values for the unused segments (required by the syntax)
+ for (i = num_segments; i < NUM_MB_SEGMENTS; ++i) {
+ enc->dqm_[i].quant_ = enc->base_quant_;
+ }
+
+ // uv_alpha_ is normally spread around ~60. The useful range is
+ // typically ~30 (quite bad) to ~100 (ok to decimate UV more).
+ // We map it to the safe maximal range of MAX/MIN_DQ_UV for dq_uv.
+ dq_uv_ac = (enc->uv_alpha_ - MID_ALPHA) * (MAX_DQ_UV - MIN_DQ_UV)
+ / (MAX_ALPHA - MIN_ALPHA);
+ // we rescale by the user-defined strength of adaptation
+ dq_uv_ac = dq_uv_ac * enc->config_->sns_strength / 100;
+ // and make it safe.
+ dq_uv_ac = clip(dq_uv_ac, MIN_DQ_UV, MAX_DQ_UV);
+ // We also boost the dc-uv-quant a little, based on sns-strength, since
+ // U/V channels are quite more reactive to high quants (flat DC-blocks
+ // tend to appear, and are displeasant).
+ dq_uv_dc = -4 * enc->config_->sns_strength / 100;
+ dq_uv_dc = clip(dq_uv_dc, -15, 15); // 4bit-signed max allowed
+
+ enc->dq_y1_dc_ = 0; // TODO(skal): dq-lum
+ enc->dq_y2_dc_ = 0;
+ enc->dq_y2_ac_ = 0;
+ enc->dq_uv_dc_ = dq_uv_dc;
+ enc->dq_uv_ac_ = dq_uv_ac;
+
+ SetupFilterStrength(enc); // initialize segments' filtering, eventually
+
+ if (num_segments > 1) SimplifySegments(enc);
+
+ SetupMatrices(enc); // finalize quantization matrices
+}
+
+//------------------------------------------------------------------------------
+// Form the predictions in cache
+
+// Must be ordered using {DC_PRED, TM_PRED, V_PRED, H_PRED} as index
+const int VP8I16ModeOffsets[4] = { I16DC16, I16TM16, I16VE16, I16HE16 };
+const int VP8UVModeOffsets[4] = { C8DC8, C8TM8, C8VE8, C8HE8 };
+
+// Must be indexed using {B_DC_PRED -> B_HU_PRED} as index
+const int VP8I4ModeOffsets[NUM_BMODES] = {
+ I4DC4, I4TM4, I4VE4, I4HE4, I4RD4, I4VR4, I4LD4, I4VL4, I4HD4, I4HU4
+};
+
+void VP8MakeLuma16Preds(const VP8EncIterator* const it) {
+ const uint8_t* const left = it->x_ ? it->y_left_ : NULL;
+ const uint8_t* const top = it->y_ ? it->y_top_ : NULL;
+ VP8EncPredLuma16(it->yuv_p_, left, top);
+}
+
+void VP8MakeChroma8Preds(const VP8EncIterator* const it) {
+ const uint8_t* const left = it->x_ ? it->u_left_ : NULL;
+ const uint8_t* const top = it->y_ ? it->uv_top_ : NULL;
+ VP8EncPredChroma8(it->yuv_p_, left, top);
+}
+
+void VP8MakeIntra4Preds(const VP8EncIterator* const it) {
+ VP8EncPredLuma4(it->yuv_p_, it->i4_top_);
+}
+
+//------------------------------------------------------------------------------
+// Quantize
+
+// Layout:
+// +----+
+// |YYYY| 0
+// |YYYY| 4
+// |YYYY| 8
+// |YYYY| 12
+// +----+
+// |UUVV| 16
+// |UUVV| 20
+// +----+
+
+const int VP8Scan[16 + 4 + 4] = {
+ // Luma
+ 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
+ 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
+ 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
+ 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS,
+
+ 0 + 0 * BPS, 4 + 0 * BPS, 0 + 4 * BPS, 4 + 4 * BPS, // U
+ 8 + 0 * BPS, 12 + 0 * BPS, 8 + 4 * BPS, 12 + 4 * BPS // V
+};
+
+//------------------------------------------------------------------------------
+// Distortion measurement
+
+static const uint16_t kWeightY[16] = {
+ 38, 32, 20, 9, 32, 28, 17, 7, 20, 17, 10, 4, 9, 7, 4, 2
+};
+
+static const uint16_t kWeightTrellis[16] = {
+#if USE_TDISTO == 0
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16
+#else
+ 30, 27, 19, 11,
+ 27, 24, 17, 10,
+ 19, 17, 12, 8,
+ 11, 10, 8, 6
+#endif
+};
+
+// Init/Copy the common fields in score.
+static void InitScore(VP8ModeScore* const rd) {
+ rd->D = 0;
+ rd->SD = 0;
+ rd->R = 0;
+ rd->H = 0;
+ rd->nz = 0;
+ rd->score = MAX_COST;
+}
+
+static void CopyScore(VP8ModeScore* const dst, const VP8ModeScore* const src) {
+ dst->D = src->D;
+ dst->SD = src->SD;
+ dst->R = src->R;
+ dst->H = src->H;
+ dst->nz = src->nz; // note that nz is not accumulated, but just copied.
+ dst->score = src->score;
+}
+
+static void AddScore(VP8ModeScore* const dst, const VP8ModeScore* const src) {
+ dst->D += src->D;
+ dst->SD += src->SD;
+ dst->R += src->R;
+ dst->H += src->H;
+ dst->nz |= src->nz; // here, new nz bits are accumulated.
+ dst->score += src->score;
+}
+
+//------------------------------------------------------------------------------
+// Performs trellis-optimized quantization.
+
+// Trellis
+
+typedef struct {
+ int prev; // best previous
+ int level; // level
+ int sign; // sign of coeff_i
+ score_t cost; // bit cost
+ score_t error; // distortion = sum of (|coeff_i| - level_i * Q_i)^2
+ int ctx; // context (only depends on 'level'. Could be spared.)
+} Node;
+
+// If a coefficient was quantized to a value Q (using a neutral bias),
+// we test all alternate possibilities between [Q-MIN_DELTA, Q+MAX_DELTA]
+// We don't test negative values though.
+#define MIN_DELTA 0 // how much lower level to try
+#define MAX_DELTA 1 // how much higher
+#define NUM_NODES (MIN_DELTA + 1 + MAX_DELTA)
+#define NODE(n, l) (nodes[(n) + 1][(l) + MIN_DELTA])
+
+static WEBP_INLINE void SetRDScore(int lambda, VP8ModeScore* const rd) {
+ // TODO: incorporate the "* 256" in the tables?
+ rd->score = (rd->R + rd->H) * lambda + 256 * (rd->D + rd->SD);
+}
+
+static WEBP_INLINE score_t RDScoreTrellis(int lambda, score_t rate,
+ score_t distortion) {
+ return rate * lambda + 256 * distortion;
+}
+
+static int TrellisQuantizeBlock(const VP8EncIterator* const it,
+ int16_t in[16], int16_t out[16],
+ int ctx0, int coeff_type,
+ const VP8Matrix* const mtx,
+ int lambda) {
+ ProbaArray* const last_costs = it->enc_->proba_.coeffs_[coeff_type];
+ CostArray* const costs = it->enc_->proba_.level_cost_[coeff_type];
+ const int first = (coeff_type == 0) ? 1 : 0;
+ Node nodes[17][NUM_NODES];
+ int best_path[3] = {-1, -1, -1}; // store best-last/best-level/best-previous
+ score_t best_score;
+ int best_node;
+ int last = first - 1;
+ int n, m, p, nz;
+
+ {
+ score_t cost;
+ score_t max_error;
+ const int thresh = mtx->q_[1] * mtx->q_[1] / 4;
+ const int last_proba = last_costs[VP8EncBands[first]][ctx0][0];
+
+ // compute maximal distortion.
+ max_error = 0;
+ for (n = first; n < 16; ++n) {
+ const int j = kZigzag[n];
+ const int err = in[j] * in[j];
+ max_error += kWeightTrellis[j] * err;
+ if (err > thresh) last = n;
+ }
+ // we don't need to go inspect up to n = 16 coeffs. We can just go up
+ // to last + 1 (inclusive) without losing much.
+ if (last < 15) ++last;
+
+ // compute 'skip' score. This is the max score one can do.
+ cost = VP8BitCost(0, last_proba);
+ best_score = RDScoreTrellis(lambda, cost, max_error);
+
+ // initialize source node.
+ n = first - 1;
+ for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) {
+ NODE(n, m).cost = 0;
+ NODE(n, m).error = max_error;
+ NODE(n, m).ctx = ctx0;
+ }
+ }
+
+ // traverse trellis.
+ for (n = first; n <= last; ++n) {
+ const int j = kZigzag[n];
+ const int Q = mtx->q_[j];
+ const int iQ = mtx->iq_[j];
+ const int B = BIAS(0x00); // neutral bias
+ // note: it's important to take sign of the _original_ coeff,
+ // so we don't have to consider level < 0 afterward.
+ const int sign = (in[j] < 0);
+ const int coeff0 = (sign ? -in[j] : in[j]) + mtx->sharpen_[j];
+ int level0 = QUANTDIV(coeff0, iQ, B);
+ if (level0 > MAX_LEVEL) level0 = MAX_LEVEL;
+
+ // test all alternate level values around level0.
+ for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) {
+ Node* const cur = &NODE(n, m);
+ int delta_error, new_error;
+ score_t cur_score = MAX_COST;
+ int level = level0 + m;
+ int last_proba;
+
+ cur->sign = sign;
+ cur->level = level;
+ cur->ctx = (level == 0) ? 0 : (level == 1) ? 1 : 2;
+ if (level > MAX_LEVEL || level < 0) { // node is dead?
+ cur->cost = MAX_COST;
+ continue;
+ }
+ last_proba = last_costs[VP8EncBands[n + 1]][cur->ctx][0];
+
+ // Compute delta_error = how much coding this level will
+ // subtract as distortion to max_error
+ new_error = coeff0 - level * Q;
+ delta_error =
+ kWeightTrellis[j] * (coeff0 * coeff0 - new_error * new_error);
+
+ // Inspect all possible non-dead predecessors. Retain only the best one.
+ for (p = -MIN_DELTA; p <= MAX_DELTA; ++p) {
+ const Node* const prev = &NODE(n - 1, p);
+ const int prev_ctx = prev->ctx;
+ const uint16_t* const tcost = costs[VP8EncBands[n]][prev_ctx];
+ const score_t total_error = prev->error - delta_error;
+ score_t cost, base_cost, score;
+
+ if (prev->cost >= MAX_COST) { // dead node?
+ continue;
+ }
+
+ // Base cost of both terminal/non-terminal
+ base_cost = prev->cost + VP8LevelCost(tcost, level);
+
+ // Examine node assuming it's a non-terminal one.
+ cost = base_cost;
+ if (level && n < 15) {
+ cost += VP8BitCost(1, last_proba);
+ }
+ score = RDScoreTrellis(lambda, cost, total_error);
+ if (score < cur_score) {
+ cur_score = score;
+ cur->cost = cost;
+ cur->error = total_error;
+ cur->prev = p;
+ }
+
+ // Now, record best terminal node (and thus best entry in the graph).
+ if (level) {
+ cost = base_cost;
+ if (n < 15) cost += VP8BitCost(0, last_proba);
+ score = RDScoreTrellis(lambda, cost, total_error);
+ if (score < best_score) {
+ best_score = score;
+ best_path[0] = n; // best eob position
+ best_path[1] = m; // best level
+ best_path[2] = p; // best predecessor
+ }
+ }
+ }
+ }
+ }
+
+ // Fresh start
+ memset(in + first, 0, (16 - first) * sizeof(*in));
+ memset(out + first, 0, (16 - first) * sizeof(*out));
+ if (best_path[0] == -1) {
+ return 0; // skip!
+ }
+
+ // Unwind the best path.
+ // Note: best-prev on terminal node is not necessarily equal to the
+ // best_prev for non-terminal. So we patch best_path[2] in.
+ n = best_path[0];
+ best_node = best_path[1];
+ NODE(n, best_node).prev = best_path[2]; // force best-prev for terminal
+ nz = 0;
+
+ for (; n >= first; --n) {
+ const Node* const node = &NODE(n, best_node);
+ const int j = kZigzag[n];
+ out[n] = node->sign ? -node->level : node->level;
+ nz |= (node->level != 0);
+ in[j] = out[n] * mtx->q_[j];
+ best_node = node->prev;
+ }
+ return nz;
+}
+
+#undef NODE
+
+//------------------------------------------------------------------------------
+// Performs: difference, transform, quantize, back-transform, add
+// all at once. Output is the reconstructed block in *yuv_out, and the
+// quantized levels in *levels.
+
+static int ReconstructIntra16(VP8EncIterator* const it,
+ VP8ModeScore* const rd,
+ uint8_t* const yuv_out,
+ int mode) {
+ VP8Encoder* const enc = it->enc_;
+ const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode];
+ const uint8_t* const src = it->yuv_in_ + Y_OFF;
+ VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
+ int nz = 0;
+ int n;
+ int16_t tmp[16][16], dc_tmp[16];
+
+ for (n = 0; n < 16; ++n) {
+ VP8FTransform(src + VP8Scan[n], ref + VP8Scan[n], tmp[n]);
+ }
+ VP8FTransformWHT(tmp[0], dc_tmp);
+ nz |= VP8EncQuantizeBlockWHT(dc_tmp, rd->y_dc_levels, &dqm->y2_) << 24;
+
+ if (DO_TRELLIS_I16 && it->do_trellis_) {
+ int x, y;
+ VP8IteratorNzToBytes(it);
+ for (y = 0, n = 0; y < 4; ++y) {
+ for (x = 0; x < 4; ++x, ++n) {
+ const int ctx = it->top_nz_[x] + it->left_nz_[y];
+ const int non_zero =
+ TrellisQuantizeBlock(it, tmp[n], rd->y_ac_levels[n], ctx, 0,
+ &dqm->y1_, dqm->lambda_trellis_i16_);
+ it->top_nz_[x] = it->left_nz_[y] = non_zero;
+ nz |= non_zero << n;
+ }
+ }
+ } else {
+ for (n = 0; n < 16; ++n) {
+ nz |= VP8EncQuantizeBlock(tmp[n], rd->y_ac_levels[n], 1, &dqm->y1_) << n;
+ }
+ }
+
+ // Transform back
+ VP8ITransformWHT(dc_tmp, tmp[0]);
+ for (n = 0; n < 16; n += 2) {
+ VP8ITransform(ref + VP8Scan[n], tmp[n], yuv_out + VP8Scan[n], 1);
+ }
+
+ return nz;
+}
+
+static int ReconstructIntra4(VP8EncIterator* const it,
+ int16_t levels[16],
+ const uint8_t* const src,
+ uint8_t* const yuv_out,
+ int mode) {
+ const VP8Encoder* const enc = it->enc_;
+ const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[mode];
+ const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
+ int nz = 0;
+ int16_t tmp[16];
+
+ VP8FTransform(src, ref, tmp);
+ if (DO_TRELLIS_I4 && it->do_trellis_) {
+ const int x = it->i4_ & 3, y = it->i4_ >> 2;
+ const int ctx = it->top_nz_[x] + it->left_nz_[y];
+ nz = TrellisQuantizeBlock(it, tmp, levels, ctx, 3, &dqm->y1_,
+ dqm->lambda_trellis_i4_);
+ } else {
+ nz = VP8EncQuantizeBlock(tmp, levels, 0, &dqm->y1_);
+ }
+ VP8ITransform(ref, tmp, yuv_out, 0);
+ return nz;
+}
+
+static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd,
+ uint8_t* const yuv_out, int mode) {
+ const VP8Encoder* const enc = it->enc_;
+ const uint8_t* const ref = it->yuv_p_ + VP8UVModeOffsets[mode];
+ const uint8_t* const src = it->yuv_in_ + U_OFF;
+ const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
+ int nz = 0;
+ int n;
+ int16_t tmp[8][16];
+
+ for (n = 0; n < 8; ++n) {
+ VP8FTransform(src + VP8Scan[16 + n], ref + VP8Scan[16 + n], tmp[n]);
+ }
+ if (DO_TRELLIS_UV && it->do_trellis_) {
+ int ch, x, y;
+ for (ch = 0, n = 0; ch <= 2; ch += 2) {
+ for (y = 0; y < 2; ++y) {
+ for (x = 0; x < 2; ++x, ++n) {
+ const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
+ const int non_zero =
+ TrellisQuantizeBlock(it, tmp[n], rd->uv_levels[n], ctx, 2,
+ &dqm->uv_, dqm->lambda_trellis_uv_);
+ it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = non_zero;
+ nz |= non_zero << n;
+ }
+ }
+ }
+ } else {
+ for (n = 0; n < 8; ++n) {
+ nz |= VP8EncQuantizeBlock(tmp[n], rd->uv_levels[n], 0, &dqm->uv_) << n;
+ }
+ }
+
+ for (n = 0; n < 8; n += 2) {
+ VP8ITransform(ref + VP8Scan[16 + n], tmp[n], yuv_out + VP8Scan[16 + n], 1);
+ }
+ return (nz << 16);
+}
+
+//------------------------------------------------------------------------------
+// RD-opt decision. Reconstruct each modes, evalue distortion and bit-cost.
+// Pick the mode is lower RD-cost = Rate + lambda * Distortion.
+
+static void StoreMaxDelta(VP8SegmentInfo* const dqm, const int16_t DCs[16]) {
+ // We look at the first three AC coefficients to determine what is the average
+ // delta between each sub-4x4 block.
+ const int v0 = abs(DCs[1]);
+ const int v1 = abs(DCs[4]);
+ const int v2 = abs(DCs[5]);
+ int max_v = (v0 > v1) ? v1 : v0;
+ max_v = (v2 > max_v) ? v2 : max_v;
+ if (max_v > dqm->max_edge_) dqm->max_edge_ = max_v;
+}
+
+static void SwapPtr(uint8_t** a, uint8_t** b) {
+ uint8_t* const tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+static void SwapOut(VP8EncIterator* const it) {
+ SwapPtr(&it->yuv_out_, &it->yuv_out2_);
+}
+
+static score_t IsFlat(const int16_t* levels, int num_blocks, score_t thresh) {
+ score_t score = 0;
+ while (num_blocks-- > 0) { // TODO(skal): refine positional scoring?
+ int i;
+ for (i = 1; i < 16; ++i) { // omit DC, we're only interested in AC
+ score += (levels[i] != 0);
+ if (score > thresh) return 0;
+ }
+ levels += 16;
+ }
+ return 1;
+}
+
+static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* const rd) {
+ const int kNumBlocks = 16;
+ VP8Encoder* const enc = it->enc_;
+ VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
+ const int lambda = dqm->lambda_i16_;
+ const int tlambda = dqm->tlambda_;
+ const uint8_t* const src = it->yuv_in_ + Y_OFF;
+ VP8ModeScore rd16;
+ int mode;
+
+ rd->mode_i16 = -1;
+ for (mode = 0; mode < NUM_PRED_MODES; ++mode) {
+ uint8_t* const tmp_dst = it->yuv_out2_ + Y_OFF; // scratch buffer
+ int nz;
+
+ // Reconstruct
+ nz = ReconstructIntra16(it, &rd16, tmp_dst, mode);
+
+ // Measure RD-score
+ rd16.D = VP8SSE16x16(src, tmp_dst);
+ rd16.SD = tlambda ? MULT_8B(tlambda, VP8TDisto16x16(src, tmp_dst, kWeightY))
+ : 0;
+ rd16.H = VP8FixedCostsI16[mode];
+ rd16.R = VP8GetCostLuma16(it, &rd16);
+ if (mode > 0 &&
+ IsFlat(rd16.y_ac_levels[0], kNumBlocks, FLATNESS_LIMIT_I16)) {
+ // penalty to avoid flat area to be mispredicted by complex mode
+ rd16.R += FLATNESS_PENALTY * kNumBlocks;
+ }
+
+ // Since we always examine Intra16 first, we can overwrite *rd directly.
+ SetRDScore(lambda, &rd16);
+ if (mode == 0 || rd16.score < rd->score) {
+ CopyScore(rd, &rd16);
+ rd->mode_i16 = mode;
+ rd->nz = nz;
+ memcpy(rd->y_ac_levels, rd16.y_ac_levels, sizeof(rd16.y_ac_levels));
+ memcpy(rd->y_dc_levels, rd16.y_dc_levels, sizeof(rd16.y_dc_levels));
+ SwapOut(it);
+ }
+ }
+ SetRDScore(dqm->lambda_mode_, rd); // finalize score for mode decision.
+ VP8SetIntra16Mode(it, rd->mode_i16);
+
+ // we have a blocky macroblock (only DCs are non-zero) with fairly high
+ // distortion, record max delta so we can later adjust the minimal filtering
+ // strength needed to smooth these blocks out.
+ if ((rd->nz & 0xffff) == 0 && rd->D > dqm->min_disto_) {
+ StoreMaxDelta(dqm, rd->y_dc_levels);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+// return the cost array corresponding to the surrounding prediction modes.
+static const uint16_t* GetCostModeI4(VP8EncIterator* const it,
+ const uint8_t modes[16]) {
+ const int preds_w = it->enc_->preds_w_;
+ const int x = (it->i4_ & 3), y = it->i4_ >> 2;
+ const int left = (x == 0) ? it->preds_[y * preds_w - 1] : modes[it->i4_ - 1];
+ const int top = (y == 0) ? it->preds_[-preds_w + x] : modes[it->i4_ - 4];
+ return VP8FixedCostsI4[top][left];
+}
+
+static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) {
+ const VP8Encoder* const enc = it->enc_;
+ const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
+ const int lambda = dqm->lambda_i4_;
+ const int tlambda = dqm->tlambda_;
+ const uint8_t* const src0 = it->yuv_in_ + Y_OFF;
+ uint8_t* const best_blocks = it->yuv_out2_ + Y_OFF;
+ int total_header_bits = 0;
+ VP8ModeScore rd_best;
+
+ if (enc->max_i4_header_bits_ == 0) {
+ return 0;
+ }
+
+ InitScore(&rd_best);
+ rd_best.H = 211; // '211' is the value of VP8BitCost(0, 145)
+ SetRDScore(dqm->lambda_mode_, &rd_best);
+ VP8IteratorStartI4(it);
+ do {
+ const int kNumBlocks = 1;
+ VP8ModeScore rd_i4;
+ int mode;
+ int best_mode = -1;
+ const uint8_t* const src = src0 + VP8Scan[it->i4_];
+ const uint16_t* const mode_costs = GetCostModeI4(it, rd->modes_i4);
+ uint8_t* best_block = best_blocks + VP8Scan[it->i4_];
+ uint8_t* tmp_dst = it->yuv_p_ + I4TMP; // scratch buffer.
+
+ InitScore(&rd_i4);
+ VP8MakeIntra4Preds(it);
+ for (mode = 0; mode < NUM_BMODES; ++mode) {
+ VP8ModeScore rd_tmp;
+ int16_t tmp_levels[16];
+
+ // Reconstruct
+ rd_tmp.nz =
+ ReconstructIntra4(it, tmp_levels, src, tmp_dst, mode) << it->i4_;
+
+ // Compute RD-score
+ rd_tmp.D = VP8SSE4x4(src, tmp_dst);
+ rd_tmp.SD =
+ tlambda ? MULT_8B(tlambda, VP8TDisto4x4(src, tmp_dst, kWeightY))
+ : 0;
+ rd_tmp.H = mode_costs[mode];
+ rd_tmp.R = VP8GetCostLuma4(it, tmp_levels);
+ if (mode > 0 && IsFlat(tmp_levels, kNumBlocks, FLATNESS_LIMIT_I4)) {
+ rd_tmp.R += FLATNESS_PENALTY * kNumBlocks;
+ }
+
+ SetRDScore(lambda, &rd_tmp);
+ if (best_mode < 0 || rd_tmp.score < rd_i4.score) {
+ CopyScore(&rd_i4, &rd_tmp);
+ best_mode = mode;
+ SwapPtr(&tmp_dst, &best_block);
+ memcpy(rd_best.y_ac_levels[it->i4_], tmp_levels, sizeof(tmp_levels));
+ }
+ }
+ SetRDScore(dqm->lambda_mode_, &rd_i4);
+ AddScore(&rd_best, &rd_i4);
+ if (rd_best.score >= rd->score) {
+ return 0;
+ }
+ total_header_bits += (int)rd_i4.H; // <- equal to mode_costs[best_mode];
+ if (total_header_bits > enc->max_i4_header_bits_) {
+ return 0;
+ }
+ // Copy selected samples if not in the right place already.
+ if (best_block != best_blocks + VP8Scan[it->i4_]) {
+ VP8Copy4x4(best_block, best_blocks + VP8Scan[it->i4_]);
+ }
+ rd->modes_i4[it->i4_] = best_mode;
+ it->top_nz_[it->i4_ & 3] = it->left_nz_[it->i4_ >> 2] = (rd_i4.nz ? 1 : 0);
+ } while (VP8IteratorRotateI4(it, best_blocks));
+
+ // finalize state
+ CopyScore(rd, &rd_best);
+ VP8SetIntra4Mode(it, rd->modes_i4);
+ SwapOut(it);
+ memcpy(rd->y_ac_levels, rd_best.y_ac_levels, sizeof(rd->y_ac_levels));
+ return 1; // select intra4x4 over intra16x16
+}
+
+//------------------------------------------------------------------------------
+
+static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) {
+ const int kNumBlocks = 8;
+ const VP8Encoder* const enc = it->enc_;
+ const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
+ const int lambda = dqm->lambda_uv_;
+ const uint8_t* const src = it->yuv_in_ + U_OFF;
+ uint8_t* const tmp_dst = it->yuv_out2_ + U_OFF; // scratch buffer
+ uint8_t* const dst0 = it->yuv_out_ + U_OFF;
+ VP8ModeScore rd_best;
+ int mode;
+
+ rd->mode_uv = -1;
+ InitScore(&rd_best);
+ for (mode = 0; mode < NUM_PRED_MODES; ++mode) {
+ VP8ModeScore rd_uv;
+
+ // Reconstruct
+ rd_uv.nz = ReconstructUV(it, &rd_uv, tmp_dst, mode);
+
+ // Compute RD-score
+ rd_uv.D = VP8SSE16x8(src, tmp_dst);
+ rd_uv.SD = 0; // TODO: should we call TDisto? it tends to flatten areas.
+ rd_uv.H = VP8FixedCostsUV[mode];
+ rd_uv.R = VP8GetCostUV(it, &rd_uv);
+ if (mode > 0 && IsFlat(rd_uv.uv_levels[0], kNumBlocks, FLATNESS_LIMIT_UV)) {
+ rd_uv.R += FLATNESS_PENALTY * kNumBlocks;
+ }
+
+ SetRDScore(lambda, &rd_uv);
+ if (mode == 0 || rd_uv.score < rd_best.score) {
+ CopyScore(&rd_best, &rd_uv);
+ rd->mode_uv = mode;
+ memcpy(rd->uv_levels, rd_uv.uv_levels, sizeof(rd->uv_levels));
+ memcpy(dst0, tmp_dst, UV_SIZE); // TODO: SwapUVOut() ?
+ }
+ }
+ VP8SetIntraUVMode(it, rd->mode_uv);
+ AddScore(rd, &rd_best);
+}
+
+//------------------------------------------------------------------------------
+// Final reconstruction and quantization.
+
+static void SimpleQuantize(VP8EncIterator* const it, VP8ModeScore* const rd) {
+ const VP8Encoder* const enc = it->enc_;
+ const int is_i16 = (it->mb_->type_ == 1);
+ int nz = 0;
+
+ if (is_i16) {
+ nz = ReconstructIntra16(it, rd, it->yuv_out_ + Y_OFF, it->preds_[0]);
+ } else {
+ VP8IteratorStartI4(it);
+ do {
+ const int mode =
+ it->preds_[(it->i4_ & 3) + (it->i4_ >> 2) * enc->preds_w_];
+ const uint8_t* const src = it->yuv_in_ + Y_OFF + VP8Scan[it->i4_];
+ uint8_t* const dst = it->yuv_out_ + Y_OFF + VP8Scan[it->i4_];
+ VP8MakeIntra4Preds(it);
+ nz |= ReconstructIntra4(it, rd->y_ac_levels[it->i4_],
+ src, dst, mode) << it->i4_;
+ } while (VP8IteratorRotateI4(it, it->yuv_out_ + Y_OFF));
+ }
+
+ nz |= ReconstructUV(it, rd, it->yuv_out_ + U_OFF, it->mb_->uv_mode_);
+ rd->nz = nz;
+}
+
+// Refine intra16/intra4 sub-modes based on distortion only (not rate).
+static void DistoRefine(VP8EncIterator* const it, int try_both_i4_i16) {
+ const int is_i16 = (it->mb_->type_ == 1);
+ score_t best_score = MAX_COST;
+
+ if (try_both_i4_i16 || is_i16) {
+ int mode;
+ int best_mode = -1;
+ for (mode = 0; mode < NUM_PRED_MODES; ++mode) {
+ const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode];
+ const uint8_t* const src = it->yuv_in_ + Y_OFF;
+ const score_t score = VP8SSE16x16(src, ref);
+ if (score < best_score) {
+ best_mode = mode;
+ best_score = score;
+ }
+ }
+ VP8SetIntra16Mode(it, best_mode);
+ }
+ if (try_both_i4_i16 || !is_i16) {
+ uint8_t modes_i4[16];
+ // We don't evaluate the rate here, but just account for it through a
+ // constant penalty (i4 mode usually needs more bits compared to i16).
+ score_t score_i4 = (score_t)I4_PENALTY;
+
+ VP8IteratorStartI4(it);
+ do {
+ int mode;
+ int best_sub_mode = -1;
+ score_t best_sub_score = MAX_COST;
+ const uint8_t* const src = it->yuv_in_ + Y_OFF + VP8Scan[it->i4_];
+
+ // TODO(skal): we don't really need the prediction pixels here,
+ // but just the distortion against 'src'.
+ VP8MakeIntra4Preds(it);
+ for (mode = 0; mode < NUM_BMODES; ++mode) {
+ const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[mode];
+ const score_t score = VP8SSE4x4(src, ref);
+ if (score < best_sub_score) {
+ best_sub_mode = mode;
+ best_sub_score = score;
+ }
+ }
+ modes_i4[it->i4_] = best_sub_mode;
+ score_i4 += best_sub_score;
+ if (score_i4 >= best_score) break;
+ } while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF));
+ if (score_i4 < best_score) {
+ VP8SetIntra4Mode(it, modes_i4);
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd,
+ VP8RDLevel rd_opt) {
+ int is_skipped;
+ const int method = it->enc_->method_;
+
+ InitScore(rd);
+
+ // We can perform predictions for Luma16x16 and Chroma8x8 already.
+ // Luma4x4 predictions needs to be done as-we-go.
+ VP8MakeLuma16Preds(it);
+ VP8MakeChroma8Preds(it);
+
+ if (rd_opt > RD_OPT_NONE) {
+ it->do_trellis_ = (rd_opt >= RD_OPT_TRELLIS_ALL);
+ PickBestIntra16(it, rd);
+ if (method >= 2) {
+ PickBestIntra4(it, rd);
+ }
+ PickBestUV(it, rd);
+ if (rd_opt == RD_OPT_TRELLIS) { // finish off with trellis-optim now
+ it->do_trellis_ = 1;
+ SimpleQuantize(it, rd);
+ }
+ } else {
+ // For method == 2, pick the best intra4/intra16 based on SSE (~tad slower).
+ // For method <= 1, we refine intra4 or intra16 (but don't re-examine mode).
+ DistoRefine(it, (method >= 2));
+ SimpleQuantize(it, rd);
+ }
+ is_skipped = (rd->nz == 0);
+ VP8SetSkip(it, is_skipped);
+ return is_skipped;
+}
+
diff --git a/src/3rdparty/libwebp/src/enc/syntax.c b/src/3rdparty/libwebp/src/enc/syntax.c
new file mode 100644
index 0000000..08cfe79
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/syntax.c
@@ -0,0 +1,423 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Header syntax writing
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+
+#include "../utils/utils.h"
+#include "../webp/format_constants.h" // RIFF constants
+#include "../webp/mux_types.h" // ALPHA_FLAG
+#include "./vp8enci.h"
+
+//------------------------------------------------------------------------------
+// Helper functions
+
+static int IsVP8XNeeded(const VP8Encoder* const enc) {
+ return !!enc->has_alpha_; // Currently the only case when VP8X is needed.
+ // This could change in the future.
+}
+
+static int PutPaddingByte(const WebPPicture* const pic) {
+ const uint8_t pad_byte[1] = { 0 };
+ return !!pic->writer(pad_byte, 1, pic);
+}
+
+//------------------------------------------------------------------------------
+// Writers for header's various pieces (in order of appearance)
+
+static WebPEncodingError PutRIFFHeader(const VP8Encoder* const enc,
+ size_t riff_size) {
+ const WebPPicture* const pic = enc->pic_;
+ uint8_t riff[RIFF_HEADER_SIZE] = {
+ 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P'
+ };
+ assert(riff_size == (uint32_t)riff_size);
+ PutLE32(riff + TAG_SIZE, (uint32_t)riff_size);
+ if (!pic->writer(riff, sizeof(riff), pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+ return VP8_ENC_OK;
+}
+
+static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) {
+ const WebPPicture* const pic = enc->pic_;
+ uint8_t vp8x[CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE] = {
+ 'V', 'P', '8', 'X'
+ };
+ uint32_t flags = 0;
+
+ assert(IsVP8XNeeded(enc));
+ assert(pic->width >= 1 && pic->height >= 1);
+ assert(pic->width <= MAX_CANVAS_SIZE && pic->height <= MAX_CANVAS_SIZE);
+
+ if (enc->has_alpha_) {
+ flags |= ALPHA_FLAG;
+ }
+
+ PutLE32(vp8x + TAG_SIZE, VP8X_CHUNK_SIZE);
+ PutLE32(vp8x + CHUNK_HEADER_SIZE, flags);
+ PutLE24(vp8x + CHUNK_HEADER_SIZE + 4, pic->width - 1);
+ PutLE24(vp8x + CHUNK_HEADER_SIZE + 7, pic->height - 1);
+ if (!pic->writer(vp8x, sizeof(vp8x), pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+ return VP8_ENC_OK;
+}
+
+static WebPEncodingError PutAlphaChunk(const VP8Encoder* const enc) {
+ const WebPPicture* const pic = enc->pic_;
+ uint8_t alpha_chunk_hdr[CHUNK_HEADER_SIZE] = {
+ 'A', 'L', 'P', 'H'
+ };
+
+ assert(enc->has_alpha_);
+
+ // Alpha chunk header.
+ PutLE32(alpha_chunk_hdr + TAG_SIZE, enc->alpha_data_size_);
+ if (!pic->writer(alpha_chunk_hdr, sizeof(alpha_chunk_hdr), pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+
+ // Alpha chunk data.
+ if (!pic->writer(enc->alpha_data_, enc->alpha_data_size_, pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+
+ // Padding.
+ if ((enc->alpha_data_size_ & 1) && !PutPaddingByte(pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+ return VP8_ENC_OK;
+}
+
+static WebPEncodingError PutVP8Header(const WebPPicture* const pic,
+ size_t vp8_size) {
+ uint8_t vp8_chunk_hdr[CHUNK_HEADER_SIZE] = {
+ 'V', 'P', '8', ' '
+ };
+ assert(vp8_size == (uint32_t)vp8_size);
+ PutLE32(vp8_chunk_hdr + TAG_SIZE, (uint32_t)vp8_size);
+ if (!pic->writer(vp8_chunk_hdr, sizeof(vp8_chunk_hdr), pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+ return VP8_ENC_OK;
+}
+
+static WebPEncodingError PutVP8FrameHeader(const WebPPicture* const pic,
+ int profile, size_t size0) {
+ uint8_t vp8_frm_hdr[VP8_FRAME_HEADER_SIZE];
+ uint32_t bits;
+
+ if (size0 >= VP8_MAX_PARTITION0_SIZE) { // partition #0 is too big to fit
+ return VP8_ENC_ERROR_PARTITION0_OVERFLOW;
+ }
+
+ // Paragraph 9.1.
+ bits = 0 // keyframe (1b)
+ | (profile << 1) // profile (3b)
+ | (1 << 4) // visible (1b)
+ | ((uint32_t)size0 << 5); // partition length (19b)
+ vp8_frm_hdr[0] = (bits >> 0) & 0xff;
+ vp8_frm_hdr[1] = (bits >> 8) & 0xff;
+ vp8_frm_hdr[2] = (bits >> 16) & 0xff;
+ // signature
+ vp8_frm_hdr[3] = (VP8_SIGNATURE >> 16) & 0xff;
+ vp8_frm_hdr[4] = (VP8_SIGNATURE >> 8) & 0xff;
+ vp8_frm_hdr[5] = (VP8_SIGNATURE >> 0) & 0xff;
+ // dimensions
+ vp8_frm_hdr[6] = pic->width & 0xff;
+ vp8_frm_hdr[7] = pic->width >> 8;
+ vp8_frm_hdr[8] = pic->height & 0xff;
+ vp8_frm_hdr[9] = pic->height >> 8;
+
+ if (!pic->writer(vp8_frm_hdr, sizeof(vp8_frm_hdr), pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+ return VP8_ENC_OK;
+}
+
+// WebP Headers.
+static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0,
+ size_t vp8_size, size_t riff_size) {
+ WebPPicture* const pic = enc->pic_;
+ WebPEncodingError err = VP8_ENC_OK;
+
+ // RIFF header.
+ err = PutRIFFHeader(enc, riff_size);
+ if (err != VP8_ENC_OK) goto Error;
+
+ // VP8X.
+ if (IsVP8XNeeded(enc)) {
+ err = PutVP8XHeader(enc);
+ if (err != VP8_ENC_OK) goto Error;
+ }
+
+ // Alpha.
+ if (enc->has_alpha_) {
+ err = PutAlphaChunk(enc);
+ if (err != VP8_ENC_OK) goto Error;
+ }
+
+ // VP8 header.
+ err = PutVP8Header(pic, vp8_size);
+ if (err != VP8_ENC_OK) goto Error;
+
+ // VP8 frame header.
+ err = PutVP8FrameHeader(pic, enc->profile_, size0);
+ if (err != VP8_ENC_OK) goto Error;
+
+ // All OK.
+ return 1;
+
+ // Error.
+ Error:
+ return WebPEncodingSetError(pic, err);
+}
+
+// Segmentation header
+static void PutSegmentHeader(VP8BitWriter* const bw,
+ const VP8Encoder* const enc) {
+ const VP8SegmentHeader* const hdr = &enc->segment_hdr_;
+ const VP8Proba* const proba = &enc->proba_;
+ if (VP8PutBitUniform(bw, (hdr->num_segments_ > 1))) {
+ // We always 'update' the quant and filter strength values
+ const int update_data = 1;
+ int s;
+ VP8PutBitUniform(bw, hdr->update_map_);
+ if (VP8PutBitUniform(bw, update_data)) {
+ // we always use absolute values, not relative ones
+ VP8PutBitUniform(bw, 1); // (segment_feature_mode = 1. Paragraph 9.3.)
+ for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
+ VP8PutSignedValue(bw, enc->dqm_[s].quant_, 7);
+ }
+ for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
+ VP8PutSignedValue(bw, enc->dqm_[s].fstrength_, 6);
+ }
+ }
+ if (hdr->update_map_) {
+ for (s = 0; s < 3; ++s) {
+ if (VP8PutBitUniform(bw, (proba->segments_[s] != 255u))) {
+ VP8PutValue(bw, proba->segments_[s], 8);
+ }
+ }
+ }
+ }
+}
+
+// Filtering parameters header
+static void PutFilterHeader(VP8BitWriter* const bw,
+ const VP8FilterHeader* const hdr) {
+ const int use_lf_delta = (hdr->i4x4_lf_delta_ != 0);
+ VP8PutBitUniform(bw, hdr->simple_);
+ VP8PutValue(bw, hdr->level_, 6);
+ VP8PutValue(bw, hdr->sharpness_, 3);
+ if (VP8PutBitUniform(bw, use_lf_delta)) {
+ // '0' is the default value for i4x4_lf_delta_ at frame #0.
+ const int need_update = (hdr->i4x4_lf_delta_ != 0);
+ if (VP8PutBitUniform(bw, need_update)) {
+ // we don't use ref_lf_delta => emit four 0 bits
+ VP8PutValue(bw, 0, 4);
+ // we use mode_lf_delta for i4x4
+ VP8PutSignedValue(bw, hdr->i4x4_lf_delta_, 6);
+ VP8PutValue(bw, 0, 3); // all others unused
+ }
+ }
+}
+
+// Nominal quantization parameters
+static void PutQuant(VP8BitWriter* const bw,
+ const VP8Encoder* const enc) {
+ VP8PutValue(bw, enc->base_quant_, 7);
+ VP8PutSignedValue(bw, enc->dq_y1_dc_, 4);
+ VP8PutSignedValue(bw, enc->dq_y2_dc_, 4);
+ VP8PutSignedValue(bw, enc->dq_y2_ac_, 4);
+ VP8PutSignedValue(bw, enc->dq_uv_dc_, 4);
+ VP8PutSignedValue(bw, enc->dq_uv_ac_, 4);
+}
+
+// Partition sizes
+static int EmitPartitionsSize(const VP8Encoder* const enc,
+ WebPPicture* const pic) {
+ uint8_t buf[3 * (MAX_NUM_PARTITIONS - 1)];
+ int p;
+ for (p = 0; p < enc->num_parts_ - 1; ++p) {
+ const size_t part_size = VP8BitWriterSize(enc->parts_ + p);
+ if (part_size >= VP8_MAX_PARTITION_SIZE) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION_OVERFLOW);
+ }
+ buf[3 * p + 0] = (part_size >> 0) & 0xff;
+ buf[3 * p + 1] = (part_size >> 8) & 0xff;
+ buf[3 * p + 2] = (part_size >> 16) & 0xff;
+ }
+ return p ? pic->writer(buf, 3 * p, pic) : 1;
+}
+
+//------------------------------------------------------------------------------
+
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+
+#define KTRAILER_SIZE 8
+
+static int WriteExtensions(VP8Encoder* const enc) {
+ uint8_t buffer[KTRAILER_SIZE];
+ VP8BitWriter* const bw = &enc->bw_;
+ WebPPicture* const pic = enc->pic_;
+
+ // Layer (bytes 0..3)
+ PutLE24(buffer + 0, enc->layer_data_size_);
+ buffer[3] = enc->pic_->colorspace & WEBP_CSP_UV_MASK;
+ if (enc->layer_data_size_ > 0) {
+ assert(enc->use_layer_);
+ // append layer data to last partition
+ if (!VP8BitWriterAppend(&enc->parts_[enc->num_parts_ - 1],
+ enc->layer_data_, enc->layer_data_size_)) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY);
+ }
+ }
+
+ buffer[KTRAILER_SIZE - 1] = 0x01; // marker
+ if (!VP8BitWriterAppend(bw, buffer, KTRAILER_SIZE)) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY);
+ }
+ return 1;
+}
+
+#endif /* WEBP_EXPERIMENTAL_FEATURES */
+
+//------------------------------------------------------------------------------
+
+static size_t GeneratePartition0(VP8Encoder* const enc) {
+ VP8BitWriter* const bw = &enc->bw_;
+ const int mb_size = enc->mb_w_ * enc->mb_h_;
+ uint64_t pos1, pos2, pos3;
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+ const int need_extensions = enc->use_layer_;
+#endif
+
+ pos1 = VP8BitWriterPos(bw);
+ VP8BitWriterInit(bw, mb_size * 7 / 8); // ~7 bits per macroblock
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+ VP8PutBitUniform(bw, need_extensions); // extensions
+#else
+ VP8PutBitUniform(bw, 0); // colorspace
+#endif
+ VP8PutBitUniform(bw, 0); // clamp type
+
+ PutSegmentHeader(bw, enc);
+ PutFilterHeader(bw, &enc->filter_hdr_);
+ VP8PutValue(bw, enc->num_parts_ == 8 ? 3 :
+ enc->num_parts_ == 4 ? 2 :
+ enc->num_parts_ == 2 ? 1 : 0, 2);
+ PutQuant(bw, enc);
+ VP8PutBitUniform(bw, 0); // no proba update
+ VP8WriteProbas(bw, &enc->proba_);
+ pos2 = VP8BitWriterPos(bw);
+ VP8CodeIntraModes(enc);
+ VP8BitWriterFinish(bw);
+
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+ if (need_extensions && !WriteExtensions(enc)) {
+ return 0;
+ }
+#endif
+
+ pos3 = VP8BitWriterPos(bw);
+
+ if (enc->pic_->stats) {
+ enc->pic_->stats->header_bytes[0] = (int)((pos2 - pos1 + 7) >> 3);
+ enc->pic_->stats->header_bytes[1] = (int)((pos3 - pos2 + 7) >> 3);
+ enc->pic_->stats->alpha_data_size = (int)enc->alpha_data_size_;
+ enc->pic_->stats->layer_data_size = (int)enc->layer_data_size_;
+ }
+ return !bw->error_;
+}
+
+void VP8EncFreeBitWriters(VP8Encoder* const enc) {
+ int p;
+ VP8BitWriterWipeOut(&enc->bw_);
+ for (p = 0; p < enc->num_parts_; ++p) {
+ VP8BitWriterWipeOut(enc->parts_ + p);
+ }
+}
+
+int VP8EncWrite(VP8Encoder* const enc) {
+ WebPPicture* const pic = enc->pic_;
+ VP8BitWriter* const bw = &enc->bw_;
+ const int task_percent = 19;
+ const int percent_per_part = task_percent / enc->num_parts_;
+ const int final_percent = enc->percent_ + task_percent;
+ int ok = 0;
+ size_t vp8_size, pad, riff_size;
+ int p;
+
+ // Partition #0 with header and partition sizes
+ ok = !!GeneratePartition0(enc);
+
+ // Compute VP8 size
+ vp8_size = VP8_FRAME_HEADER_SIZE +
+ VP8BitWriterSize(bw) +
+ 3 * (enc->num_parts_ - 1);
+ for (p = 0; p < enc->num_parts_; ++p) {
+ vp8_size += VP8BitWriterSize(enc->parts_ + p);
+ }
+ pad = vp8_size & 1;
+ vp8_size += pad;
+
+ // Compute RIFF size
+ // At the minimum it is: "WEBPVP8 nnnn" + VP8 data size.
+ riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8_size;
+ if (IsVP8XNeeded(enc)) { // Add size for: VP8X header + data.
+ riff_size += CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
+ }
+ if (enc->has_alpha_) { // Add size for: ALPH header + data.
+ const uint32_t padded_alpha_size = enc->alpha_data_size_ +
+ (enc->alpha_data_size_ & 1);
+ riff_size += CHUNK_HEADER_SIZE + padded_alpha_size;
+ }
+ // Sanity check.
+ if (riff_size > 0xfffffffeU) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG);
+ }
+
+ // Emit headers and partition #0
+ {
+ const uint8_t* const part0 = VP8BitWriterBuf(bw);
+ const size_t size0 = VP8BitWriterSize(bw);
+ ok = ok && PutWebPHeaders(enc, size0, vp8_size, riff_size)
+ && pic->writer(part0, size0, pic)
+ && EmitPartitionsSize(enc, pic);
+ VP8BitWriterWipeOut(bw); // will free the internal buffer.
+ }
+
+ // Token partitions
+ for (p = 0; p < enc->num_parts_; ++p) {
+ const uint8_t* const buf = VP8BitWriterBuf(enc->parts_ + p);
+ const size_t size = VP8BitWriterSize(enc->parts_ + p);
+ if (size)
+ ok = ok && pic->writer(buf, size, pic);
+ VP8BitWriterWipeOut(enc->parts_ + p); // will free the internal buffer.
+ ok = ok && WebPReportProgress(pic, enc->percent_ + percent_per_part,
+ &enc->percent_);
+ }
+
+ // Padding byte
+ if (ok && pad) {
+ ok = PutPaddingByte(pic);
+ }
+
+ enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size);
+ ok = ok && WebPReportProgress(pic, final_percent, &enc->percent_);
+ return ok;
+}
+
+//------------------------------------------------------------------------------
+
diff --git a/src/3rdparty/libwebp/src/enc/token.c b/src/3rdparty/libwebp/src/enc/token.c
new file mode 100644
index 0000000..e696642
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/token.c
@@ -0,0 +1,273 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Paginated token buffer
+//
+// A 'token' is a bit value associated with a probability, either fixed
+// or a later-to-be-determined after statistics have been collected.
+// For dynamic probability, we just record the slot id (idx) for the probability
+// value in the final probability array (uint8_t* probas in VP8EmitTokens).
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "./cost.h"
+#include "./vp8enci.h"
+
+#if !defined(DISABLE_TOKEN_BUFFER)
+
+// we use pages to reduce the number of memcpy()
+#define MAX_NUM_TOKEN 8192 // max number of token per page
+#define FIXED_PROBA_BIT (1u << 14)
+
+struct VP8Tokens {
+ uint16_t tokens_[MAX_NUM_TOKEN]; // bit#15: bit
+ // bit #14: constant proba or idx
+ // bits 0..13: slot or constant proba
+ VP8Tokens* next_;
+};
+
+//------------------------------------------------------------------------------
+
+void VP8TBufferInit(VP8TBuffer* const b) {
+ b->tokens_ = NULL;
+ b->pages_ = NULL;
+ b->last_page_ = &b->pages_;
+ b->left_ = 0;
+ b->error_ = 0;
+}
+
+void VP8TBufferClear(VP8TBuffer* const b) {
+ if (b != NULL) {
+ const VP8Tokens* p = b->pages_;
+ while (p != NULL) {
+ const VP8Tokens* const next = p->next_;
+ free((void*)p);
+ p = next;
+ }
+ VP8TBufferInit(b);
+ }
+}
+
+static int TBufferNewPage(VP8TBuffer* const b) {
+ VP8Tokens* const page = b->error_ ? NULL : (VP8Tokens*)malloc(sizeof(*page));
+ if (page == NULL) {
+ b->error_ = 1;
+ return 0;
+ }
+ *b->last_page_ = page;
+ b->last_page_ = &page->next_;
+ b->left_ = MAX_NUM_TOKEN;
+ b->tokens_ = page->tokens_;
+ page->next_ = NULL;
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+
+#define TOKEN_ID(t, b, ctx, p) \
+ ((p) + NUM_PROBAS * ((ctx) + NUM_CTX * ((b) + NUM_BANDS * (t))))
+
+static WEBP_INLINE int AddToken(VP8TBuffer* const b,
+ int bit, uint32_t proba_idx) {
+ assert(proba_idx < FIXED_PROBA_BIT);
+ assert(bit == 0 || bit == 1);
+ if (b->left_ > 0 || TBufferNewPage(b)) {
+ const int slot = --b->left_;
+ b->tokens_[slot] = (bit << 15) | proba_idx;
+ }
+ return bit;
+}
+
+static WEBP_INLINE void AddConstantToken(VP8TBuffer* const b,
+ int bit, int proba) {
+ assert(proba < 256);
+ assert(bit == 0 || bit == 1);
+ if (b->left_ > 0 || TBufferNewPage(b)) {
+ const int slot = --b->left_;
+ b->tokens_[slot] = (bit << 15) | FIXED_PROBA_BIT | proba;
+ }
+}
+
+int VP8RecordCoeffTokens(int ctx, int coeff_type, int first, int last,
+ const int16_t* const coeffs,
+ VP8TBuffer* const tokens) {
+ int n = first;
+ uint32_t base_id = TOKEN_ID(coeff_type, n, ctx, 0);
+ if (!AddToken(tokens, last >= 0, base_id + 0)) {
+ return 0;
+ }
+
+ while (n < 16) {
+ const int c = coeffs[n++];
+ const int sign = c < 0;
+ int v = sign ? -c : c;
+ if (!AddToken(tokens, v != 0, base_id + 1)) {
+ ctx = 0;
+ base_id = TOKEN_ID(coeff_type, VP8EncBands[n], ctx, 0);
+ continue;
+ }
+ if (!AddToken(tokens, v > 1, base_id + 2)) {
+ ctx = 1;
+ } else {
+ if (!AddToken(tokens, v > 4, base_id + 3)) {
+ if (AddToken(tokens, v != 2, base_id + 4))
+ AddToken(tokens, v == 4, base_id + 5);
+ } else if (!AddToken(tokens, v > 10, base_id + 6)) {
+ if (!AddToken(tokens, v > 6, base_id + 7)) {
+ AddConstantToken(tokens, v == 6, 159);
+ } else {
+ AddConstantToken(tokens, v >= 9, 165);
+ AddConstantToken(tokens, !(v & 1), 145);
+ }
+ } else {
+ int mask;
+ const uint8_t* tab;
+ if (v < 3 + (8 << 1)) { // VP8Cat3 (3b)
+ AddToken(tokens, 0, base_id + 8);
+ AddToken(tokens, 0, base_id + 9);
+ v -= 3 + (8 << 0);
+ mask = 1 << 2;
+ tab = VP8Cat3;
+ } else if (v < 3 + (8 << 2)) { // VP8Cat4 (4b)
+ AddToken(tokens, 0, base_id + 8);
+ AddToken(tokens, 1, base_id + 9);
+ v -= 3 + (8 << 1);
+ mask = 1 << 3;
+ tab = VP8Cat4;
+ } else if (v < 3 + (8 << 3)) { // VP8Cat5 (5b)
+ AddToken(tokens, 1, base_id + 8);
+ AddToken(tokens, 0, base_id + 10);
+ v -= 3 + (8 << 2);
+ mask = 1 << 4;
+ tab = VP8Cat5;
+ } else { // VP8Cat6 (11b)
+ AddToken(tokens, 1, base_id + 8);
+ AddToken(tokens, 1, base_id + 10);
+ v -= 3 + (8 << 3);
+ mask = 1 << 10;
+ tab = VP8Cat6;
+ }
+ while (mask) {
+ AddConstantToken(tokens, !!(v & mask), *tab++);
+ mask >>= 1;
+ }
+ }
+ ctx = 2;
+ }
+ AddConstantToken(tokens, sign, 128);
+ base_id = TOKEN_ID(coeff_type, VP8EncBands[n], ctx, 0);
+ if (n == 16 || !AddToken(tokens, n <= last, base_id + 0)) {
+ return 1; // EOB
+ }
+ }
+ return 1;
+}
+
+#undef TOKEN_ID
+
+//------------------------------------------------------------------------------
+// This function works, but isn't currently used. Saved for later.
+
+#if 0
+
+static void Record(int bit, proba_t* const stats) {
+ proba_t p = *stats;
+ if (p >= 0xffff0000u) { // an overflow is inbound.
+ p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2.
+ }
+ // record bit count (lower 16 bits) and increment total count (upper 16 bits).
+ p += 0x00010000u + bit;
+ *stats = p;
+}
+
+void VP8TokenToStats(const VP8TBuffer* const b, proba_t* const stats) {
+ const VP8Tokens* p = b->pages_;
+ while (p != NULL) {
+ const int N = (p->next_ == NULL) ? b->left_ : 0;
+ int n = MAX_NUM_TOKEN;
+ while (n-- > N) {
+ const uint16_t token = p->tokens_[n];
+ if (!(token & FIXED_PROBA_BIT)) {
+ Record((token >> 15) & 1, stats + (token & 0x3fffu));
+ }
+ }
+ p = p->next_;
+ }
+}
+
+#endif // 0
+
+//------------------------------------------------------------------------------
+// Final coding pass, with known probabilities
+
+int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw,
+ const uint8_t* const probas, int final_pass) {
+ const VP8Tokens* p = b->pages_;
+ (void)final_pass;
+ if (b->error_) return 0;
+ while (p != NULL) {
+ const VP8Tokens* const next = p->next_;
+ const int N = (next == NULL) ? b->left_ : 0;
+ int n = MAX_NUM_TOKEN;
+ while (n-- > N) {
+ const uint16_t token = p->tokens_[n];
+ const int bit = (token >> 15) & 1;
+ if (token & FIXED_PROBA_BIT) {
+ VP8PutBit(bw, bit, token & 0xffu); // constant proba
+ } else {
+ VP8PutBit(bw, bit, probas[token & 0x3fffu]);
+ }
+ }
+ if (final_pass) free((void*)p);
+ p = next;
+ }
+ if (final_pass) b->pages_ = NULL;
+ return 1;
+}
+
+// Size estimation
+size_t VP8EstimateTokenSize(VP8TBuffer* const b, const uint8_t* const probas) {
+ size_t size = 0;
+ const VP8Tokens* p = b->pages_;
+ if (b->error_) return 0;
+ while (p != NULL) {
+ const VP8Tokens* const next = p->next_;
+ const int N = (next == NULL) ? b->left_ : 0;
+ int n = MAX_NUM_TOKEN;
+ while (n-- > N) {
+ const uint16_t token = p->tokens_[n];
+ const int bit = token & (1 << 15);
+ if (token & FIXED_PROBA_BIT) {
+ size += VP8BitCost(bit, token & 0xffu);
+ } else {
+ size += VP8BitCost(bit, probas[token & 0x3fffu]);
+ }
+ }
+ p = next;
+ }
+ return size;
+}
+
+//------------------------------------------------------------------------------
+
+#else // DISABLE_TOKEN_BUFFER
+
+void VP8TBufferInit(VP8TBuffer* const b) {
+ (void)b;
+}
+void VP8TBufferClear(VP8TBuffer* const b) {
+ (void)b;
+}
+
+#endif // !DISABLE_TOKEN_BUFFER
+
diff --git a/src/3rdparty/libwebp/src/enc/tree.c b/src/3rdparty/libwebp/src/enc/tree.c
new file mode 100644
index 0000000..e5d05e5
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/tree.c
@@ -0,0 +1,504 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Coding of token probabilities, intra modes and segments.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "./vp8enci.h"
+
+//------------------------------------------------------------------------------
+// Default probabilities
+
+// Paragraph 13.5
+const uint8_t
+ VP8CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
+ { { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 },
+ { 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 },
+ { 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 },
+ { 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 },
+ { 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 },
+ },
+ { { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 },
+ { 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 },
+ { 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 },
+ },
+ { { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 },
+ { 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 },
+ { 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 },
+ { 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 },
+ { 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 },
+ { 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 },
+ { 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
+ }
+ },
+ { { { 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 },
+ { 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 },
+ { 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 }
+ },
+ { { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 },
+ { 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 },
+ { 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 }
+ },
+ { { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 },
+ { 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 },
+ { 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 }
+ },
+ { { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 },
+ { 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 },
+ { 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 },
+ { 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 },
+ { 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 }
+ },
+ { { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 },
+ { 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 },
+ { 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 }
+ },
+ { { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 },
+ { 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 },
+ { 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 }
+ },
+ { { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 },
+ { 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 }
+ }
+ },
+ { { { 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 },
+ { 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 },
+ { 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 }
+ },
+ { { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 },
+ { 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 },
+ { 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 },
+ { 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 },
+ { 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 }
+ },
+ { { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 },
+ { 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 },
+ { 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
+ }
+ },
+ { { { 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 },
+ { 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 },
+ { 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 }
+ },
+ { { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 },
+ { 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 },
+ { 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 }
+ },
+ { { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 },
+ { 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 },
+ { 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 }
+ },
+ { { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 },
+ { 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 },
+ { 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 }
+ },
+ { { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 },
+ { 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 },
+ { 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 }
+ },
+ { { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 },
+ { 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 },
+ { 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 },
+ { 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 },
+ { 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 }
+ },
+ { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
+ }
+ }
+};
+
+void VP8DefaultProbas(VP8Encoder* const enc) {
+ VP8Proba* const probas = &enc->proba_;
+ probas->use_skip_proba_ = 0;
+ memset(probas->segments_, 255u, sizeof(probas->segments_));
+ memcpy(probas->coeffs_, VP8CoeffsProba0, sizeof(VP8CoeffsProba0));
+ // Note: we could hard-code the level_costs_ corresponding to VP8CoeffsProba0,
+ // but that's ~11k of static data. Better call VP8CalculateLevelCosts() later.
+ probas->dirty_ = 1;
+}
+
+// Paragraph 11.5. 900bytes.
+static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = {
+ { { 231, 120, 48, 89, 115, 113, 120, 152, 112 },
+ { 152, 179, 64, 126, 170, 118, 46, 70, 95 },
+ { 175, 69, 143, 80, 85, 82, 72, 155, 103 },
+ { 56, 58, 10, 171, 218, 189, 17, 13, 152 },
+ { 114, 26, 17, 163, 44, 195, 21, 10, 173 },
+ { 121, 24, 80, 195, 26, 62, 44, 64, 85 },
+ { 144, 71, 10, 38, 171, 213, 144, 34, 26 },
+ { 170, 46, 55, 19, 136, 160, 33, 206, 71 },
+ { 63, 20, 8, 114, 114, 208, 12, 9, 226 },
+ { 81, 40, 11, 96, 182, 84, 29, 16, 36 } },
+ { { 134, 183, 89, 137, 98, 101, 106, 165, 148 },
+ { 72, 187, 100, 130, 157, 111, 32, 75, 80 },
+ { 66, 102, 167, 99, 74, 62, 40, 234, 128 },
+ { 41, 53, 9, 178, 241, 141, 26, 8, 107 },
+ { 74, 43, 26, 146, 73, 166, 49, 23, 157 },
+ { 65, 38, 105, 160, 51, 52, 31, 115, 128 },
+ { 104, 79, 12, 27, 217, 255, 87, 17, 7 },
+ { 87, 68, 71, 44, 114, 51, 15, 186, 23 },
+ { 47, 41, 14, 110, 182, 183, 21, 17, 194 },
+ { 66, 45, 25, 102, 197, 189, 23, 18, 22 } },
+ { { 88, 88, 147, 150, 42, 46, 45, 196, 205 },
+ { 43, 97, 183, 117, 85, 38, 35, 179, 61 },
+ { 39, 53, 200, 87, 26, 21, 43, 232, 171 },
+ { 56, 34, 51, 104, 114, 102, 29, 93, 77 },
+ { 39, 28, 85, 171, 58, 165, 90, 98, 64 },
+ { 34, 22, 116, 206, 23, 34, 43, 166, 73 },
+ { 107, 54, 32, 26, 51, 1, 81, 43, 31 },
+ { 68, 25, 106, 22, 64, 171, 36, 225, 114 },
+ { 34, 19, 21, 102, 132, 188, 16, 76, 124 },
+ { 62, 18, 78, 95, 85, 57, 50, 48, 51 } },
+ { { 193, 101, 35, 159, 215, 111, 89, 46, 111 },
+ { 60, 148, 31, 172, 219, 228, 21, 18, 111 },
+ { 112, 113, 77, 85, 179, 255, 38, 120, 114 },
+ { 40, 42, 1, 196, 245, 209, 10, 25, 109 },
+ { 88, 43, 29, 140, 166, 213, 37, 43, 154 },
+ { 61, 63, 30, 155, 67, 45, 68, 1, 209 },
+ { 100, 80, 8, 43, 154, 1, 51, 26, 71 },
+ { 142, 78, 78, 16, 255, 128, 34, 197, 171 },
+ { 41, 40, 5, 102, 211, 183, 4, 1, 221 },
+ { 51, 50, 17, 168, 209, 192, 23, 25, 82 } },
+ { { 138, 31, 36, 171, 27, 166, 38, 44, 229 },
+ { 67, 87, 58, 169, 82, 115, 26, 59, 179 },
+ { 63, 59, 90, 180, 59, 166, 93, 73, 154 },
+ { 40, 40, 21, 116, 143, 209, 34, 39, 175 },
+ { 47, 15, 16, 183, 34, 223, 49, 45, 183 },
+ { 46, 17, 33, 183, 6, 98, 15, 32, 183 },
+ { 57, 46, 22, 24, 128, 1, 54, 17, 37 },
+ { 65, 32, 73, 115, 28, 128, 23, 128, 205 },
+ { 40, 3, 9, 115, 51, 192, 18, 6, 223 },
+ { 87, 37, 9, 115, 59, 77, 64, 21, 47 } },
+ { { 104, 55, 44, 218, 9, 54, 53, 130, 226 },
+ { 64, 90, 70, 205, 40, 41, 23, 26, 57 },
+ { 54, 57, 112, 184, 5, 41, 38, 166, 213 },
+ { 30, 34, 26, 133, 152, 116, 10, 32, 134 },
+ { 39, 19, 53, 221, 26, 114, 32, 73, 255 },
+ { 31, 9, 65, 234, 2, 15, 1, 118, 73 },
+ { 75, 32, 12, 51, 192, 255, 160, 43, 51 },
+ { 88, 31, 35, 67, 102, 85, 55, 186, 85 },
+ { 56, 21, 23, 111, 59, 205, 45, 37, 192 },
+ { 55, 38, 70, 124, 73, 102, 1, 34, 98 } },
+ { { 125, 98, 42, 88, 104, 85, 117, 175, 82 },
+ { 95, 84, 53, 89, 128, 100, 113, 101, 45 },
+ { 75, 79, 123, 47, 51, 128, 81, 171, 1 },
+ { 57, 17, 5, 71, 102, 57, 53, 41, 49 },
+ { 38, 33, 13, 121, 57, 73, 26, 1, 85 },
+ { 41, 10, 67, 138, 77, 110, 90, 47, 114 },
+ { 115, 21, 2, 10, 102, 255, 166, 23, 6 },
+ { 101, 29, 16, 10, 85, 128, 101, 196, 26 },
+ { 57, 18, 10, 102, 102, 213, 34, 20, 43 },
+ { 117, 20, 15, 36, 163, 128, 68, 1, 26 } },
+ { { 102, 61, 71, 37, 34, 53, 31, 243, 192 },
+ { 69, 60, 71, 38, 73, 119, 28, 222, 37 },
+ { 68, 45, 128, 34, 1, 47, 11, 245, 171 },
+ { 62, 17, 19, 70, 146, 85, 55, 62, 70 },
+ { 37, 43, 37, 154, 100, 163, 85, 160, 1 },
+ { 63, 9, 92, 136, 28, 64, 32, 201, 85 },
+ { 75, 15, 9, 9, 64, 255, 184, 119, 16 },
+ { 86, 6, 28, 5, 64, 255, 25, 248, 1 },
+ { 56, 8, 17, 132, 137, 255, 55, 116, 128 },
+ { 58, 15, 20, 82, 135, 57, 26, 121, 40 } },
+ { { 164, 50, 31, 137, 154, 133, 25, 35, 218 },
+ { 51, 103, 44, 131, 131, 123, 31, 6, 158 },
+ { 86, 40, 64, 135, 148, 224, 45, 183, 128 },
+ { 22, 26, 17, 131, 240, 154, 14, 1, 209 },
+ { 45, 16, 21, 91, 64, 222, 7, 1, 197 },
+ { 56, 21, 39, 155, 60, 138, 23, 102, 213 },
+ { 83, 12, 13, 54, 192, 255, 68, 47, 28 },
+ { 85, 26, 85, 85, 128, 128, 32, 146, 171 },
+ { 18, 11, 7, 63, 144, 171, 4, 4, 246 },
+ { 35, 27, 10, 146, 174, 171, 12, 26, 128 } },
+ { { 190, 80, 35, 99, 180, 80, 126, 54, 45 },
+ { 85, 126, 47, 87, 176, 51, 41, 20, 32 },
+ { 101, 75, 128, 139, 118, 146, 116, 128, 85 },
+ { 56, 41, 15, 176, 236, 85, 37, 9, 62 },
+ { 71, 30, 17, 119, 118, 255, 17, 18, 138 },
+ { 101, 38, 60, 138, 55, 70, 43, 26, 142 },
+ { 146, 36, 19, 30, 171, 255, 97, 27, 20 },
+ { 138, 45, 61, 62, 219, 1, 81, 188, 64 },
+ { 32, 41, 20, 117, 151, 142, 20, 21, 163 },
+ { 112, 19, 12, 61, 195, 128, 48, 4, 24 } }
+};
+
+static int PutI4Mode(VP8BitWriter* const bw, int mode,
+ const uint8_t* const prob) {
+ if (VP8PutBit(bw, mode != B_DC_PRED, prob[0])) {
+ if (VP8PutBit(bw, mode != B_TM_PRED, prob[1])) {
+ if (VP8PutBit(bw, mode != B_VE_PRED, prob[2])) {
+ if (!VP8PutBit(bw, mode >= B_LD_PRED, prob[3])) {
+ if (VP8PutBit(bw, mode != B_HE_PRED, prob[4])) {
+ VP8PutBit(bw, mode != B_RD_PRED, prob[5]);
+ }
+ } else {
+ if (VP8PutBit(bw, mode != B_LD_PRED, prob[6])) {
+ if (VP8PutBit(bw, mode != B_VL_PRED, prob[7])) {
+ VP8PutBit(bw, mode != B_HD_PRED, prob[8]);
+ }
+ }
+ }
+ }
+ }
+ }
+ return mode;
+}
+
+static void PutI16Mode(VP8BitWriter* const bw, int mode) {
+ if (VP8PutBit(bw, (mode == TM_PRED || mode == H_PRED), 156)) {
+ VP8PutBit(bw, mode == TM_PRED, 128); // TM or HE
+ } else {
+ VP8PutBit(bw, mode == V_PRED, 163); // VE or DC
+ }
+}
+
+static void PutUVMode(VP8BitWriter* const bw, int uv_mode) {
+ if (VP8PutBit(bw, uv_mode != DC_PRED, 142)) {
+ if (VP8PutBit(bw, uv_mode != V_PRED, 114)) {
+ VP8PutBit(bw, uv_mode != H_PRED, 183); // else: TM_PRED
+ }
+ }
+}
+
+static void PutSegment(VP8BitWriter* const bw, int s, const uint8_t* p) {
+ if (VP8PutBit(bw, s >= 2, p[0])) p += 1;
+ VP8PutBit(bw, s & 1, p[1]);
+}
+
+void VP8CodeIntraModes(VP8Encoder* const enc) {
+ VP8BitWriter* const bw = &enc->bw_;
+ VP8EncIterator it;
+ VP8IteratorInit(enc, &it);
+ do {
+ const VP8MBInfo* const mb = it.mb_;
+ const uint8_t* preds = it.preds_;
+ if (enc->segment_hdr_.update_map_) {
+ PutSegment(bw, mb->segment_, enc->proba_.segments_);
+ }
+ if (enc->proba_.use_skip_proba_) {
+ VP8PutBit(bw, mb->skip_, enc->proba_.skip_proba_);
+ }
+ if (VP8PutBit(bw, (mb->type_ != 0), 145)) { // i16x16
+ PutI16Mode(bw, preds[0]);
+ } else {
+ const int preds_w = enc->preds_w_;
+ const uint8_t* top_pred = preds - preds_w;
+ int x, y;
+ for (y = 0; y < 4; ++y) {
+ int left = preds[-1];
+ for (x = 0; x < 4; ++x) {
+ const uint8_t* const probas = kBModesProba[top_pred[x]][left];
+ left = PutI4Mode(bw, preds[x], probas);
+ }
+ top_pred = preds;
+ preds += preds_w;
+ }
+ }
+ PutUVMode(bw, mb->uv_mode_);
+ } while (VP8IteratorNext(&it));
+}
+
+//------------------------------------------------------------------------------
+// Paragraph 13
+
+const uint8_t
+ VP8CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
+ { { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 },
+ { 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ }
+ },
+ { { { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 },
+ { 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 }
+ },
+ { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ }
+ },
+ { { { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 },
+ { 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ }
+ },
+ { { { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 },
+ { 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ }
+ }
+};
+
+void VP8WriteProbas(VP8BitWriter* const bw, const VP8Proba* const probas) {
+ int t, b, c, p;
+ for (t = 0; t < NUM_TYPES; ++t) {
+ for (b = 0; b < NUM_BANDS; ++b) {
+ for (c = 0; c < NUM_CTX; ++c) {
+ for (p = 0; p < NUM_PROBAS; ++p) {
+ const uint8_t p0 = probas->coeffs_[t][b][c][p];
+ const int update = (p0 != VP8CoeffsProba0[t][b][c][p]);
+ if (VP8PutBit(bw, update, VP8CoeffsUpdateProba[t][b][c][p])) {
+ VP8PutValue(bw, p0, 8);
+ }
+ }
+ }
+ }
+ }
+ if (VP8PutBitUniform(bw, probas->use_skip_proba_)) {
+ VP8PutValue(bw, probas->skip_proba_, 8);
+ }
+}
+
diff --git a/src/3rdparty/libwebp/src/enc/vp8enci.h b/src/3rdparty/libwebp/src/enc/vp8enci.h
new file mode 100644
index 0000000..71adf6c
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/vp8enci.h
@@ -0,0 +1,570 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebP encoder: internal header.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_ENC_VP8ENCI_H_
+#define WEBP_ENC_VP8ENCI_H_
+
+#include <string.h> // for memcpy()
+#include "../webp/encode.h"
+#include "../dsp/dsp.h"
+#include "../utils/bit_writer.h"
+#include "../utils/thread.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------
+// Various defines and enums
+
+// version numbers
+#define ENC_MAJ_VERSION 0
+#define ENC_MIN_VERSION 4
+#define ENC_REV_VERSION 0
+
+// intra prediction modes
+enum { B_DC_PRED = 0, // 4x4 modes
+ B_TM_PRED = 1,
+ B_VE_PRED = 2,
+ B_HE_PRED = 3,
+ B_RD_PRED = 4,
+ B_VR_PRED = 5,
+ B_LD_PRED = 6,
+ B_VL_PRED = 7,
+ B_HD_PRED = 8,
+ B_HU_PRED = 9,
+ NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10
+
+ // Luma16 or UV modes
+ DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED,
+ H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED,
+ NUM_PRED_MODES = 4
+ };
+
+enum { NUM_MB_SEGMENTS = 4,
+ MAX_NUM_PARTITIONS = 8,
+ NUM_TYPES = 4, // 0: i16-AC, 1: i16-DC, 2:chroma-AC, 3:i4-AC
+ NUM_BANDS = 8,
+ NUM_CTX = 3,
+ NUM_PROBAS = 11,
+ MAX_LF_LEVELS = 64, // Maximum loop filter level
+ MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost
+ MAX_LEVEL = 2047 // max level (note: max codable is 2047 + 67)
+ };
+
+typedef enum { // Rate-distortion optimization levels
+ RD_OPT_NONE = 0, // no rd-opt
+ RD_OPT_BASIC = 1, // basic scoring (no trellis)
+ RD_OPT_TRELLIS = 2, // perform trellis-quant on the final decision only
+ RD_OPT_TRELLIS_ALL = 3 // trellis-quant for every scoring (much slower)
+} VP8RDLevel;
+
+// YUV-cache parameters. Cache is 16-pixels wide.
+// The original or reconstructed samples can be accessed using VP8Scan[]
+// The predicted blocks can be accessed using offsets to yuv_p_ and
+// the arrays VP8*ModeOffsets[];
+// +----+ YUV Samples area. See VP8Scan[] for accessing the blocks.
+// Y_OFF |YYYY| <- original samples ('yuv_in_')
+// |YYYY|
+// |YYYY|
+// |YYYY|
+// U_OFF |UUVV| V_OFF (=U_OFF + 8)
+// |UUVV|
+// +----+
+// Y_OFF |YYYY| <- compressed/decoded samples ('yuv_out_')
+// |YYYY| There are two buffers like this ('yuv_out_'/'yuv_out2_')
+// |YYYY|
+// |YYYY|
+// U_OFF |UUVV| V_OFF
+// |UUVV|
+// x2 (for yuv_out2_)
+// +----+ Prediction area ('yuv_p_', size = PRED_SIZE)
+// I16DC16 |YYYY| Intra16 predictions (16x16 block each)
+// |YYYY|
+// |YYYY|
+// |YYYY|
+// I16TM16 |YYYY|
+// |YYYY|
+// |YYYY|
+// |YYYY|
+// I16VE16 |YYYY|
+// |YYYY|
+// |YYYY|
+// |YYYY|
+// I16HE16 |YYYY|
+// |YYYY|
+// |YYYY|
+// |YYYY|
+// +----+ Chroma U/V predictions (16x8 block each)
+// C8DC8 |UUVV|
+// |UUVV|
+// C8TM8 |UUVV|
+// |UUVV|
+// C8VE8 |UUVV|
+// |UUVV|
+// C8HE8 |UUVV|
+// |UUVV|
+// +----+ Intra 4x4 predictions (4x4 block each)
+// |YYYY| I4DC4 I4TM4 I4VE4 I4HE4
+// |YYYY| I4RD4 I4VR4 I4LD4 I4VL4
+// |YY..| I4HD4 I4HU4 I4TMP
+// +----+
+#define BPS 16 // this is the common stride
+#define Y_SIZE (BPS * 16)
+#define UV_SIZE (BPS * 8)
+#define YUV_SIZE (Y_SIZE + UV_SIZE)
+#define PRED_SIZE (6 * 16 * BPS + 12 * BPS)
+#define Y_OFF (0)
+#define U_OFF (Y_SIZE)
+#define V_OFF (U_OFF + 8)
+#define ALIGN_CST 15
+#define DO_ALIGN(PTR) ((uintptr_t)((PTR) + ALIGN_CST) & ~ALIGN_CST)
+
+extern const int VP8Scan[16 + 4 + 4]; // in quant.c
+extern const int VP8UVModeOffsets[4]; // in analyze.c
+extern const int VP8I16ModeOffsets[4];
+extern const int VP8I4ModeOffsets[NUM_BMODES];
+
+// Layout of prediction blocks
+// intra 16x16
+#define I16DC16 (0 * 16 * BPS)
+#define I16TM16 (1 * 16 * BPS)
+#define I16VE16 (2 * 16 * BPS)
+#define I16HE16 (3 * 16 * BPS)
+// chroma 8x8, two U/V blocks side by side (hence: 16x8 each)
+#define C8DC8 (4 * 16 * BPS)
+#define C8TM8 (4 * 16 * BPS + 8 * BPS)
+#define C8VE8 (5 * 16 * BPS)
+#define C8HE8 (5 * 16 * BPS + 8 * BPS)
+// intra 4x4
+#define I4DC4 (6 * 16 * BPS + 0)
+#define I4TM4 (6 * 16 * BPS + 4)
+#define I4VE4 (6 * 16 * BPS + 8)
+#define I4HE4 (6 * 16 * BPS + 12)
+#define I4RD4 (6 * 16 * BPS + 4 * BPS + 0)
+#define I4VR4 (6 * 16 * BPS + 4 * BPS + 4)
+#define I4LD4 (6 * 16 * BPS + 4 * BPS + 8)
+#define I4VL4 (6 * 16 * BPS + 4 * BPS + 12)
+#define I4HD4 (6 * 16 * BPS + 8 * BPS + 0)
+#define I4HU4 (6 * 16 * BPS + 8 * BPS + 4)
+#define I4TMP (6 * 16 * BPS + 8 * BPS + 8)
+
+typedef int64_t score_t; // type used for scores, rate, distortion
+#define MAX_COST ((score_t)0x7fffffffffffffLL)
+
+#define QFIX 17
+#define BIAS(b) ((b) << (QFIX - 8))
+// Fun fact: this is the _only_ line where we're actually being lossy and
+// discarding bits.
+static WEBP_INLINE int QUANTDIV(int n, int iQ, int B) {
+ return (n * iQ + B) >> QFIX;
+}
+
+// size of histogram used by CollectHistogram.
+#define MAX_COEFF_THRESH 31
+typedef struct VP8Histogram VP8Histogram;
+struct VP8Histogram {
+ // TODO(skal): we only need to store the max_value and last_non_zero actually.
+ int distribution[MAX_COEFF_THRESH + 1];
+};
+
+// Uncomment the following to remove token-buffer code:
+// #define DISABLE_TOKEN_BUFFER
+
+//------------------------------------------------------------------------------
+// Headers
+
+typedef uint32_t proba_t; // 16b + 16b
+typedef uint8_t ProbaArray[NUM_CTX][NUM_PROBAS];
+typedef proba_t StatsArray[NUM_CTX][NUM_PROBAS];
+typedef uint16_t CostArray[NUM_CTX][MAX_VARIABLE_LEVEL + 1];
+typedef double LFStats[NUM_MB_SEGMENTS][MAX_LF_LEVELS]; // filter stats
+
+typedef struct VP8Encoder VP8Encoder;
+
+// segment features
+typedef struct {
+ int num_segments_; // Actual number of segments. 1 segment only = unused.
+ int update_map_; // whether to update the segment map or not.
+ // must be 0 if there's only 1 segment.
+ int size_; // bit-cost for transmitting the segment map
+} VP8SegmentHeader;
+
+// Struct collecting all frame-persistent probabilities.
+typedef struct {
+ uint8_t segments_[3]; // probabilities for segment tree
+ uint8_t skip_proba_; // final probability of being skipped.
+ ProbaArray coeffs_[NUM_TYPES][NUM_BANDS]; // 924 bytes
+ StatsArray stats_[NUM_TYPES][NUM_BANDS]; // 4224 bytes
+ CostArray level_cost_[NUM_TYPES][NUM_BANDS]; // 11.4k
+ int dirty_; // if true, need to call VP8CalculateLevelCosts()
+ int use_skip_proba_; // Note: we always use skip_proba for now.
+ int nb_skip_; // number of skipped blocks
+} VP8Proba;
+
+// Filter parameters. Not actually used in the code (we don't perform
+// the in-loop filtering), but filled from user's config
+typedef struct {
+ int simple_; // filtering type: 0=complex, 1=simple
+ int level_; // base filter level [0..63]
+ int sharpness_; // [0..7]
+ int i4x4_lf_delta_; // delta filter level for i4x4 relative to i16x16
+} VP8FilterHeader;
+
+//------------------------------------------------------------------------------
+// Informations about the macroblocks.
+
+typedef struct {
+ // block type
+ unsigned int type_:2; // 0=i4x4, 1=i16x16
+ unsigned int uv_mode_:2;
+ unsigned int skip_:1;
+ unsigned int segment_:2;
+ uint8_t alpha_; // quantization-susceptibility
+} VP8MBInfo;
+
+typedef struct VP8Matrix {
+ uint16_t q_[16]; // quantizer steps
+ uint16_t iq_[16]; // reciprocals, fixed point.
+ uint16_t bias_[16]; // rounding bias
+ uint16_t zthresh_[16]; // value under which a coefficient is zeroed
+ uint16_t sharpen_[16]; // frequency boosters for slight sharpening
+} VP8Matrix;
+
+typedef struct {
+ VP8Matrix y1_, y2_, uv_; // quantization matrices
+ int alpha_; // quant-susceptibility, range [-127,127]. Zero is neutral.
+ // Lower values indicate a lower risk of blurriness.
+ int beta_; // filter-susceptibility, range [0,255].
+ int quant_; // final segment quantizer.
+ int fstrength_; // final in-loop filtering strength
+ int max_edge_; // max edge delta (for filtering strength)
+ int min_disto_; // minimum distortion required to trigger filtering record
+ // reactivities
+ int lambda_i16_, lambda_i4_, lambda_uv_;
+ int lambda_mode_, lambda_trellis_, tlambda_;
+ int lambda_trellis_i16_, lambda_trellis_i4_, lambda_trellis_uv_;
+} VP8SegmentInfo;
+
+// Handy transient struct to accumulate score and info during RD-optimization
+// and mode evaluation.
+typedef struct {
+ score_t D, SD; // Distortion, spectral distortion
+ score_t H, R, score; // header bits, rate, score.
+ int16_t y_dc_levels[16]; // Quantized levels for luma-DC, luma-AC, chroma.
+ int16_t y_ac_levels[16][16];
+ int16_t uv_levels[4 + 4][16];
+ int mode_i16; // mode number for intra16 prediction
+ uint8_t modes_i4[16]; // mode numbers for intra4 predictions
+ int mode_uv; // mode number of chroma prediction
+ uint32_t nz; // non-zero blocks
+} VP8ModeScore;
+
+// Iterator structure to iterate through macroblocks, pointing to the
+// right neighbouring data (samples, predictions, contexts, ...)
+typedef struct {
+ int x_, y_; // current macroblock
+ int y_stride_, uv_stride_; // respective strides
+ uint8_t* yuv_in_; // input samples
+ uint8_t* yuv_out_; // output samples
+ uint8_t* yuv_out2_; // secondary buffer swapped with yuv_out_.
+ uint8_t* yuv_p_; // scratch buffer for prediction
+ VP8Encoder* enc_; // back-pointer
+ VP8MBInfo* mb_; // current macroblock
+ VP8BitWriter* bw_; // current bit-writer
+ uint8_t* preds_; // intra mode predictors (4x4 blocks)
+ uint32_t* nz_; // non-zero pattern
+ uint8_t i4_boundary_[37]; // 32+5 boundary samples needed by intra4x4
+ uint8_t* i4_top_; // pointer to the current top boundary sample
+ int i4_; // current intra4x4 mode being tested
+ int top_nz_[9]; // top-non-zero context.
+ int left_nz_[9]; // left-non-zero. left_nz[8] is independent.
+ uint64_t bit_count_[4][3]; // bit counters for coded levels.
+ uint64_t luma_bits_; // macroblock bit-cost for luma
+ uint64_t uv_bits_; // macroblock bit-cost for chroma
+ LFStats* lf_stats_; // filter stats (borrowed from enc_)
+ int do_trellis_; // if true, perform extra level optimisation
+ int count_down_; // number of mb still to be processed
+ int count_down0_; // starting counter value (for progress)
+ int percent0_; // saved initial progress percent
+
+ uint8_t* y_left_; // left luma samples (addressable from index -1 to 15).
+ uint8_t* u_left_; // left u samples (addressable from index -1 to 7)
+ uint8_t* v_left_; // left v samples (addressable from index -1 to 7)
+
+ uint8_t* y_top_; // top luma samples at position 'x_'
+ uint8_t* uv_top_; // top u/v samples at position 'x_', packed as 16 bytes
+
+ // memory for storing y/u/v_left_ and yuv_in_/out_*
+ uint8_t yuv_left_mem_[17 + 16 + 16 + 8 + ALIGN_CST]; // memory for *_left_
+ uint8_t yuv_mem_[3 * YUV_SIZE + PRED_SIZE + ALIGN_CST]; // memory for yuv_*
+} VP8EncIterator;
+
+ // in iterator.c
+// must be called first
+void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it);
+// restart a scan
+void VP8IteratorReset(VP8EncIterator* const it);
+// reset iterator position to row 'y'
+void VP8IteratorSetRow(VP8EncIterator* const it, int y);
+// set count down (=number of iterations to go)
+void VP8IteratorSetCountDown(VP8EncIterator* const it, int count_down);
+// return true if iteration is finished
+int VP8IteratorIsDone(const VP8EncIterator* const it);
+// Import uncompressed samples from source.
+// If tmp_32 is not NULL, import boundary samples too.
+// tmp_32 is a 32-bytes scratch buffer that must be aligned in memory.
+void VP8IteratorImport(VP8EncIterator* const it, uint8_t* tmp_32);
+// export decimated samples
+void VP8IteratorExport(const VP8EncIterator* const it);
+// go to next macroblock. Returns false if not finished.
+int VP8IteratorNext(VP8EncIterator* const it);
+// save the yuv_out_ boundary values to top_/left_ arrays for next iterations.
+void VP8IteratorSaveBoundary(VP8EncIterator* const it);
+// Report progression based on macroblock rows. Return 0 for user-abort request.
+int VP8IteratorProgress(const VP8EncIterator* const it,
+ int final_delta_percent);
+// Intra4x4 iterations
+void VP8IteratorStartI4(VP8EncIterator* const it);
+// returns true if not done.
+int VP8IteratorRotateI4(VP8EncIterator* const it,
+ const uint8_t* const yuv_out);
+
+// Non-zero context setup/teardown
+void VP8IteratorNzToBytes(VP8EncIterator* const it);
+void VP8IteratorBytesToNz(VP8EncIterator* const it);
+
+// Helper functions to set mode properties
+void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode);
+void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes);
+void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode);
+void VP8SetSkip(const VP8EncIterator* const it, int skip);
+void VP8SetSegment(const VP8EncIterator* const it, int segment);
+
+//------------------------------------------------------------------------------
+// Paginated token buffer
+
+typedef struct VP8Tokens VP8Tokens; // struct details in token.c
+
+typedef struct {
+#if !defined(DISABLE_TOKEN_BUFFER)
+ VP8Tokens* pages_; // first page
+ VP8Tokens** last_page_; // last page
+ uint16_t* tokens_; // set to (*last_page_)->tokens_
+ int left_; // how many free tokens left before the page is full.
+#endif
+ int error_; // true in case of malloc error
+} VP8TBuffer;
+
+void VP8TBufferInit(VP8TBuffer* const b); // initialize an empty buffer
+void VP8TBufferClear(VP8TBuffer* const b); // de-allocate pages memory
+
+#if !defined(DISABLE_TOKEN_BUFFER)
+
+// Finalizes bitstream when probabilities are known.
+// Deletes the allocated token memory if final_pass is true.
+int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw,
+ const uint8_t* const probas, int final_pass);
+
+// record the coding of coefficients without knowing the probabilities yet
+int VP8RecordCoeffTokens(int ctx, int coeff_type, int first, int last,
+ const int16_t* const coeffs,
+ VP8TBuffer* const tokens);
+
+// Estimate the final coded size given a set of 'probas'.
+size_t VP8EstimateTokenSize(VP8TBuffer* const b, const uint8_t* const probas);
+
+// unused for now
+void VP8TokenToStats(const VP8TBuffer* const b, proba_t* const stats);
+
+#endif // !DISABLE_TOKEN_BUFFER
+
+//------------------------------------------------------------------------------
+// VP8Encoder
+
+struct VP8Encoder {
+ const WebPConfig* config_; // user configuration and parameters
+ WebPPicture* pic_; // input / output picture
+
+ // headers
+ VP8FilterHeader filter_hdr_; // filtering information
+ VP8SegmentHeader segment_hdr_; // segment information
+
+ int profile_; // VP8's profile, deduced from Config.
+
+ // dimension, in macroblock units.
+ int mb_w_, mb_h_;
+ int preds_w_; // stride of the *preds_ prediction plane (=4*mb_w + 1)
+
+ // number of partitions (1, 2, 4 or 8 = MAX_NUM_PARTITIONS)
+ int num_parts_;
+
+ // per-partition boolean decoders.
+ VP8BitWriter bw_; // part0
+ VP8BitWriter parts_[MAX_NUM_PARTITIONS]; // token partitions
+ VP8TBuffer tokens_; // token buffer
+
+ int percent_; // for progress
+
+ // transparency blob
+ int has_alpha_;
+ uint8_t* alpha_data_; // non-NULL if transparency is present
+ uint32_t alpha_data_size_;
+ WebPWorker alpha_worker_;
+
+ // enhancement layer
+ int use_layer_;
+ VP8BitWriter layer_bw_;
+ uint8_t* layer_data_;
+ size_t layer_data_size_;
+
+ // quantization info (one set of DC/AC dequant factor per segment)
+ VP8SegmentInfo dqm_[NUM_MB_SEGMENTS];
+ int base_quant_; // nominal quantizer value. Only used
+ // for relative coding of segments' quant.
+ int alpha_; // global susceptibility (<=> complexity)
+ int uv_alpha_; // U/V quantization susceptibility
+ // global offset of quantizers, shared by all segments
+ int dq_y1_dc_;
+ int dq_y2_dc_, dq_y2_ac_;
+ int dq_uv_dc_, dq_uv_ac_;
+
+ // probabilities and statistics
+ VP8Proba proba_;
+ uint64_t sse_[4]; // sum of Y/U/V/A squared errors for all macroblocks
+ uint64_t sse_count_; // pixel count for the sse_[] stats
+ int coded_size_;
+ int residual_bytes_[3][4];
+ int block_count_[3];
+
+ // quality/speed settings
+ int method_; // 0=fastest, 6=best/slowest.
+ VP8RDLevel rd_opt_level_; // Deduced from method_.
+ int max_i4_header_bits_; // partition #0 safeness factor
+ int thread_level_; // derived from config->thread_level
+ int do_search_; // derived from config->target_XXX
+ int use_tokens_; // if true, use token buffer
+
+ // Memory
+ VP8MBInfo* mb_info_; // contextual macroblock infos (mb_w_ + 1)
+ uint8_t* preds_; // predictions modes: (4*mb_w+1) * (4*mb_h+1)
+ uint32_t* nz_; // non-zero bit context: mb_w+1
+ uint8_t *y_top_; // top luma samples.
+ uint8_t *uv_top_; // top u/v samples.
+ // U and V are packed into 16 bytes (8 U + 8 V)
+ LFStats *lf_stats_; // autofilter stats (if NULL, autofilter is off)
+};
+
+//------------------------------------------------------------------------------
+// internal functions. Not public.
+
+ // in tree.c
+extern const uint8_t VP8CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS];
+extern const uint8_t
+ VP8CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS];
+// Reset the token probabilities to their initial (default) values
+void VP8DefaultProbas(VP8Encoder* const enc);
+// Write the token probabilities
+void VP8WriteProbas(VP8BitWriter* const bw, const VP8Proba* const probas);
+// Writes the partition #0 modes (that is: all intra modes)
+void VP8CodeIntraModes(VP8Encoder* const enc);
+
+ // in syntax.c
+// Generates the final bitstream by coding the partition0 and headers,
+// and appending an assembly of all the pre-coded token partitions.
+// Return true if everything is ok.
+int VP8EncWrite(VP8Encoder* const enc);
+// Release memory allocated for bit-writing in VP8EncLoop & seq.
+void VP8EncFreeBitWriters(VP8Encoder* const enc);
+
+ // in frame.c
+extern const uint8_t VP8EncBands[16 + 1];
+extern const uint8_t VP8Cat3[];
+extern const uint8_t VP8Cat4[];
+extern const uint8_t VP8Cat5[];
+extern const uint8_t VP8Cat6[];
+
+// Form all the four Intra16x16 predictions in the yuv_p_ cache
+void VP8MakeLuma16Preds(const VP8EncIterator* const it);
+// Form all the four Chroma8x8 predictions in the yuv_p_ cache
+void VP8MakeChroma8Preds(const VP8EncIterator* const it);
+// Form all the ten Intra4x4 predictions in the yuv_p_ cache
+// for the 4x4 block it->i4_
+void VP8MakeIntra4Preds(const VP8EncIterator* const it);
+// Rate calculation
+int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd);
+int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]);
+int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd);
+// Main coding calls
+int VP8EncLoop(VP8Encoder* const enc);
+int VP8EncTokenLoop(VP8Encoder* const enc);
+
+ // in webpenc.c
+// Assign an error code to a picture. Return false for convenience.
+int WebPEncodingSetError(const WebPPicture* const pic, WebPEncodingError error);
+int WebPReportProgress(const WebPPicture* const pic,
+ int percent, int* const percent_store);
+
+ // in analysis.c
+// Main analysis loop. Decides the segmentations and complexity.
+// Assigns a first guess for Intra16 and uvmode_ prediction modes.
+int VP8EncAnalyze(VP8Encoder* const enc);
+
+ // in quant.c
+// Sets up segment's quantization values, base_quant_ and filter strengths.
+void VP8SetSegmentParams(VP8Encoder* const enc, float quality);
+// Pick best modes and fills the levels. Returns true if skipped.
+int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd,
+ VP8RDLevel rd_opt);
+
+ // in alpha.c
+void VP8EncInitAlpha(VP8Encoder* const enc); // initialize alpha compression
+int VP8EncStartAlpha(VP8Encoder* const enc); // start alpha coding process
+int VP8EncFinishAlpha(VP8Encoder* const enc); // finalize compressed data
+int VP8EncDeleteAlpha(VP8Encoder* const enc); // delete compressed data
+
+ // in layer.c
+void VP8EncInitLayer(VP8Encoder* const enc); // init everything
+void VP8EncCodeLayerBlock(VP8EncIterator* it); // code one more macroblock
+int VP8EncFinishLayer(VP8Encoder* const enc); // finalize coding
+void VP8EncDeleteLayer(VP8Encoder* enc); // reclaim memory
+
+ // in filter.c
+
+// SSIM utils
+typedef struct {
+ double w, xm, ym, xxm, xym, yym;
+} DistoStats;
+void VP8SSIMAddStats(const DistoStats* const src, DistoStats* const dst);
+void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1,
+ const uint8_t* src2, int stride2,
+ int W, int H, DistoStats* const stats);
+double VP8SSIMGet(const DistoStats* const stats);
+double VP8SSIMGetSquaredError(const DistoStats* const stats);
+
+// autofilter
+void VP8InitFilter(VP8EncIterator* const it);
+void VP8StoreFilterStats(VP8EncIterator* const it);
+void VP8AdjustFilterStrength(VP8EncIterator* const it);
+
+// returns the approximate filtering strength needed to smooth a edge
+// step of 'delta', given a sharpness parameter 'sharpness'.
+int VP8FilterStrengthFromDelta(int sharpness, int delta);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_ENC_VP8ENCI_H_ */
diff --git a/src/3rdparty/libwebp/src/enc/vp8l.c b/src/3rdparty/libwebp/src/enc/vp8l.c
new file mode 100644
index 0000000..1572631
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/vp8l.c
@@ -0,0 +1,1168 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// main entry for the lossless encoder.
+//
+// Author: Vikas Arora (vikaas.arora@gmail.com)
+//
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "./backward_references.h"
+#include "./vp8enci.h"
+#include "./vp8li.h"
+#include "../dsp/lossless.h"
+#include "../utils/bit_writer.h"
+#include "../utils/huffman_encode.h"
+#include "../utils/utils.h"
+#include "../webp/format_constants.h"
+
+#define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer.
+#define MAX_HUFF_IMAGE_SIZE (16 * 1024 * 1024)
+#define MAX_COLORS_FOR_GRAPH 64
+
+// -----------------------------------------------------------------------------
+// Palette
+
+static int CompareColors(const void* p1, const void* p2) {
+ const uint32_t a = *(const uint32_t*)p1;
+ const uint32_t b = *(const uint32_t*)p2;
+ assert(a != b);
+ return (a < b) ? -1 : 1;
+}
+
+// If number of colors in the image is less than or equal to MAX_PALETTE_SIZE,
+// creates a palette and returns true, else returns false.
+static int AnalyzeAndCreatePalette(const WebPPicture* const pic,
+ uint32_t palette[MAX_PALETTE_SIZE],
+ int* const palette_size) {
+ int i, x, y, key;
+ int num_colors = 0;
+ uint8_t in_use[MAX_PALETTE_SIZE * 4] = { 0 };
+ uint32_t colors[MAX_PALETTE_SIZE * 4];
+ static const uint32_t kHashMul = 0x1e35a7bd;
+ const uint32_t* argb = pic->argb;
+ const int width = pic->width;
+ const int height = pic->height;
+ uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0]
+
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ if (argb[x] == last_pix) {
+ continue;
+ }
+ last_pix = argb[x];
+ key = (kHashMul * last_pix) >> PALETTE_KEY_RIGHT_SHIFT;
+ while (1) {
+ if (!in_use[key]) {
+ colors[key] = last_pix;
+ in_use[key] = 1;
+ ++num_colors;
+ if (num_colors > MAX_PALETTE_SIZE) {
+ return 0;
+ }
+ break;
+ } else if (colors[key] == last_pix) {
+ // The color is already there.
+ break;
+ } else {
+ // Some other color sits there.
+ // Do linear conflict resolution.
+ ++key;
+ key &= (MAX_PALETTE_SIZE * 4 - 1); // key mask for 1K buffer.
+ }
+ }
+ }
+ argb += pic->argb_stride;
+ }
+
+ // TODO(skal): could we reuse in_use[] to speed up EncodePalette()?
+ num_colors = 0;
+ for (i = 0; i < (int)(sizeof(in_use) / sizeof(in_use[0])); ++i) {
+ if (in_use[i]) {
+ palette[num_colors] = colors[i];
+ ++num_colors;
+ }
+ }
+
+ qsort(palette, num_colors, sizeof(*palette), CompareColors);
+ *palette_size = num_colors;
+ return 1;
+}
+
+static int AnalyzeEntropy(const uint32_t* argb,
+ int width, int height, int argb_stride,
+ double* const nonpredicted_bits,
+ double* const predicted_bits) {
+ int x, y;
+ const uint32_t* last_line = NULL;
+ uint32_t last_pix = argb[0]; // so we're sure that pix_diff == 0
+
+ VP8LHistogram* nonpredicted = NULL;
+ VP8LHistogram* predicted =
+ (VP8LHistogram*)malloc(2 * sizeof(*predicted));
+ if (predicted == NULL) return 0;
+ nonpredicted = predicted + 1;
+
+ VP8LHistogramInit(predicted, 0);
+ VP8LHistogramInit(nonpredicted, 0);
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ const uint32_t pix = argb[x];
+ const uint32_t pix_diff = VP8LSubPixels(pix, last_pix);
+ if (pix_diff == 0) continue;
+ if (last_line != NULL && pix == last_line[x]) {
+ continue;
+ }
+ last_pix = pix;
+ {
+ const PixOrCopy pix_token = PixOrCopyCreateLiteral(pix);
+ const PixOrCopy pix_diff_token = PixOrCopyCreateLiteral(pix_diff);
+ VP8LHistogramAddSinglePixOrCopy(nonpredicted, &pix_token);
+ VP8LHistogramAddSinglePixOrCopy(predicted, &pix_diff_token);
+ }
+ }
+ last_line = argb;
+ argb += argb_stride;
+ }
+ *nonpredicted_bits = VP8LHistogramEstimateBitsBulk(nonpredicted);
+ *predicted_bits = VP8LHistogramEstimateBitsBulk(predicted);
+ free(predicted);
+ return 1;
+}
+
+static int VP8LEncAnalyze(VP8LEncoder* const enc, WebPImageHint image_hint) {
+ const WebPPicture* const pic = enc->pic_;
+ assert(pic != NULL && pic->argb != NULL);
+
+ enc->use_palette_ =
+ AnalyzeAndCreatePalette(pic, enc->palette_, &enc->palette_size_);
+
+ if (image_hint == WEBP_HINT_GRAPH) {
+ if (enc->use_palette_ && enc->palette_size_ < MAX_COLORS_FOR_GRAPH) {
+ enc->use_palette_ = 0;
+ }
+ }
+
+ if (!enc->use_palette_) {
+ if (image_hint == WEBP_HINT_PHOTO) {
+ enc->use_predict_ = 1;
+ enc->use_cross_color_ = 1;
+ } else {
+ double non_pred_entropy, pred_entropy;
+ if (!AnalyzeEntropy(pic->argb, pic->width, pic->height, pic->argb_stride,
+ &non_pred_entropy, &pred_entropy)) {
+ return 0;
+ }
+ if (pred_entropy < 0.95 * non_pred_entropy) {
+ enc->use_predict_ = 1;
+ enc->use_cross_color_ = 1;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int GetHuffBitLengthsAndCodes(
+ const VP8LHistogramSet* const histogram_image,
+ HuffmanTreeCode* const huffman_codes) {
+ int i, k;
+ int ok = 1;
+ uint64_t total_length_size = 0;
+ uint8_t* mem_buf = NULL;
+ const int histogram_image_size = histogram_image->size;
+
+ // Iterate over all histograms and get the aggregate number of codes used.
+ for (i = 0; i < histogram_image_size; ++i) {
+ const VP8LHistogram* const histo = histogram_image->histograms[i];
+ HuffmanTreeCode* const codes = &huffman_codes[5 * i];
+ for (k = 0; k < 5; ++k) {
+ const int num_symbols = (k == 0) ? VP8LHistogramNumCodes(histo)
+ : (k == 4) ? NUM_DISTANCE_CODES
+ : 256;
+ codes[k].num_symbols = num_symbols;
+ total_length_size += num_symbols;
+ }
+ }
+
+ // Allocate and Set Huffman codes.
+ {
+ uint16_t* codes;
+ uint8_t* lengths;
+ mem_buf = (uint8_t*)WebPSafeCalloc(total_length_size,
+ sizeof(*lengths) + sizeof(*codes));
+ if (mem_buf == NULL) {
+ ok = 0;
+ goto End;
+ }
+ codes = (uint16_t*)mem_buf;
+ lengths = (uint8_t*)&codes[total_length_size];
+ for (i = 0; i < 5 * histogram_image_size; ++i) {
+ const int bit_length = huffman_codes[i].num_symbols;
+ huffman_codes[i].codes = codes;
+ huffman_codes[i].code_lengths = lengths;
+ codes += bit_length;
+ lengths += bit_length;
+ }
+ }
+
+ // Create Huffman trees.
+ for (i = 0; ok && (i < histogram_image_size); ++i) {
+ HuffmanTreeCode* const codes = &huffman_codes[5 * i];
+ VP8LHistogram* const histo = histogram_image->histograms[i];
+ ok = ok && VP8LCreateHuffmanTree(histo->literal_, 15, codes + 0);
+ ok = ok && VP8LCreateHuffmanTree(histo->red_, 15, codes + 1);
+ ok = ok && VP8LCreateHuffmanTree(histo->blue_, 15, codes + 2);
+ ok = ok && VP8LCreateHuffmanTree(histo->alpha_, 15, codes + 3);
+ ok = ok && VP8LCreateHuffmanTree(histo->distance_, 15, codes + 4);
+ }
+
+ End:
+ if (!ok) {
+ free(mem_buf);
+ // If one VP8LCreateHuffmanTree() above fails, we need to clean up behind.
+ memset(huffman_codes, 0, 5 * histogram_image_size * sizeof(*huffman_codes));
+ }
+ return ok;
+}
+
+static void StoreHuffmanTreeOfHuffmanTreeToBitMask(
+ VP8LBitWriter* const bw, const uint8_t* code_length_bitdepth) {
+ // RFC 1951 will calm you down if you are worried about this funny sequence.
+ // This sequence is tuned from that, but more weighted for lower symbol count,
+ // and more spiking histograms.
+ static const uint8_t kStorageOrder[CODE_LENGTH_CODES] = {
+ 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ };
+ int i;
+ // Throw away trailing zeros:
+ int codes_to_store = CODE_LENGTH_CODES;
+ for (; codes_to_store > 4; --codes_to_store) {
+ if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) {
+ break;
+ }
+ }
+ VP8LWriteBits(bw, 4, codes_to_store - 4);
+ for (i = 0; i < codes_to_store; ++i) {
+ VP8LWriteBits(bw, 3, code_length_bitdepth[kStorageOrder[i]]);
+ }
+}
+
+static void ClearHuffmanTreeIfOnlyOneSymbol(
+ HuffmanTreeCode* const huffman_code) {
+ int k;
+ int count = 0;
+ for (k = 0; k < huffman_code->num_symbols; ++k) {
+ if (huffman_code->code_lengths[k] != 0) {
+ ++count;
+ if (count > 1) return;
+ }
+ }
+ for (k = 0; k < huffman_code->num_symbols; ++k) {
+ huffman_code->code_lengths[k] = 0;
+ huffman_code->codes[k] = 0;
+ }
+}
+
+static void StoreHuffmanTreeToBitMask(
+ VP8LBitWriter* const bw,
+ const HuffmanTreeToken* const tokens, const int num_tokens,
+ const HuffmanTreeCode* const huffman_code) {
+ int i;
+ for (i = 0; i < num_tokens; ++i) {
+ const int ix = tokens[i].code;
+ const int extra_bits = tokens[i].extra_bits;
+ VP8LWriteBits(bw, huffman_code->code_lengths[ix], huffman_code->codes[ix]);
+ switch (ix) {
+ case 16:
+ VP8LWriteBits(bw, 2, extra_bits);
+ break;
+ case 17:
+ VP8LWriteBits(bw, 3, extra_bits);
+ break;
+ case 18:
+ VP8LWriteBits(bw, 7, extra_bits);
+ break;
+ }
+ }
+}
+
+static int StoreFullHuffmanCode(VP8LBitWriter* const bw,
+ const HuffmanTreeCode* const tree) {
+ int ok = 0;
+ uint8_t code_length_bitdepth[CODE_LENGTH_CODES] = { 0 };
+ uint16_t code_length_bitdepth_symbols[CODE_LENGTH_CODES] = { 0 };
+ const int max_tokens = tree->num_symbols;
+ int num_tokens;
+ HuffmanTreeCode huffman_code;
+ HuffmanTreeToken* const tokens =
+ (HuffmanTreeToken*)WebPSafeMalloc((uint64_t)max_tokens, sizeof(*tokens));
+ if (tokens == NULL) return 0;
+
+ huffman_code.num_symbols = CODE_LENGTH_CODES;
+ huffman_code.code_lengths = code_length_bitdepth;
+ huffman_code.codes = code_length_bitdepth_symbols;
+
+ VP8LWriteBits(bw, 1, 0);
+ num_tokens = VP8LCreateCompressedHuffmanTree(tree, tokens, max_tokens);
+ {
+ int histogram[CODE_LENGTH_CODES] = { 0 };
+ int i;
+ for (i = 0; i < num_tokens; ++i) {
+ ++histogram[tokens[i].code];
+ }
+
+ if (!VP8LCreateHuffmanTree(histogram, 7, &huffman_code)) {
+ goto End;
+ }
+ }
+
+ StoreHuffmanTreeOfHuffmanTreeToBitMask(bw, code_length_bitdepth);
+ ClearHuffmanTreeIfOnlyOneSymbol(&huffman_code);
+ {
+ int trailing_zero_bits = 0;
+ int trimmed_length = num_tokens;
+ int write_trimmed_length;
+ int length;
+ int i = num_tokens;
+ while (i-- > 0) {
+ const int ix = tokens[i].code;
+ if (ix == 0 || ix == 17 || ix == 18) {
+ --trimmed_length; // discount trailing zeros
+ trailing_zero_bits += code_length_bitdepth[ix];
+ if (ix == 17) {
+ trailing_zero_bits += 3;
+ } else if (ix == 18) {
+ trailing_zero_bits += 7;
+ }
+ } else {
+ break;
+ }
+ }
+ write_trimmed_length = (trimmed_length > 1 && trailing_zero_bits > 12);
+ length = write_trimmed_length ? trimmed_length : num_tokens;
+ VP8LWriteBits(bw, 1, write_trimmed_length);
+ if (write_trimmed_length) {
+ const int nbits = VP8LBitsLog2Ceiling(trimmed_length - 1);
+ const int nbitpairs = (nbits == 0) ? 1 : (nbits + 1) / 2;
+ VP8LWriteBits(bw, 3, nbitpairs - 1);
+ assert(trimmed_length >= 2);
+ VP8LWriteBits(bw, nbitpairs * 2, trimmed_length - 2);
+ }
+ StoreHuffmanTreeToBitMask(bw, tokens, length, &huffman_code);
+ }
+ ok = 1;
+ End:
+ free(tokens);
+ return ok;
+}
+
+static int StoreHuffmanCode(VP8LBitWriter* const bw,
+ const HuffmanTreeCode* const huffman_code) {
+ int i;
+ int count = 0;
+ int symbols[2] = { 0, 0 };
+ const int kMaxBits = 8;
+ const int kMaxSymbol = 1 << kMaxBits;
+
+ // Check whether it's a small tree.
+ for (i = 0; i < huffman_code->num_symbols && count < 3; ++i) {
+ if (huffman_code->code_lengths[i] != 0) {
+ if (count < 2) symbols[count] = i;
+ ++count;
+ }
+ }
+
+ if (count == 0) { // emit minimal tree for empty cases
+ // bits: small tree marker: 1, count-1: 0, large 8-bit code: 0, code: 0
+ VP8LWriteBits(bw, 4, 0x01);
+ return 1;
+ } else if (count <= 2 && symbols[0] < kMaxSymbol && symbols[1] < kMaxSymbol) {
+ VP8LWriteBits(bw, 1, 1); // Small tree marker to encode 1 or 2 symbols.
+ VP8LWriteBits(bw, 1, count - 1);
+ if (symbols[0] <= 1) {
+ VP8LWriteBits(bw, 1, 0); // Code bit for small (1 bit) symbol value.
+ VP8LWriteBits(bw, 1, symbols[0]);
+ } else {
+ VP8LWriteBits(bw, 1, 1);
+ VP8LWriteBits(bw, 8, symbols[0]);
+ }
+ if (count == 2) {
+ VP8LWriteBits(bw, 8, symbols[1]);
+ }
+ return 1;
+ } else {
+ return StoreFullHuffmanCode(bw, huffman_code);
+ }
+}
+
+static void WriteHuffmanCode(VP8LBitWriter* const bw,
+ const HuffmanTreeCode* const code,
+ int code_index) {
+ const int depth = code->code_lengths[code_index];
+ const int symbol = code->codes[code_index];
+ VP8LWriteBits(bw, depth, symbol);
+}
+
+static void StoreImageToBitMask(
+ VP8LBitWriter* const bw, int width, int histo_bits,
+ const VP8LBackwardRefs* const refs,
+ const uint16_t* histogram_symbols,
+ const HuffmanTreeCode* const huffman_codes) {
+ // x and y trace the position in the image.
+ int x = 0;
+ int y = 0;
+ const int histo_xsize = histo_bits ? VP8LSubSampleSize(width, histo_bits) : 1;
+ int i;
+ for (i = 0; i < refs->size; ++i) {
+ const PixOrCopy* const v = &refs->refs[i];
+ const int histogram_ix = histogram_symbols[histo_bits ?
+ (y >> histo_bits) * histo_xsize +
+ (x >> histo_bits) : 0];
+ const HuffmanTreeCode* const codes = huffman_codes + 5 * histogram_ix;
+ if (PixOrCopyIsCacheIdx(v)) {
+ const int code = PixOrCopyCacheIdx(v);
+ const int literal_ix = 256 + NUM_LENGTH_CODES + code;
+ WriteHuffmanCode(bw, codes, literal_ix);
+ } else if (PixOrCopyIsLiteral(v)) {
+ static const int order[] = { 1, 2, 0, 3 };
+ int k;
+ for (k = 0; k < 4; ++k) {
+ const int code = PixOrCopyLiteral(v, order[k]);
+ WriteHuffmanCode(bw, codes + k, code);
+ }
+ } else {
+ int bits, n_bits;
+ int code, distance;
+
+ VP8LPrefixEncode(v->len, &code, &n_bits, &bits);
+ WriteHuffmanCode(bw, codes, 256 + code);
+ VP8LWriteBits(bw, n_bits, bits);
+
+ distance = PixOrCopyDistance(v);
+ VP8LPrefixEncode(distance, &code, &n_bits, &bits);
+ WriteHuffmanCode(bw, codes + 4, code);
+ VP8LWriteBits(bw, n_bits, bits);
+ }
+ x += PixOrCopyLength(v);
+ while (x >= width) {
+ x -= width;
+ ++y;
+ }
+ }
+}
+
+// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31
+static int EncodeImageNoHuffman(VP8LBitWriter* const bw,
+ const uint32_t* const argb,
+ int width, int height, int quality) {
+ int i;
+ int ok = 0;
+ VP8LBackwardRefs refs;
+ HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } };
+ const uint16_t histogram_symbols[1] = { 0 }; // only one tree, one symbol
+ VP8LHistogramSet* const histogram_image = VP8LAllocateHistogramSet(1, 0);
+ if (histogram_image == NULL) return 0;
+
+ // Calculate backward references from ARGB image.
+ if (!VP8LGetBackwardReferences(width, height, argb, quality, 0, 1, &refs)) {
+ goto Error;
+ }
+ // Build histogram image and symbols from backward references.
+ VP8LHistogramStoreRefs(&refs, histogram_image->histograms[0]);
+
+ // Create Huffman bit lengths and codes for each histogram image.
+ assert(histogram_image->size == 1);
+ if (!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
+ goto Error;
+ }
+
+ // No color cache, no Huffman image.
+ VP8LWriteBits(bw, 1, 0);
+
+ // Store Huffman codes.
+ for (i = 0; i < 5; ++i) {
+ HuffmanTreeCode* const codes = &huffman_codes[i];
+ if (!StoreHuffmanCode(bw, codes)) {
+ goto Error;
+ }
+ ClearHuffmanTreeIfOnlyOneSymbol(codes);
+ }
+
+ // Store actual literals.
+ StoreImageToBitMask(bw, width, 0, &refs, histogram_symbols, huffman_codes);
+ ok = 1;
+
+ Error:
+ free(histogram_image);
+ VP8LClearBackwardRefs(&refs);
+ free(huffman_codes[0].codes);
+ return ok;
+}
+
+static int EncodeImageInternal(VP8LBitWriter* const bw,
+ const uint32_t* const argb,
+ int width, int height, int quality,
+ int cache_bits, int histogram_bits) {
+ int ok = 0;
+ const int use_2d_locality = 1;
+ const int use_color_cache = (cache_bits > 0);
+ const uint32_t histogram_image_xysize =
+ VP8LSubSampleSize(width, histogram_bits) *
+ VP8LSubSampleSize(height, histogram_bits);
+ VP8LHistogramSet* histogram_image =
+ VP8LAllocateHistogramSet(histogram_image_xysize, 0);
+ int histogram_image_size = 0;
+ size_t bit_array_size = 0;
+ HuffmanTreeCode* huffman_codes = NULL;
+ VP8LBackwardRefs refs;
+ uint16_t* const histogram_symbols =
+ (uint16_t*)WebPSafeMalloc((uint64_t)histogram_image_xysize,
+ sizeof(*histogram_symbols));
+ assert(histogram_bits >= MIN_HUFFMAN_BITS);
+ assert(histogram_bits <= MAX_HUFFMAN_BITS);
+
+ if (histogram_image == NULL || histogram_symbols == NULL) {
+ free(histogram_image);
+ free(histogram_symbols);
+ return 0;
+ }
+
+ // Calculate backward references from ARGB image.
+ if (!VP8LGetBackwardReferences(width, height, argb, quality, cache_bits,
+ use_2d_locality, &refs)) {
+ goto Error;
+ }
+ // Build histogram image and symbols from backward references.
+ if (!VP8LGetHistoImageSymbols(width, height, &refs,
+ quality, histogram_bits, cache_bits,
+ histogram_image,
+ histogram_symbols)) {
+ goto Error;
+ }
+ // Create Huffman bit lengths and codes for each histogram image.
+ histogram_image_size = histogram_image->size;
+ bit_array_size = 5 * histogram_image_size;
+ huffman_codes = (HuffmanTreeCode*)WebPSafeCalloc(bit_array_size,
+ sizeof(*huffman_codes));
+ if (huffman_codes == NULL ||
+ !GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
+ goto Error;
+ }
+ // Free combined histograms.
+ free(histogram_image);
+ histogram_image = NULL;
+
+ // Color Cache parameters.
+ VP8LWriteBits(bw, 1, use_color_cache);
+ if (use_color_cache) {
+ VP8LWriteBits(bw, 4, cache_bits);
+ }
+
+ // Huffman image + meta huffman.
+ {
+ const int write_histogram_image = (histogram_image_size > 1);
+ VP8LWriteBits(bw, 1, write_histogram_image);
+ if (write_histogram_image) {
+ uint32_t* const histogram_argb =
+ (uint32_t*)WebPSafeMalloc((uint64_t)histogram_image_xysize,
+ sizeof(*histogram_argb));
+ int max_index = 0;
+ uint32_t i;
+ if (histogram_argb == NULL) goto Error;
+ for (i = 0; i < histogram_image_xysize; ++i) {
+ const int symbol_index = histogram_symbols[i] & 0xffff;
+ histogram_argb[i] = 0xff000000 | (symbol_index << 8);
+ if (symbol_index >= max_index) {
+ max_index = symbol_index + 1;
+ }
+ }
+ histogram_image_size = max_index;
+
+ VP8LWriteBits(bw, 3, histogram_bits - 2);
+ ok = EncodeImageNoHuffman(bw, histogram_argb,
+ VP8LSubSampleSize(width, histogram_bits),
+ VP8LSubSampleSize(height, histogram_bits),
+ quality);
+ free(histogram_argb);
+ if (!ok) goto Error;
+ }
+ }
+
+ // Store Huffman codes.
+ {
+ int i;
+ for (i = 0; i < 5 * histogram_image_size; ++i) {
+ HuffmanTreeCode* const codes = &huffman_codes[i];
+ if (!StoreHuffmanCode(bw, codes)) goto Error;
+ ClearHuffmanTreeIfOnlyOneSymbol(codes);
+ }
+ }
+
+ // Store actual literals.
+ StoreImageToBitMask(bw, width, histogram_bits, &refs,
+ histogram_symbols, huffman_codes);
+ ok = 1;
+
+ Error:
+ free(histogram_image);
+
+ VP8LClearBackwardRefs(&refs);
+ if (huffman_codes != NULL) {
+ free(huffman_codes->codes);
+ free(huffman_codes);
+ }
+ free(histogram_symbols);
+ return ok;
+}
+
+// -----------------------------------------------------------------------------
+// Transforms
+
+// Check if it would be a good idea to subtract green from red and blue. We
+// only impact entropy in red/blue components, don't bother to look at others.
+static int EvalAndApplySubtractGreen(VP8LEncoder* const enc,
+ int width, int height,
+ VP8LBitWriter* const bw) {
+ if (!enc->use_palette_) {
+ int i;
+ const uint32_t* const argb = enc->argb_;
+ double bit_cost_before, bit_cost_after;
+ VP8LHistogram* const histo = (VP8LHistogram*)malloc(sizeof(*histo));
+ if (histo == NULL) return 0;
+
+ VP8LHistogramInit(histo, 1);
+ for (i = 0; i < width * height; ++i) {
+ const uint32_t c = argb[i];
+ ++histo->red_[(c >> 16) & 0xff];
+ ++histo->blue_[(c >> 0) & 0xff];
+ }
+ bit_cost_before = VP8LHistogramEstimateBits(histo);
+
+ VP8LHistogramInit(histo, 1);
+ for (i = 0; i < width * height; ++i) {
+ const uint32_t c = argb[i];
+ const int green = (c >> 8) & 0xff;
+ ++histo->red_[((c >> 16) - green) & 0xff];
+ ++histo->blue_[((c >> 0) - green) & 0xff];
+ }
+ bit_cost_after = VP8LHistogramEstimateBits(histo);
+ free(histo);
+
+ // Check if subtracting green yields low entropy.
+ enc->use_subtract_green_ = (bit_cost_after < bit_cost_before);
+ if (enc->use_subtract_green_) {
+ VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
+ VP8LWriteBits(bw, 2, SUBTRACT_GREEN);
+ VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height);
+ }
+ }
+ return 1;
+}
+
+static int ApplyPredictFilter(const VP8LEncoder* const enc,
+ int width, int height, int quality,
+ VP8LBitWriter* const bw) {
+ const int pred_bits = enc->transform_bits_;
+ const int transform_width = VP8LSubSampleSize(width, pred_bits);
+ const int transform_height = VP8LSubSampleSize(height, pred_bits);
+
+ VP8LResidualImage(width, height, pred_bits, enc->argb_, enc->argb_scratch_,
+ enc->transform_data_);
+ VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
+ VP8LWriteBits(bw, 2, PREDICTOR_TRANSFORM);
+ assert(pred_bits >= 2);
+ VP8LWriteBits(bw, 3, pred_bits - 2);
+ if (!EncodeImageNoHuffman(bw, enc->transform_data_,
+ transform_width, transform_height, quality)) {
+ return 0;
+ }
+ return 1;
+}
+
+static int ApplyCrossColorFilter(const VP8LEncoder* const enc,
+ int width, int height, int quality,
+ VP8LBitWriter* const bw) {
+ const int ccolor_transform_bits = enc->transform_bits_;
+ const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits);
+ const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits);
+ const int step = (quality < 25) ? 32 : (quality > 50) ? 8 : 16;
+
+ VP8LColorSpaceTransform(width, height, ccolor_transform_bits, step,
+ enc->argb_, enc->transform_data_);
+ VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
+ VP8LWriteBits(bw, 2, CROSS_COLOR_TRANSFORM);
+ assert(ccolor_transform_bits >= 2);
+ VP8LWriteBits(bw, 3, ccolor_transform_bits - 2);
+ if (!EncodeImageNoHuffman(bw, enc->transform_data_,
+ transform_width, transform_height, quality)) {
+ return 0;
+ }
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static WebPEncodingError WriteRiffHeader(const WebPPicture* const pic,
+ size_t riff_size, size_t vp8l_size) {
+ uint8_t riff[RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8L_SIGNATURE_SIZE] = {
+ 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P',
+ 'V', 'P', '8', 'L', 0, 0, 0, 0, VP8L_MAGIC_BYTE,
+ };
+ PutLE32(riff + TAG_SIZE, (uint32_t)riff_size);
+ PutLE32(riff + RIFF_HEADER_SIZE + TAG_SIZE, (uint32_t)vp8l_size);
+ if (!pic->writer(riff, sizeof(riff), pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+ return VP8_ENC_OK;
+}
+
+static int WriteImageSize(const WebPPicture* const pic,
+ VP8LBitWriter* const bw) {
+ const int width = pic->width - 1;
+ const int height = pic->height - 1;
+ assert(width < WEBP_MAX_DIMENSION && height < WEBP_MAX_DIMENSION);
+
+ VP8LWriteBits(bw, VP8L_IMAGE_SIZE_BITS, width);
+ VP8LWriteBits(bw, VP8L_IMAGE_SIZE_BITS, height);
+ return !bw->error_;
+}
+
+static int WriteRealAlphaAndVersion(VP8LBitWriter* const bw, int has_alpha) {
+ VP8LWriteBits(bw, 1, has_alpha);
+ VP8LWriteBits(bw, VP8L_VERSION_BITS, VP8L_VERSION);
+ return !bw->error_;
+}
+
+static WebPEncodingError WriteImage(const WebPPicture* const pic,
+ VP8LBitWriter* const bw,
+ size_t* const coded_size) {
+ WebPEncodingError err = VP8_ENC_OK;
+ const uint8_t* const webpll_data = VP8LBitWriterFinish(bw);
+ const size_t webpll_size = VP8LBitWriterNumBytes(bw);
+ const size_t vp8l_size = VP8L_SIGNATURE_SIZE + webpll_size;
+ const size_t pad = vp8l_size & 1;
+ const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad;
+
+ err = WriteRiffHeader(pic, riff_size, vp8l_size);
+ if (err != VP8_ENC_OK) goto Error;
+
+ if (!pic->writer(webpll_data, webpll_size, pic)) {
+ err = VP8_ENC_ERROR_BAD_WRITE;
+ goto Error;
+ }
+
+ if (pad) {
+ const uint8_t pad_byte[1] = { 0 };
+ if (!pic->writer(pad_byte, 1, pic)) {
+ err = VP8_ENC_ERROR_BAD_WRITE;
+ goto Error;
+ }
+ }
+ *coded_size = CHUNK_HEADER_SIZE + riff_size;
+ return VP8_ENC_OK;
+
+ Error:
+ return err;
+}
+
+// -----------------------------------------------------------------------------
+
+// Allocates the memory for argb (W x H) buffer, 2 rows of context for
+// prediction and transform data.
+static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc,
+ int width, int height) {
+ WebPEncodingError err = VP8_ENC_OK;
+ const int tile_size = 1 << enc->transform_bits_;
+ const uint64_t image_size = width * height;
+ const uint64_t argb_scratch_size = tile_size * width + width;
+ const uint64_t transform_data_size =
+ (uint64_t)VP8LSubSampleSize(width, enc->transform_bits_) *
+ (uint64_t)VP8LSubSampleSize(height, enc->transform_bits_);
+ const uint64_t total_size =
+ image_size + argb_scratch_size + transform_data_size;
+ uint32_t* mem = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*mem));
+ if (mem == NULL) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+ enc->argb_ = mem;
+ mem += image_size;
+ enc->argb_scratch_ = mem;
+ mem += argb_scratch_size;
+ enc->transform_data_ = mem;
+ enc->current_width_ = width;
+
+ Error:
+ return err;
+}
+
+static void ApplyPalette(uint32_t* src, uint32_t* dst,
+ uint32_t src_stride, uint32_t dst_stride,
+ const uint32_t* palette, int palette_size,
+ int width, int height, int xbits, uint8_t* row) {
+ int i, x, y;
+ int use_LUT = 1;
+ for (i = 0; i < palette_size; ++i) {
+ if ((palette[i] & 0xffff00ffu) != 0) {
+ use_LUT = 0;
+ break;
+ }
+ }
+
+ if (use_LUT) {
+ uint8_t inv_palette[MAX_PALETTE_SIZE] = { 0 };
+ for (i = 0; i < palette_size; ++i) {
+ const int color = (palette[i] >> 8) & 0xff;
+ inv_palette[color] = i;
+ }
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ const int color = (src[x] >> 8) & 0xff;
+ row[x] = inv_palette[color];
+ }
+ VP8LBundleColorMap(row, width, xbits, dst);
+ src += src_stride;
+ dst += dst_stride;
+ }
+ } else {
+ // Use 1 pixel cache for ARGB pixels.
+ uint32_t last_pix = palette[0];
+ int last_idx = 0;
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ const uint32_t pix = src[x];
+ if (pix != last_pix) {
+ for (i = 0; i < palette_size; ++i) {
+ if (pix == palette[i]) {
+ last_idx = i;
+ last_pix = pix;
+ break;
+ }
+ }
+ }
+ row[x] = last_idx;
+ }
+ VP8LBundleColorMap(row, width, xbits, dst);
+ src += src_stride;
+ dst += dst_stride;
+ }
+ }
+}
+
+// Note: Expects "enc->palette_" to be set properly.
+// Also, "enc->palette_" will be modified after this call and should not be used
+// later.
+static WebPEncodingError EncodePalette(VP8LBitWriter* const bw,
+ VP8LEncoder* const enc, int quality) {
+ WebPEncodingError err = VP8_ENC_OK;
+ int i;
+ const WebPPicture* const pic = enc->pic_;
+ uint32_t* src = pic->argb;
+ uint32_t* dst;
+ const int width = pic->width;
+ const int height = pic->height;
+ uint32_t* const palette = enc->palette_;
+ const int palette_size = enc->palette_size_;
+ uint8_t* row = NULL;
+ int xbits;
+
+ // Replace each input pixel by corresponding palette index.
+ // This is done line by line.
+ if (palette_size <= 4) {
+ xbits = (palette_size <= 2) ? 3 : 2;
+ } else {
+ xbits = (palette_size <= 16) ? 1 : 0;
+ }
+
+ err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height);
+ if (err != VP8_ENC_OK) goto Error;
+ dst = enc->argb_;
+
+ row = (uint8_t*)WebPSafeMalloc((uint64_t)width, sizeof(*row));
+ if (row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
+
+ ApplyPalette(src, dst, pic->argb_stride, enc->current_width_,
+ palette, palette_size, width, height, xbits, row);
+
+ // Save palette to bitstream.
+ VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
+ VP8LWriteBits(bw, 2, COLOR_INDEXING_TRANSFORM);
+ assert(palette_size >= 1);
+ VP8LWriteBits(bw, 8, palette_size - 1);
+ for (i = palette_size - 1; i >= 1; --i) {
+ palette[i] = VP8LSubPixels(palette[i], palette[i - 1]);
+ }
+ if (!EncodeImageNoHuffman(bw, palette, palette_size, 1, quality)) {
+ err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
+ goto Error;
+ }
+
+ Error:
+ free(row);
+ return err;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetHistoBits(int method, int use_palette, int width, int height) {
+ const uint64_t hist_size = sizeof(VP8LHistogram);
+ // Make tile size a function of encoding method (Range: 0 to 6).
+ int histo_bits = (use_palette ? 9 : 7) - method;
+ while (1) {
+ const uint64_t huff_image_size = VP8LSubSampleSize(width, histo_bits) *
+ VP8LSubSampleSize(height, histo_bits) *
+ hist_size;
+ if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break;
+ ++histo_bits;
+ }
+ return (histo_bits < MIN_HUFFMAN_BITS) ? MIN_HUFFMAN_BITS :
+ (histo_bits > MAX_HUFFMAN_BITS) ? MAX_HUFFMAN_BITS : histo_bits;
+}
+
+static void FinishEncParams(VP8LEncoder* const enc) {
+ const WebPConfig* const config = enc->config_;
+ const WebPPicture* const pic = enc->pic_;
+ const int method = config->method;
+ const float quality = config->quality;
+ const int use_palette = enc->use_palette_;
+ enc->transform_bits_ = (method < 4) ? 5 : (method > 4) ? 3 : 4;
+ enc->histo_bits_ = GetHistoBits(method, use_palette, pic->width, pic->height);
+ enc->cache_bits_ = (quality <= 25.f) ? 0 : 7;
+}
+
+// -----------------------------------------------------------------------------
+// VP8LEncoder
+
+static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config,
+ const WebPPicture* const picture) {
+ VP8LEncoder* const enc = (VP8LEncoder*)calloc(1, sizeof(*enc));
+ if (enc == NULL) {
+ WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ return NULL;
+ }
+ enc->config_ = config;
+ enc->pic_ = picture;
+
+ VP8LDspInit();
+
+ return enc;
+}
+
+static void VP8LEncoderDelete(VP8LEncoder* enc) {
+ free(enc->argb_);
+ free(enc);
+}
+
+// -----------------------------------------------------------------------------
+// Main call
+
+WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
+ const WebPPicture* const picture,
+ VP8LBitWriter* const bw) {
+ WebPEncodingError err = VP8_ENC_OK;
+ const int quality = (int)config->quality;
+ const int width = picture->width;
+ const int height = picture->height;
+ VP8LEncoder* const enc = VP8LEncoderNew(config, picture);
+ const size_t byte_position = VP8LBitWriterNumBytes(bw);
+
+ if (enc == NULL) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ // ---------------------------------------------------------------------------
+ // Analyze image (entropy, num_palettes etc)
+
+ if (!VP8LEncAnalyze(enc, config->image_hint)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ FinishEncParams(enc);
+
+ if (enc->use_palette_) {
+ err = EncodePalette(bw, enc, quality);
+ if (err != VP8_ENC_OK) goto Error;
+ // Color cache is disabled for palette.
+ enc->cache_bits_ = 0;
+ }
+
+ // In case image is not packed.
+ if (enc->argb_ == NULL) {
+ int y;
+ err = AllocateTransformBuffer(enc, width, height);
+ if (err != VP8_ENC_OK) goto Error;
+ for (y = 0; y < height; ++y) {
+ memcpy(enc->argb_ + y * width,
+ picture->argb + y * picture->argb_stride,
+ width * sizeof(*enc->argb_));
+ }
+ enc->current_width_ = width;
+ }
+
+ // ---------------------------------------------------------------------------
+ // Apply transforms and write transform data.
+
+ if (!EvalAndApplySubtractGreen(enc, enc->current_width_, height, bw)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ if (enc->use_predict_) {
+ if (!ApplyPredictFilter(enc, enc->current_width_, height, quality, bw)) {
+ err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
+ goto Error;
+ }
+ }
+
+ if (enc->use_cross_color_) {
+ if (!ApplyCrossColorFilter(enc, enc->current_width_, height, quality, bw)) {
+ err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
+ goto Error;
+ }
+ }
+
+ VP8LWriteBits(bw, 1, !TRANSFORM_PRESENT); // No more transforms.
+
+ // ---------------------------------------------------------------------------
+ // Estimate the color cache size.
+
+ if (enc->cache_bits_ > 0) {
+ if (!VP8LCalculateEstimateForCacheSize(enc->argb_, enc->current_width_,
+ height, &enc->cache_bits_)) {
+ err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
+ goto Error;
+ }
+ }
+
+ // ---------------------------------------------------------------------------
+ // Encode and write the transformed image.
+
+ if (!EncodeImageInternal(bw, enc->argb_, enc->current_width_, height,
+ quality, enc->cache_bits_, enc->histo_bits_)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ if (picture->stats != NULL) {
+ WebPAuxStats* const stats = picture->stats;
+ stats->lossless_features = 0;
+ if (enc->use_predict_) stats->lossless_features |= 1;
+ if (enc->use_cross_color_) stats->lossless_features |= 2;
+ if (enc->use_subtract_green_) stats->lossless_features |= 4;
+ if (enc->use_palette_) stats->lossless_features |= 8;
+ stats->histogram_bits = enc->histo_bits_;
+ stats->transform_bits = enc->transform_bits_;
+ stats->cache_bits = enc->cache_bits_;
+ stats->palette_size = enc->palette_size_;
+ stats->lossless_size = (int)(VP8LBitWriterNumBytes(bw) - byte_position);
+ }
+
+ Error:
+ VP8LEncoderDelete(enc);
+ return err;
+}
+
+int VP8LEncodeImage(const WebPConfig* const config,
+ const WebPPicture* const picture) {
+ int width, height;
+ int has_alpha;
+ size_t coded_size;
+ int percent = 0;
+ WebPEncodingError err = VP8_ENC_OK;
+ VP8LBitWriter bw;
+
+ if (picture == NULL) return 0;
+
+ if (config == NULL || picture->argb == NULL) {
+ err = VP8_ENC_ERROR_NULL_PARAMETER;
+ WebPEncodingSetError(picture, err);
+ return 0;
+ }
+
+ width = picture->width;
+ height = picture->height;
+ if (!VP8LBitWriterInit(&bw, (width * height) >> 1)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ if (!WebPReportProgress(picture, 1, &percent)) {
+ UserAbort:
+ err = VP8_ENC_ERROR_USER_ABORT;
+ goto Error;
+ }
+ // Reset stats (for pure lossless coding)
+ if (picture->stats != NULL) {
+ WebPAuxStats* const stats = picture->stats;
+ memset(stats, 0, sizeof(*stats));
+ stats->PSNR[0] = 99.f;
+ stats->PSNR[1] = 99.f;
+ stats->PSNR[2] = 99.f;
+ stats->PSNR[3] = 99.f;
+ stats->PSNR[4] = 99.f;
+ }
+
+ // Write image size.
+ if (!WriteImageSize(picture, &bw)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ has_alpha = WebPPictureHasTransparency(picture);
+ // Write the non-trivial Alpha flag and lossless version.
+ if (!WriteRealAlphaAndVersion(&bw, has_alpha)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ if (!WebPReportProgress(picture, 5, &percent)) goto UserAbort;
+
+ // Encode main image stream.
+ err = VP8LEncodeStream(config, picture, &bw);
+ if (err != VP8_ENC_OK) goto Error;
+
+ // TODO(skal): have a fine-grained progress report in VP8LEncodeStream().
+ if (!WebPReportProgress(picture, 90, &percent)) goto UserAbort;
+
+ // Finish the RIFF chunk.
+ err = WriteImage(picture, &bw, &coded_size);
+ if (err != VP8_ENC_OK) goto Error;
+
+ if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort;
+
+ // Save size.
+ if (picture->stats != NULL) {
+ picture->stats->coded_size += (int)coded_size;
+ picture->stats->lossless_size = (int)coded_size;
+ }
+
+ if (picture->extra_info != NULL) {
+ const int mb_w = (width + 15) >> 4;
+ const int mb_h = (height + 15) >> 4;
+ memset(picture->extra_info, 0, mb_w * mb_h * sizeof(*picture->extra_info));
+ }
+
+ Error:
+ if (bw.error_) err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ VP8LBitWriterDestroy(&bw);
+ if (err != VP8_ENC_OK) {
+ WebPEncodingSetError(picture, err);
+ return 0;
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+
diff --git a/src/3rdparty/libwebp/src/enc/vp8li.h b/src/3rdparty/libwebp/src/enc/vp8li.h
new file mode 100644
index 0000000..96d6fae
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/vp8li.h
@@ -0,0 +1,70 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Lossless encoder: internal header.
+//
+// Author: Vikas Arora (vikaas.arora@gmail.com)
+
+#ifndef WEBP_ENC_VP8LI_H_
+#define WEBP_ENC_VP8LI_H_
+
+#include "./histogram.h"
+#include "../utils/bit_writer.h"
+#include "../webp/encode.h"
+#include "../webp/format_constants.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ const WebPConfig* config_; // user configuration and parameters
+ const WebPPicture* pic_; // input picture.
+
+ uint32_t* argb_; // Transformed argb image data.
+ uint32_t* argb_scratch_; // Scratch memory for argb rows
+ // (used for prediction).
+ uint32_t* transform_data_; // Scratch memory for transform data.
+ int current_width_; // Corresponds to packed image width.
+
+ // Encoding parameters derived from quality parameter.
+ int histo_bits_;
+ int transform_bits_;
+ int cache_bits_; // If equal to 0, don't use color cache.
+
+ // Encoding parameters derived from image characteristics.
+ int use_cross_color_;
+ int use_subtract_green_;
+ int use_predict_;
+ int use_palette_;
+ int palette_size_;
+ uint32_t palette_[MAX_PALETTE_SIZE];
+} VP8LEncoder;
+
+//------------------------------------------------------------------------------
+// internal functions. Not public.
+
+// Encodes the picture.
+// Returns 0 if config or picture is NULL or picture doesn't have valid argb
+// input.
+int VP8LEncodeImage(const WebPConfig* const config,
+ const WebPPicture* const picture);
+
+// Encodes the main image stream using the supplied bit writer.
+WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
+ const WebPPicture* const picture,
+ VP8LBitWriter* const bw);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_ENC_VP8LI_H_ */
diff --git a/src/3rdparty/libwebp/src/enc/webpenc.c b/src/3rdparty/libwebp/src/enc/webpenc.c
new file mode 100644
index 0000000..207cce6
--- /dev/null
+++ b/src/3rdparty/libwebp/src/enc/webpenc.c
@@ -0,0 +1,404 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebP encoder: main entry point
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "./vp8enci.h"
+#include "./vp8li.h"
+#include "../utils/utils.h"
+
+// #define PRINT_MEMORY_INFO
+
+#ifdef PRINT_MEMORY_INFO
+#include <stdio.h>
+#endif
+
+//------------------------------------------------------------------------------
+
+int WebPGetEncoderVersion(void) {
+ return (ENC_MAJ_VERSION << 16) | (ENC_MIN_VERSION << 8) | ENC_REV_VERSION;
+}
+
+//------------------------------------------------------------------------------
+// WebPPicture
+//------------------------------------------------------------------------------
+
+static int DummyWriter(const uint8_t* data, size_t data_size,
+ const WebPPicture* const picture) {
+ // The following are to prevent 'unused variable' error message.
+ (void)data;
+ (void)data_size;
+ (void)picture;
+ return 1;
+}
+
+int WebPPictureInitInternal(WebPPicture* picture, int version) {
+ if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) {
+ return 0; // caller/system version mismatch!
+ }
+ if (picture != NULL) {
+ memset(picture, 0, sizeof(*picture));
+ picture->writer = DummyWriter;
+ WebPEncodingSetError(picture, VP8_ENC_OK);
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// VP8Encoder
+//------------------------------------------------------------------------------
+
+static void ResetSegmentHeader(VP8Encoder* const enc) {
+ VP8SegmentHeader* const hdr = &enc->segment_hdr_;
+ hdr->num_segments_ = enc->config_->segments;
+ hdr->update_map_ = (hdr->num_segments_ > 1);
+ hdr->size_ = 0;
+}
+
+static void ResetFilterHeader(VP8Encoder* const enc) {
+ VP8FilterHeader* const hdr = &enc->filter_hdr_;
+ hdr->simple_ = 1;
+ hdr->level_ = 0;
+ hdr->sharpness_ = 0;
+ hdr->i4x4_lf_delta_ = 0;
+}
+
+static void ResetBoundaryPredictions(VP8Encoder* const enc) {
+ // init boundary values once for all
+ // Note: actually, initializing the preds_[] is only needed for intra4.
+ int i;
+ uint8_t* const top = enc->preds_ - enc->preds_w_;
+ uint8_t* const left = enc->preds_ - 1;
+ for (i = -1; i < 4 * enc->mb_w_; ++i) {
+ top[i] = B_DC_PRED;
+ }
+ for (i = 0; i < 4 * enc->mb_h_; ++i) {
+ left[i * enc->preds_w_] = B_DC_PRED;
+ }
+ enc->nz_[-1] = 0; // constant
+}
+
+// Mapping from config->method_ to coding tools used.
+//-------------------+---+---+---+---+---+---+---+
+// Method | 0 | 1 | 2 | 3 |(4)| 5 | 6 |
+//-------------------+---+---+---+---+---+---+---+
+// fast probe | x | | | x | | | |
+//-------------------+---+---+---+---+---+---+---+
+// dynamic proba | ~ | x | x | x | x | x | x |
+//-------------------+---+---+---+---+---+---+---+
+// fast mode analysis| | | | | x | x | x |
+//-------------------+---+---+---+---+---+---+---+
+// basic rd-opt | | | | x | x | x | x |
+//-------------------+---+---+---+---+---+---+---+
+// disto-score i4/16 | | | x | | | | |
+//-------------------+---+---+---+---+---+---+---+
+// rd-opt i4/16 | | | ~ | x | x | x | x |
+//-------------------+---+---+---+---+---+---+---+
+// token buffer (opt)| | | | x | x | x | x |
+//-------------------+---+---+---+---+---+---+---+
+// Trellis | | | | | | x |Ful|
+//-------------------+---+---+---+---+---+---+---+
+// full-SNS | | | | | x | x | x |
+//-------------------+---+---+---+---+---+---+---+
+
+static void MapConfigToTools(VP8Encoder* const enc) {
+ const WebPConfig* const config = enc->config_;
+ const int method = config->method;
+ const int limit = 100 - config->partition_limit;
+ enc->method_ = method;
+ enc->rd_opt_level_ = (method >= 6) ? RD_OPT_TRELLIS_ALL
+ : (method >= 5) ? RD_OPT_TRELLIS
+ : (method >= 3) ? RD_OPT_BASIC
+ : RD_OPT_NONE;
+ enc->max_i4_header_bits_ =
+ 256 * 16 * 16 * // upper bound: up to 16bit per 4x4 block
+ (limit * limit) / (100 * 100); // ... modulated with a quadratic curve.
+
+ enc->thread_level_ = config->thread_level;
+
+ enc->do_search_ = (config->target_size > 0 || config->target_PSNR > 0);
+ if (!config->low_memory) {
+#if !defined(DISABLE_TOKEN_BUFFER)
+ enc->use_tokens_ = (enc->rd_opt_level_ >= RD_OPT_BASIC); // need rd stats
+#endif
+ if (enc->use_tokens_) {
+ enc->num_parts_ = 1; // doesn't work with multi-partition
+ }
+ }
+}
+
+// Memory scaling with dimensions:
+// memory (bytes) ~= 2.25 * w + 0.0625 * w * h
+//
+// Typical memory footprint (768x510 picture)
+// Memory used:
+// encoder: 33919
+// block cache: 2880
+// info: 3072
+// preds: 24897
+// top samples: 1623
+// non-zero: 196
+// lf-stats: 2048
+// total: 68635
+// Transient object sizes:
+// VP8EncIterator: 352
+// VP8ModeScore: 912
+// VP8SegmentInfo: 532
+// VP8Proba: 31032
+// LFStats: 2048
+// Picture size (yuv): 589824
+
+static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
+ WebPPicture* const picture) {
+ const int use_filter =
+ (config->filter_strength > 0) || (config->autofilter > 0);
+ const int mb_w = (picture->width + 15) >> 4;
+ const int mb_h = (picture->height + 15) >> 4;
+ const int preds_w = 4 * mb_w + 1;
+ const int preds_h = 4 * mb_h + 1;
+ const size_t preds_size = preds_w * preds_h * sizeof(uint8_t);
+ const int top_stride = mb_w * 16;
+ const size_t nz_size = (mb_w + 1) * sizeof(uint32_t) + ALIGN_CST;
+ const size_t info_size = mb_w * mb_h * sizeof(VP8MBInfo);
+ const size_t samples_size = 2 * top_stride * sizeof(uint8_t) // top-luma/u/v
+ + ALIGN_CST; // align all
+ const size_t lf_stats_size =
+ config->autofilter ? sizeof(LFStats) + ALIGN_CST : 0;
+ VP8Encoder* enc;
+ uint8_t* mem;
+ const uint64_t size = (uint64_t)sizeof(VP8Encoder) // main struct
+ + ALIGN_CST // cache alignment
+ + info_size // modes info
+ + preds_size // prediction modes
+ + samples_size // top/left samples
+ + nz_size // coeff context bits
+ + lf_stats_size; // autofilter stats
+
+#ifdef PRINT_MEMORY_INFO
+ printf("===================================\n");
+ printf("Memory used:\n"
+ " encoder: %ld\n"
+ " info: %ld\n"
+ " preds: %ld\n"
+ " top samples: %ld\n"
+ " non-zero: %ld\n"
+ " lf-stats: %ld\n"
+ " total: %ld\n",
+ sizeof(VP8Encoder) + ALIGN_CST, info_size,
+ preds_size, samples_size, nz_size, lf_stats_size, size);
+ printf("Transient object sizes:\n"
+ " VP8EncIterator: %ld\n"
+ " VP8ModeScore: %ld\n"
+ " VP8SegmentInfo: %ld\n"
+ " VP8Proba: %ld\n"
+ " LFStats: %ld\n",
+ sizeof(VP8EncIterator), sizeof(VP8ModeScore),
+ sizeof(VP8SegmentInfo), sizeof(VP8Proba),
+ sizeof(LFStats));
+ printf("Picture size (yuv): %ld\n",
+ mb_w * mb_h * 384 * sizeof(uint8_t));
+ printf("===================================\n");
+#endif
+ mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem));
+ if (mem == NULL) {
+ WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ return NULL;
+ }
+ enc = (VP8Encoder*)mem;
+ mem = (uint8_t*)DO_ALIGN(mem + sizeof(*enc));
+ memset(enc, 0, sizeof(*enc));
+ enc->num_parts_ = 1 << config->partitions;
+ enc->mb_w_ = mb_w;
+ enc->mb_h_ = mb_h;
+ enc->preds_w_ = preds_w;
+ enc->mb_info_ = (VP8MBInfo*)mem;
+ mem += info_size;
+ enc->preds_ = ((uint8_t*)mem) + 1 + enc->preds_w_;
+ mem += preds_w * preds_h * sizeof(uint8_t);
+ enc->nz_ = 1 + (uint32_t*)DO_ALIGN(mem);
+ mem += nz_size;
+ enc->lf_stats_ = lf_stats_size ? (LFStats*)DO_ALIGN(mem) : NULL;
+ mem += lf_stats_size;
+
+ // top samples (all 16-aligned)
+ mem = (uint8_t*)DO_ALIGN(mem);
+ enc->y_top_ = (uint8_t*)mem;
+ enc->uv_top_ = enc->y_top_ + top_stride;
+ mem += 2 * top_stride;
+ assert(mem <= (uint8_t*)enc + size);
+
+ enc->config_ = config;
+ enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2;
+ enc->pic_ = picture;
+ enc->percent_ = 0;
+
+ MapConfigToTools(enc);
+ VP8EncDspInit();
+ VP8DefaultProbas(enc);
+ ResetSegmentHeader(enc);
+ ResetFilterHeader(enc);
+ ResetBoundaryPredictions(enc);
+
+ VP8EncInitAlpha(enc);
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+ VP8EncInitLayer(enc);
+#endif
+
+ VP8TBufferInit(&enc->tokens_);
+ return enc;
+}
+
+static int DeleteVP8Encoder(VP8Encoder* enc) {
+ int ok = 1;
+ if (enc != NULL) {
+ ok = VP8EncDeleteAlpha(enc);
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+ VP8EncDeleteLayer(enc);
+#endif
+ VP8TBufferClear(&enc->tokens_);
+ free(enc);
+ }
+ return ok;
+}
+
+//------------------------------------------------------------------------------
+
+static double GetPSNR(uint64_t err, uint64_t size) {
+ return (err > 0 && size > 0) ? 10. * log10(255. * 255. * size / err) : 99.;
+}
+
+static void FinalizePSNR(const VP8Encoder* const enc) {
+ WebPAuxStats* stats = enc->pic_->stats;
+ const uint64_t size = enc->sse_count_;
+ const uint64_t* const sse = enc->sse_;
+ stats->PSNR[0] = (float)GetPSNR(sse[0], size);
+ stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4);
+ stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4);
+ stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2);
+ stats->PSNR[4] = (float)GetPSNR(sse[3], size);
+}
+
+static void StoreStats(VP8Encoder* const enc) {
+ WebPAuxStats* const stats = enc->pic_->stats;
+ if (stats != NULL) {
+ int i, s;
+ for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
+ stats->segment_level[i] = enc->dqm_[i].fstrength_;
+ stats->segment_quant[i] = enc->dqm_[i].quant_;
+ for (s = 0; s <= 2; ++s) {
+ stats->residual_bytes[s][i] = enc->residual_bytes_[s][i];
+ }
+ }
+ FinalizePSNR(enc);
+ stats->coded_size = enc->coded_size_;
+ for (i = 0; i < 3; ++i) {
+ stats->block_count[i] = enc->block_count_[i];
+ }
+ }
+ WebPReportProgress(enc->pic_, 100, &enc->percent_); // done!
+}
+
+int WebPEncodingSetError(const WebPPicture* const pic,
+ WebPEncodingError error) {
+ assert((int)error < VP8_ENC_ERROR_LAST);
+ assert((int)error >= VP8_ENC_OK);
+ ((WebPPicture*)pic)->error_code = error;
+ return 0;
+}
+
+int WebPReportProgress(const WebPPicture* const pic,
+ int percent, int* const percent_store) {
+ if (percent_store != NULL && percent != *percent_store) {
+ *percent_store = percent;
+ if (pic->progress_hook && !pic->progress_hook(percent, pic)) {
+ // user abort requested
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);
+ return 0;
+ }
+ }
+ return 1; // ok
+}
+//------------------------------------------------------------------------------
+
+int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
+ int ok = 0;
+
+ if (pic == NULL)
+ return 0;
+ WebPEncodingSetError(pic, VP8_ENC_OK); // all ok so far
+ if (config == NULL) // bad params
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
+ if (!WebPValidateConfig(config))
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
+ if (pic->width <= 0 || pic->height <= 0)
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
+ if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION)
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
+
+ if (pic->stats != NULL) memset(pic->stats, 0, sizeof(*pic->stats));
+
+ if (!config->lossless) {
+ VP8Encoder* enc = NULL;
+ if (pic->y == NULL || pic->u == NULL || pic->v == NULL) {
+ // Make sure we have YUVA samples.
+ float dithering = 0.f;
+ if (config->preprocessing & 2) {
+ const float x = config->quality / 100.f;
+ const float x2 = x * x;
+ // slowly decreasing from max dithering at low quality (q->0)
+ // to 0.5 dithering amplitude at high quality (q->100)
+ dithering = 1.0f + (0.5f - 1.0f) * x2 * x2;
+ }
+ if (!WebPPictureARGBToYUVADithered(pic, WEBP_YUV420, dithering)) {
+ return 0;
+ }
+ }
+
+ enc = InitVP8Encoder(config, pic);
+ if (enc == NULL) return 0; // pic->error is already set.
+ // Note: each of the tasks below account for 20% in the progress report.
+ ok = VP8EncAnalyze(enc);
+
+ // Analysis is done, proceed to actual coding.
+ ok = ok && VP8EncStartAlpha(enc); // possibly done in parallel
+ if (!enc->use_tokens_) {
+ ok = ok && VP8EncLoop(enc);
+ } else {
+ ok = ok && VP8EncTokenLoop(enc);
+ }
+ ok = ok && VP8EncFinishAlpha(enc);
+#ifdef WEBP_EXPERIMENTAL_FEATURES
+ ok = ok && VP8EncFinishLayer(enc);
+#endif
+
+ ok = ok && VP8EncWrite(enc);
+ StoreStats(enc);
+ if (!ok) {
+ VP8EncFreeBitWriters(enc);
+ }
+ ok &= DeleteVP8Encoder(enc); // must always be called, even if !ok
+ } else {
+ // Make sure we have ARGB samples.
+ if (pic->argb == NULL && !WebPPictureYUVAToARGB(pic)) {
+ return 0;
+ }
+
+ ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem.
+ }
+
+ return ok;
+}
+