summaryrefslogtreecommitdiffstats
path: root/third_party
diff options
context:
space:
mode:
authorabarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-15 07:46:12 +0000
committerabarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-15 07:46:12 +0000
commit0e53ac13ca3985a26ed8e49e59a8e8e384474bf5 (patch)
tree382606299435d6742334304e195a471c661585bb /third_party
parent55d27ae96bccb1da93ee693642d2de58f37f9a72 (diff)
downloadchromium_src-0e53ac13ca3985a26ed8e49e59a8e8e384474bf5.zip
chromium_src-0e53ac13ca3985a26ed8e49e59a8e8e384474bf5.tar.gz
chromium_src-0e53ac13ca3985a26ed8e49e59a8e8e384474bf5.tar.bz2
Update libwebp
Patch originally by Pascal Massimino. Review URL: http://codereview.chromium.org/6677038 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@78173 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party')
-rw-r--r--third_party/libwebp/README.chromium7
-rw-r--r--third_party/libwebp/dec/bits.c (renamed from third_party/libwebp/bits.c)19
-rw-r--r--third_party/libwebp/dec/bits.h (renamed from third_party/libwebp/bits.h)57
-rw-r--r--third_party/libwebp/dec/dsp.c (renamed from third_party/libwebp/dsp.c)6
-rw-r--r--third_party/libwebp/dec/frame.c (renamed from third_party/libwebp/frame.c)15
-rw-r--r--third_party/libwebp/dec/quant.c (renamed from third_party/libwebp/quant.c)0
-rw-r--r--third_party/libwebp/dec/tree.c (renamed from third_party/libwebp/tree.c)2
-rw-r--r--third_party/libwebp/dec/vp8.c (renamed from third_party/libwebp/vp8.c)281
-rw-r--r--third_party/libwebp/dec/vp8i.h (renamed from third_party/libwebp/vp8i.h)26
-rw-r--r--third_party/libwebp/dec/webp.c (renamed from third_party/libwebp/webp.c)207
-rw-r--r--third_party/libwebp/dec/webpi.h63
-rw-r--r--third_party/libwebp/dec/yuv.c (renamed from third_party/libwebp/yuv.c)0
-rw-r--r--third_party/libwebp/dec/yuv.h (renamed from third_party/libwebp/yuv.h)8
-rw-r--r--third_party/libwebp/enc/analysis.c399
-rw-r--r--third_party/libwebp/enc/bit_writer.c175
-rw-r--r--third_party/libwebp/enc/bit_writer.h61
-rw-r--r--third_party/libwebp/enc/config.c115
-rw-r--r--third_party/libwebp/enc/cost.c491
-rw-r--r--third_party/libwebp/enc/cost.h52
-rw-r--r--third_party/libwebp/enc/dsp.c614
-rw-r--r--third_party/libwebp/enc/filter.c378
-rw-r--r--third_party/libwebp/enc/frame.c695
-rw-r--r--third_party/libwebp/enc/iterator.c406
-rw-r--r--third_party/libwebp/enc/picture.c316
-rw-r--r--third_party/libwebp/enc/quant.c956
-rw-r--r--third_party/libwebp/enc/syntax.c237
-rw-r--r--third_party/libwebp/enc/tree.c507
-rw-r--r--third_party/libwebp/enc/vp8enci.h445
-rw-r--r--third_party/libwebp/enc/webpenc.c307
-rw-r--r--third_party/libwebp/libwebp.gyp49
-rw-r--r--third_party/libwebp/webp/decode.h20
-rw-r--r--third_party/libwebp/webp/decode_vp8.h50
-rw-r--r--third_party/libwebp/webp/encode.h216
-rw-r--r--third_party/libwebp/webp/types.h29
34 files changed, 6907 insertions, 302 deletions
diff --git a/third_party/libwebp/README.chromium b/third_party/libwebp/README.chromium
index 7ef5f1d..7677d78 100644
--- a/third_party/libwebp/README.chromium
+++ b/third_party/libwebp/README.chromium
@@ -3,11 +3,10 @@ URL: http://code.google.com/speed/webp
Here is a copy of libwebp-decode, from the repository:
git://review.webmproject.org/libwebp.git
-This code was pulled from MASTER on Dec 20th 2010, with the most recent
-Change-Id: I943a335b92b5ee6c2980c2ba9d4092f0b79f9a6b, "handle corner case of
-zero-dimensions".
-
+This code was pulled from MASTER on March 13th 2011, with the most recent
+Change-Id: I68194cb510760dd82a2ae83359154bf622502991 "fix bigger-by-1 array"
The project files do not include from the distribution:
examples/
+Changed the include path from webp/ to ../webp/ \ No newline at end of file
diff --git a/third_party/libwebp/bits.c b/third_party/libwebp/dec/bits.c
index a51775d..da3b777 100644
--- a/third_party/libwebp/bits.c
+++ b/third_party/libwebp/dec/bits.c
@@ -18,16 +18,17 @@ extern "C" {
//-----------------------------------------------------------------------------
// VP8BitReader
-void VP8Init(VP8BitReader* const br, const uint8_t* buf, uint32_t size) {
+void VP8InitBitReader(VP8BitReader* const br,
+ const uint8_t* const start, const uint8_t* const end) {
assert(br);
- assert(buf);
- br->range_ = 255 - 1;
- br->eof_ = 0;
- br->buf_ = buf;
- br->buf_end_ = buf + size;
- // Need two initial bytes.
- br->value_ = (VP8GetByte(br) << 8) | VP8GetByte(br);
- br->left_ = -8;
+ assert(start);
+ assert(start <= end);
+ br->range_ = 255 - 1;
+ br->buf_ = start;
+ br->buf_end_ = end;
+ br->value_ = 0;
+ br->missing_ = 8;
+ br->eof_ = 0;
}
const uint8_t kVP8Log2Range[128] = {
diff --git a/third_party/libwebp/bits.h b/third_party/libwebp/dec/bits.h
index f19530f..e0572e4 100644
--- a/third_party/libwebp/bits.h
+++ b/third_party/libwebp/dec/bits.h
@@ -9,11 +9,11 @@
//
// Author: Skal (pascal.massimino@gmail.com)
-#ifndef WEBP_DECODE_BITS_H_
-#define WEBP_DECODE_BITS_H_
+#ifndef WEBP_DEC_BITS_H_
+#define WEBP_DEC_BITS_H_
#include <assert.h>
-#include "webp/decode_vp8.h"
+#include "../webp/decode_vp8.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
@@ -30,11 +30,12 @@ typedef struct {
// boolean decoder
uint32_t range_; // current range minus 1. In [127, 254] interval.
uint32_t value_; // current value
- int left_; // how many unused bits (negated)
+ int missing_; // number of missing bits in value_ (8bit)
} VP8BitReader;
// Initialize the bit reader and the boolean decoder.
-void VP8Init(VP8BitReader* const br, const uint8_t* buf, uint32_t size);
+void VP8InitBitReader(VP8BitReader* const br,
+ const uint8_t* const start, const uint8_t* const end);
// return the next value made of 'num_bits' bits
uint32_t VP8GetValue(VP8BitReader* const br, int num_bits);
@@ -55,7 +56,24 @@ static inline uint32_t VP8GetByte(VP8BitReader* const br) {
return *br->buf_++;
}
br->eof_ = 1;
- return 0x80;
+ return 0xff;
+}
+
+static inline uint32_t VP8BitUpdate(VP8BitReader* const br, uint32_t split) {
+ uint32_t bit;
+ // Make sure we have a least 8 bits in 'value_'
+ if (br->missing_ > 0) {
+ br->value_ |= VP8GetByte(br) << br->missing_;
+ br->missing_ -= 8;
+ }
+ bit = ((br->value_ >> 8) > split);
+ if (bit) {
+ br->range_ -= split + 1;
+ br->value_ -= (split + 1) << 8;
+ } else {
+ br->range_ = split;
+ }
+ return bit;
}
static inline void VP8Shift(VP8BitReader* const br) {
@@ -63,22 +81,12 @@ static inline void VP8Shift(VP8BitReader* const br) {
const int shift = kVP8Log2Range[br->range_];
br->range_ = kVP8NewRange[br->range_];
br->value_ <<= shift;
- br->left_ += shift;
- if (br->left_ > 0) {
- br->value_ |= VP8GetByte(br) << br->left_;
- br->left_ -= 8;
- }
+ br->missing_ += shift;
}
static inline uint32_t VP8GetBit(VP8BitReader* const br, int prob) {
const uint32_t split = (br->range_ * prob) >> 8;
- const uint32_t bit = ((br->value_ >> 8) > split);
- if (bit) {
- br->range_ -= split + 1;
- br->value_ -= (split + 1) << 8;
- } else {
- br->range_ = split;
- }
+ const uint32_t bit = VP8BitUpdate(br, split);
if (br->range_ < 0x7f) {
VP8Shift(br);
}
@@ -87,20 +95,13 @@ static inline uint32_t VP8GetBit(VP8BitReader* const br, int prob) {
static inline int VP8GetSigned(VP8BitReader* const br, int v) {
const uint32_t split = br->range_ >> 1;
- const uint32_t bit = ((br->value_ >> 8) > split);
- if (bit) {
- br->range_ -= split + 1;
- br->value_ -= (split + 1) << 8;
- v = -v;
- } else {
- br->range_ = split;
- }
+ const uint32_t bit = VP8BitUpdate(br, split);
VP8Shift(br);
- return v;
+ return bit ? -v : v;
}
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif
-#endif // WEBP_DECODE_BITS_H_
+#endif // WEBP_DEC_BITS_H_
diff --git a/third_party/libwebp/dsp.c b/third_party/libwebp/dec/dsp.c
index a64a9be..2737cb4 100644
--- a/third_party/libwebp/dsp.c
+++ b/third_party/libwebp/dec/dsp.c
@@ -468,16 +468,16 @@ static void DC8uvNoTopLeft(uint8_t *dst) { // DC with nothing
//-----------------------------------------------------------------------------
// default C implementations
-VP8PredFunc VP8PredLuma4[11] = {
+VP8PredFunc VP8PredLuma4[NUM_BMODES] = {
DC4, TM4, VE4, HE4, RD4, VR4, LD4, VL4, HD4, HU4
};
-VP8PredFunc VP8PredLuma16[7] = {
+VP8PredFunc VP8PredLuma16[NUM_B_DC_MODES] = {
DC16, TM16, VE16, HE16,
DC16NoTop, DC16NoLeft, DC16NoTopLeft
};
-VP8PredFunc VP8PredChroma8[7] = {
+VP8PredFunc VP8PredChroma8[NUM_B_DC_MODES] = {
DC8uv, TM8uv, VE8uv, HE8uv,
DC8uvNoTop, DC8uvNoLeft, DC8uvNoTopLeft
};
diff --git a/third_party/libwebp/frame.c b/third_party/libwebp/dec/frame.c
index d934412..44c6357 100644
--- a/third_party/libwebp/frame.c
+++ b/third_party/libwebp/dec/frame.c
@@ -44,7 +44,8 @@ int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) {
dec->mem_size_ = 0;
dec->mem_ = (uint8_t*)malloc(needed);
if (dec->mem_ == NULL) {
- return VP8SetError(dec, 1, "no memory during frame initialization.");
+ return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
+ "no memory during frame initialization.");
}
dec->mem_size_ = needed;
}
@@ -125,6 +126,9 @@ static void DoFilter(VP8Decoder* const dec, int mb_x, int mb_y) {
const int level = mb->f_level_;
const int ilevel = mb->f_ilevel_;
const int limit = 2 * level + ilevel;
+ if (level == 0) {
+ return;
+ }
if (dec->filter_type_ == 1) { // simple
if (mb_x > 0) {
VP8SimpleHFilter16(y_dst, y_bps, limit + 4);
@@ -210,7 +214,7 @@ void VP8StoreBlock(VP8Decoder* const dec) {
}
}
-void VP8FinishRow(VP8Decoder* const dec, VP8Io* io) {
+int VP8FinishRow(VP8Decoder* const dec, VP8Io* io) {
const int extra_y_rows = kFilterExtraRows[dec->filter_type_];
const int ysize = extra_y_rows * dec->cache_y_stride_;
const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_;
@@ -246,7 +250,9 @@ void VP8FinishRow(VP8Decoder* const dec, VP8Io* io) {
}
io->mb_y = y_start;
io->mb_h = y_end - y_start;
- io->put(io);
+ if (!io->put(io)) {
+ return 0;
+ }
}
// rotate top samples
if (!last_row) {
@@ -254,6 +260,7 @@ void VP8FinishRow(VP8Decoder* const dec, VP8Io* io) {
memcpy(udst, udst + 8 * dec->cache_uv_stride_, uvsize);
memcpy(vdst, vdst + 8 * dec->cache_uv_stride_, uvsize);
}
+ return 1;
}
//-----------------------------------------------------------------------------
@@ -350,7 +357,7 @@ void VP8ReconstructBlock(VP8Decoder* const dec) {
for (n = 0; n < 16; n++) {
uint8_t* const dst = y_dst + kScan[n];
VP8PredLuma4[dec->imodes_[n]](dst);
- if (dec->non_zero_ & (1 << n)) {
+ if (dec->non_zero_ac_ & (1 << n)) {
VP8Transform(coeffs + n * 16, dst);
} else if (dec->non_zero_ & (1 << n)) { // only DC is present
VP8TransformDC(coeffs + n * 16, dst);
diff --git a/third_party/libwebp/quant.c b/third_party/libwebp/dec/quant.c
index 47edbf5..47edbf5 100644
--- a/third_party/libwebp/quant.c
+++ b/third_party/libwebp/dec/quant.c
diff --git a/third_party/libwebp/tree.c b/third_party/libwebp/dec/tree.c
index 03cb745..9c7074b 100644
--- a/third_party/libwebp/tree.c
+++ b/third_party/libwebp/dec/tree.c
@@ -331,7 +331,7 @@ void VP8ResetProba(VP8Proba* const proba) {
#ifndef ONLY_KEYFRAME_CODE
memcpy(proba->mv_, kMVProba0, sizeof(kMVProba0));
memcpy(proba->ymode_, kYModeProbaInter0, sizeof(kYModeProbaInter0));
- memcpy(proba->uvmode_, kUVModeProbaInter0, sizeof(kYModeProbaInter0));
+ memcpy(proba->uvmode_, kUVModeProbaInter0, sizeof(kUVModeProbaInter0));
#endif
}
diff --git a/third_party/libwebp/vp8.c b/third_party/libwebp/dec/vp8.c
index caae7d9..43319b5 100644
--- a/third_party/libwebp/vp8.c
+++ b/third_party/libwebp/dec/vp8.c
@@ -12,18 +12,25 @@
#include <stdlib.h>
#include "vp8i.h"
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
//-----------------------------------------------------------------------------
// VP8Decoder
static void SetOk(VP8Decoder* const dec) {
- dec->status_ = 0;
+ dec->status_ = VP8_STATUS_OK;
dec->error_msg_ = "OK";
}
-void VP8InitIo(VP8Io* const io) {
+int VP8InitIoInternal(VP8Io* const io, int version) {
+ if (version != WEBP_DECODER_ABI_VERSION)
+ return 0; // mismatch error
if (io) {
memset(io, 0, sizeof(*io));
}
+ return 1;
}
VP8Decoder* VP8New() {
@@ -35,8 +42,8 @@ VP8Decoder* VP8New() {
return dec;
}
-int VP8Status(VP8Decoder* const dec) {
- if (!dec) return 2;
+VP8StatusCode VP8Status(VP8Decoder* const dec) {
+ if (!dec) return VP8_STATUS_INVALID_PARAM;
return dec->status_;
}
@@ -53,7 +60,8 @@ void VP8Delete(VP8Decoder* const dec) {
}
}
-int VP8SetError(VP8Decoder* const dec, int error, const char *msg) {
+int VP8SetError(VP8Decoder* const dec,
+ VP8StatusCode error, const char * const msg) {
dec->status_ = error;
dec->error_msg_ = msg;
dec->ready_ = 0;
@@ -99,37 +107,45 @@ static int ParseSegmentHeader(VP8BitReader* br,
} else {
hdr->update_map_ = 0;
}
- return 1;
+ return !br->eof_;
}
// Paragraph 9.5
-static int ParsePartitions(VP8Decoder* const dec,
- const uint8_t* buf, uint32_t size) {
+// This function returns VP8_STATUS_SUSPENDED if we don't have all the
+// necessary data in 'buf'.
+// This case is not necessarily an error (for incremental decoding).
+// Still, no bitreader is ever initialized to make it possible to read
+// unavailable memory.
+// If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA
+// is returned, and this is an unrecoverable error.
+// If the partitions were positioned ok, VP8_STATUS_OK is returned.
+static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
+ const uint8_t* buf, uint32_t size) {
VP8BitReader* const br = &dec->br_;
const uint8_t* sz = buf;
+ const uint8_t* buf_end = buf + size;
+ const uint8_t* part_start;
int last_part;
- uint32_t offset;
int p;
dec->num_parts_ = 1 << VP8GetValue(br, 2);
last_part = dec->num_parts_ - 1;
- offset = last_part * 3;
-
- if (size <= offset) {
- return 0;
+ part_start = buf + last_part * 3;
+ if (buf_end < part_start) {
+ // we can't even read the sizes with sz[]! That's a failure.
+ return VP8_STATUS_NOT_ENOUGH_DATA;
}
for (p = 0; p < last_part; ++p) {
const uint32_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
- if (offset + psize > size) {
- return 0;
- }
- VP8Init(dec->parts_ + p, buf + offset, psize);
- offset += psize;
+ const uint8_t* part_end = part_start + psize;
+ if (part_end > buf_end) part_end = buf_end;
+ VP8InitBitReader(dec->parts_ + p, part_start, part_end);
+ part_start = part_end;
sz += 3;
}
- size -= offset;
- VP8Init(dec->parts_ + last_part, buf + offset, size);
- return 1;
+ VP8InitBitReader(dec->parts_ + last_part, part_start, buf_end);
+ return (part_start < buf_end) ? VP8_STATUS_OK :
+ VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data
}
// Paragraph 9.4
@@ -169,7 +185,7 @@ static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) {
dec->filter_levels_[0] = hdr->level_;
}
}
- return 1;
+ return !br->eof_;
}
static inline uint32_t get_le32(const uint8_t* const data) {
@@ -189,13 +205,15 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
}
SetOk(dec);
if (io == NULL) {
- return VP8SetError(dec, 2, "null VP8Io passed to VP8GetHeaders()");
+ return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
+ "null VP8Io passed to VP8GetHeaders()");
}
buf = (uint8_t *)io->data;
buf_size = io->data_size;
if (buf == NULL || buf_size <= 4) {
- return VP8SetError(dec, 2, "Not enough data to parse frame header");
+ return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
+ "Not enough data to parse frame header");
}
// Skip over valid RIFF headers
@@ -203,18 +221,26 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
uint32_t riff_size;
uint32_t chunk_size;
if (buf_size < 20 + 4) {
- return VP8SetError(dec, 2, "RIFF: Truncated header.");
+ return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
+ "RIFF: Truncated header.");
}
if (memcmp(buf + 8, "WEBP", 4)) { // wrong image file signature
- return VP8SetError(dec, 2, "RIFF: WEBP signature not found.");
+ return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
+ "RIFF: WEBP signature not found.");
}
riff_size = get_le32(buf + 4);
+ if (riff_size < 12) {
+ return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
+ "RIFF: Truncated header.");
+ }
if (memcmp(buf + 12, "VP8 ", 4)) {
- return VP8SetError(dec, 2, "RIFF: Invalid compression format.");
+ return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
+ "RIFF: Invalid compression format.");
}
chunk_size = get_le32(buf + 16);
- if ((chunk_size > riff_size + 8) || (chunk_size & 1)) {
- return VP8SetError(dec, 2, "RIFF: Inconsistent size information.");
+ if (chunk_size > riff_size - 12) {
+ return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
+ "RIFF: Inconsistent size information.");
}
buf += 20;
buf_size -= 20;
@@ -228,6 +254,12 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
frm_hdr->profile_ = (bits >> 1) & 7;
frm_hdr->show_ = (bits >> 4) & 1;
frm_hdr->partition_length_ = (bits >> 5);
+ if (frm_hdr->profile_ > 3)
+ return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
+ "Incorrect keyframe parameters.");
+ if (!frm_hdr->show_)
+ return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
+ "Frame not displayable.");
buf += 3;
buf_size -= 3;
}
@@ -236,10 +268,12 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
if (frm_hdr->key_frame_) {
// Paragraph 9.2
if (buf_size < 7) {
- return VP8SetError(dec, 2, "cannot parse picture header");
+ return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
+ "cannot parse picture header");
}
if (buf[0] != 0x9d || buf[1] != 0x01 || buf[2] != 0x2a) {
- return VP8SetError(dec, 2, "Bad code word");
+ return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
+ "Bad code word");
}
pic_hdr->width_ = ((buf[4] << 8) | buf[3]) & 0x3fff;
pic_hdr->xscale_ = buf[4] >> 6; // ratio: 1, 5/4 5/3 or 2
@@ -258,26 +292,33 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
dec->segment_ = 0; // default for intra
}
- br = &dec->br_;
- VP8Init(br, buf, buf_size);
+ // Check if we have all the partition #0 available, and initialize dec->br_
+ // to read this partition (and this partition only).
if (frm_hdr->partition_length_ > buf_size) {
- return VP8SetError(dec, 2, "bad partition length");
+ return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
+ "bad partition length");
}
+ br = &dec->br_;
+ VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_);
buf += frm_hdr->partition_length_;
buf_size -= frm_hdr->partition_length_;
+
if (frm_hdr->key_frame_) {
pic_hdr->colorspace_ = VP8Get(br);
pic_hdr->clamp_type_ = VP8Get(br);
}
if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) {
- return VP8SetError(dec, 2, "cannot parse segment header");
+ return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
+ "cannot parse segment header");
}
// Filter specs
if (!ParseFilterHeader(br, dec)) {
- return VP8SetError(dec, 2, "cannot parse filter header");
+ return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
+ "cannot parse filter header");
}
- if (!ParsePartitions(dec, buf, buf_size)) {
- return VP8SetError(dec, 2, "cannot parse partitions");
+ if (ParsePartitions(dec, buf, buf_size) != VP8_STATUS_OK) {
+ return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
+ "cannot parse partitions");
}
// quantizer change
@@ -298,7 +339,8 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
dec->buffer_flags_ |= VP8Get(br) << 6; // sign bias golden
dec->buffer_flags_ |= VP8Get(br) << 7; // sign bias alt ref
#else
- return VP8SetError(dec, 2, "Not a key frame.");
+ return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
+ "Not a key frame.");
#endif
} else {
dec->buffer_flags_ = 0x003 | 0x100;
@@ -401,12 +443,18 @@ static int GetCoeffs(VP8BitReader* const br, ProbaArray prob,
return 0;
}
+// Alias-safe way of converting 4bytes to 32bits.
+typedef union {
+ uint8_t i8[4];
+ uint32_t i32;
+} PackedNz;
+
// Table to unpack four bits into four bytes
-static const uint8_t kUnpackTab[16][4] = {
- {0, 0, 0, 0}, {1, 0, 0, 0}, {0, 1, 0, 0}, {1, 1, 0, 0},
- {0, 0, 1, 0}, {1, 0, 1, 0}, {0, 1, 1, 0}, {1, 1, 1, 0},
- {0, 0, 0, 1}, {1, 0, 0, 1}, {0, 1, 0, 1}, {1, 1, 0, 1},
- {0, 0, 1, 1}, {1, 0, 1, 1}, {0, 1, 1, 1}, {1, 1, 1, 1} };
+static const PackedNz kUnpackTab[16] = {
+ {{0, 0, 0, 0}}, {{1, 0, 0, 0}}, {{0, 1, 0, 0}}, {{1, 1, 0, 0}},
+ {{0, 0, 1, 0}}, {{1, 0, 1, 0}}, {{0, 1, 1, 0}}, {{1, 1, 1, 0}},
+ {{0, 0, 0, 1}}, {{1, 0, 0, 1}}, {{0, 1, 0, 1}}, {{1, 1, 0, 1}},
+ {{0, 0, 1, 1}}, {{1, 0, 1, 1}}, {{0, 1, 1, 1}}, {{1, 1, 1, 1}} };
// Macro to pack four LSB of four bytes into four bits.
#if defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || \
@@ -415,19 +463,19 @@ static const uint8_t kUnpackTab[16][4] = {
#else
#define PACK_CST 0x01020408U
#endif
-#define PACK(X, S) ((((*(uint32_t*)(X)) * PACK_CST) & 0xff000000) >> (S))
+#define PACK(X, S) ((((X).i32 * PACK_CST) & 0xff000000) >> (S))
-static int ParseResiduals(VP8Decoder* const dec,
- VP8MB* const mb, VP8BitReader* const token_br) {
+static void ParseResiduals(VP8Decoder* const dec,
+ VP8MB* const mb, VP8BitReader* const token_br) {
int out_t_nz, out_l_nz, first;
ProbaArray ac_prob;
const VP8QuantMatrix* q = &dec->dqm_[dec->segment_];
int16_t* dst = dec->coeffs_;
VP8MB* const left_mb = dec->mb_info_ - 1;
- uint8_t nz_ac[4], nz_dc[4];
+ PackedNz nz_ac, nz_dc;
+ PackedNz tnz, lnz;
uint32_t non_zero_ac = 0;
uint32_t non_zero_dc = 0;
- uint8_t tnz[4], lnz[4];
int x, y, ch;
memset(dst, 0, 384 * sizeof(*dst));
@@ -445,40 +493,39 @@ static int ParseResiduals(VP8Decoder* const dec,
ac_prob = (ProbaArray)dec->proba_.coeffs_[3];
}
- memcpy(tnz, kUnpackTab[mb->nz_ & 0xf], sizeof(tnz));
- memcpy(lnz, kUnpackTab[left_mb->nz_ & 0xf], sizeof(lnz));
+ tnz = kUnpackTab[mb->nz_ & 0xf];
+ lnz = kUnpackTab[left_mb->nz_ & 0xf];
for (y = 0; y < 4; ++y) {
- int l = lnz[y];
-
+ int l = lnz.i8[y];
for (x = 0; x < 4; ++x) {
- const int ctx = l + tnz[x];
+ const int ctx = l + tnz.i8[x];
l = GetCoeffs(token_br, ac_prob, ctx,
q->y1_mat_, first, dst);
- nz_dc[x] = (dst[0] != 0);
- nz_ac[x] = tnz[x] = l;
+ nz_dc.i8[x] = (dst[0] != 0);
+ nz_ac.i8[x] = tnz.i8[x] = l;
dst += 16;
}
- lnz[y] = l;
+ lnz.i8[y] = l;
non_zero_dc |= PACK(nz_dc, 24 - y * 4);
non_zero_ac |= PACK(nz_ac, 24 - y * 4);
}
out_t_nz = PACK(tnz, 24);
out_l_nz = PACK(lnz, 24);
- memcpy(tnz, kUnpackTab[mb->nz_ >> 4], sizeof(tnz));
- memcpy(lnz, kUnpackTab[left_mb->nz_ >> 4], sizeof(lnz));
+ tnz = kUnpackTab[mb->nz_ >> 4];
+ lnz = kUnpackTab[left_mb->nz_ >> 4];
for (ch = 0; ch < 4; ch += 2) {
for (y = 0; y < 2; ++y) {
- int l = lnz[ch + y];
+ int l = lnz.i8[ch + y];
for (x = 0; x < 2; ++x) {
- const int ctx = l + tnz[ch + x];
+ const int ctx = l + tnz.i8[ch + x];
l = GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[2],
ctx, q->uv_mat_, 0, dst);
- nz_dc[y * 2 + x] = (dst[0] != 0);
- nz_ac[y * 2 + x] = tnz[ch + x] = l;
+ nz_dc.i8[y * 2 + x] = (dst[0] != 0);
+ nz_ac.i8[y * 2 + x] = tnz.i8[ch + x] = l;
dst += 16;
}
- lnz[ch + y] = l;
+ lnz.i8[ch + y] = l;
}
non_zero_dc |= PACK(nz_dc, 8 - ch * 2);
non_zero_ac |= PACK(nz_ac, 8 - ch * 2);
@@ -491,68 +538,69 @@ static int ParseResiduals(VP8Decoder* const dec,
dec->non_zero_ac_ = non_zero_ac;
dec->non_zero_ = non_zero_ac | non_zero_dc;
mb->skip_ = !dec->non_zero_;
-
- return 1;
}
#undef PACK
//-----------------------------------------------------------------------------
// Main loop
-static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
- int ok = 1;
+int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) {
VP8BitReader* const br = &dec->br_;
- VP8BitReader* token_br;
+ VP8MB* const left = dec->mb_info_ - 1;
+ VP8MB* const info = dec->mb_info_ + dec->mb_x_;
+ // Note: we don't save segment map (yet), as we don't expect
+ // to decode more than 1 keyframe.
+ if (dec->segment_hdr_.update_map_) {
+ // Hardcoded tree parsing
+ dec->segment_ = !VP8GetBit(br, dec->proba_.segments_[0]) ?
+ VP8GetBit(br, dec->proba_.segments_[1]) :
+ 2 + VP8GetBit(br, dec->proba_.segments_[2]);
+ }
+ info->skip_ = dec->use_skip_proba_ ? VP8GetBit(br, dec->skip_p_) : 0;
+
+ VP8ParseIntraMode(br, dec);
+ if (br->eof_) {
+ return 0;
+ }
+
+ if (!info->skip_) {
+ ParseResiduals(dec, info, token_br);
+ } else {
+ left->nz_ = info->nz_ = 0;
+ if (!dec->is_i4x4_) {
+ left->dc_nz_ = info->dc_nz_ = 0;
+ }
+ dec->non_zero_ = 0;
+ dec->non_zero_ac_ = 0;
+ }
+
+ return (!token_br->eof_);
+}
+
+static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
for (dec->mb_y_ = 0; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {
VP8MB* const left = dec->mb_info_ - 1;
-
- memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
+ VP8BitReader* const token_br =
+ &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
left->nz_ = 0;
left->dc_nz_ = 0;
- token_br = &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
+ memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
for (dec->mb_x_ = 0; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) {
- VP8MB* const info = dec->mb_info_ + dec->mb_x_;
-
- // Note: we don't save segment map (yet), as we don't expect
- // to decode more than 1 keyframe.
- if (dec->segment_hdr_.update_map_) {
- // Hardcoded tree parsing
- dec->segment_ = !VP8GetBit(br, dec->proba_.segments_[0]) ?
- VP8GetBit(br, dec->proba_.segments_[1]) :
- 2 + VP8GetBit(br, dec->proba_.segments_[2]);
- }
- info->skip_ = dec->use_skip_proba_ ? VP8GetBit(br, dec->skip_p_) : 0;
-
- VP8ParseIntraMode(br, dec);
-
- if (!info->skip_) {
- if (!ParseResiduals(dec, info, token_br)) {
- ok = 0;
- break;
- }
- } else {
- left->nz_ = info->nz_ = 0;
- if (!dec->is_i4x4_) {
- left->dc_nz_ = info->dc_nz_ = 0;
- }
- dec->non_zero_ = 0;
- dec->non_zero_ac_ = 0;
+ if (!VP8DecodeMB(dec, token_br)) {
+ return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
+ "Premature end-of-file encountered.");
}
VP8ReconstructBlock(dec);
// Store data and save block's filtering params
VP8StoreBlock(dec);
}
- if (!ok) {
- break;
- }
- VP8FinishRow(dec, io);
- if (dec->br_.eof_ || token_br->eof_) {
- ok = 0;
- break;
+ if (!VP8FinishRow(dec, io)) {
+ return VP8SetError(dec, VP8_STATUS_USER_ABORT,
+ "Output aborted.");
}
}
@@ -563,7 +611,7 @@ static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
}
#endif
- return ok;
+ return 1;
}
// Main entry point
@@ -572,7 +620,8 @@ int VP8Decode(VP8Decoder* const dec, VP8Io* const io) {
return 0;
}
if (io == NULL) {
- return VP8SetError(dec, 2, "NULL VP8Io parameter in VP8Decode().");
+ return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
+ "NULL VP8Io parameter in VP8Decode().");
}
if (!dec->ready_) {
@@ -585,15 +634,19 @@ int VP8Decode(VP8Decoder* const dec, VP8Io* const io) {
// will allocate memory and prepare everything.
if (!VP8InitFrame(dec, io)) {
VP8Clear(dec);
- return VP8SetError(dec, 3, "Allocation failed");
+ return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
+ "Allocation failed");
}
-
if (io->setup && !io->setup(io)) {
VP8Clear(dec);
- return VP8SetError(dec, 3, "Frame setup failed");
+ return VP8SetError(dec, VP8_STATUS_USER_ABORT,
+ "Frame setup failed");
}
+ // Disable filtering per user request (_after_ setup() is called)
+ if (io->bypass_filtering) dec->filter_type_ = 0;
+
// Main decoding loop
{
const int ret = ParseFrame(dec, io);
@@ -602,7 +655,7 @@ int VP8Decode(VP8Decoder* const dec, VP8Io* const io) {
}
if (!ret) {
VP8Clear(dec);
- return VP8SetError(dec, 3, "Frame decoding failed");
+ return 0;
}
}
@@ -622,3 +675,9 @@ void VP8Clear(VP8Decoder* const dec) {
memset(&dec->br_, 0, sizeof(dec->br_));
dec->ready_ = 0;
}
+
+//-----------------------------------------------------------------------------
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
diff --git a/third_party/libwebp/vp8i.h b/third_party/libwebp/dec/vp8i.h
index 76985b8..9cfee04 100644
--- a/third_party/libwebp/vp8i.h
+++ b/third_party/libwebp/dec/vp8i.h
@@ -9,8 +9,8 @@
//
// Author: Skal (pascal.massimino@gmail.com)
-#ifndef WEBP_DECODE_VP8I_H_
-#define WEBP_DECODE_VP8I_H_
+#ifndef WEBP_DEC_VP8I_H_
+#define WEBP_DEC_VP8I_H_
#include <string.h> // for memcpy()
#include "bits.h"
@@ -45,7 +45,8 @@ enum { B_DC_PRED = 0, // 4x4 modes
// special modes
B_DC_PRED_NOTOP = 4,
B_DC_PRED_NOLEFT = 5,
- B_DC_PRED_NOTOPLEFT = 6 };
+ B_DC_PRED_NOTOPLEFT = 6,
+ NUM_B_DC_MODES = 7 };
enum { MB_FEATURE_TREE_PROBS = 3,
NUM_MB_SEGMENTS = 4,
@@ -162,7 +163,7 @@ typedef struct {
// VP8Decoder: the main opaque structure handed over to user
struct VP8Decoder {
- int status_; // 0 = OK
+ VP8StatusCode status_;
int ready_; // true if ready to decode a picture with VP8Decode()
const char* error_msg_; // set when status_ is not OK.
@@ -246,7 +247,8 @@ struct VP8Decoder {
// internal functions. Not public.
// in vp8.c
-int VP8SetError(VP8Decoder* const dec, int error, const char *msg);
+int VP8SetError(VP8Decoder* const dec,
+ VP8StatusCode error, const char * const msg);
// in tree.c
void VP8ResetProba(VP8Proba* const proba);
@@ -262,8 +264,10 @@ int VP8InitFrame(VP8Decoder* const dec, VP8Io* io);
void VP8ReconstructBlock(VP8Decoder* const dec);
// Store a block, along with filtering params
void VP8StoreBlock(VP8Decoder* const dec);
-// Finalize and transmit a complete row
-void VP8FinishRow(VP8Decoder* const dec, VP8Io* io);
+// Finalize and transmit a complete row. Return false in case of user-abort.
+int VP8FinishRow(VP8Decoder* const dec, VP8Io* io);
+// Decode one macroblock. Returns false if there is not enough data.
+int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br);
// in dsp.c
typedef void (*VP8Idct)(const int16_t* coeffs, uint8_t* dst);
@@ -276,9 +280,9 @@ extern void (*VP8TransformWHT)(const int16_t* in, int16_t* out);
// *dst is the destination block, with stride BPS. Boundary samples are
// assumed accessible when needed.
typedef void (*VP8PredFunc)(uint8_t *dst);
-extern VP8PredFunc VP8PredLuma16[7];
-extern VP8PredFunc VP8PredChroma8[7];
-extern VP8PredFunc VP8PredLuma4[11];
+extern VP8PredFunc VP8PredLuma16[NUM_B_DC_MODES];
+extern VP8PredFunc VP8PredChroma8[NUM_B_DC_MODES];
+extern VP8PredFunc VP8PredLuma4[NUM_BMODES];
void VP8DspInit(); // must be called before anything using the above
void VP8DspInitTables(); // needs to be called no matter what.
@@ -313,4 +317,4 @@ extern VP8ChromaFilterFunc VP8HFilter8i;
} // extern "C"
#endif
-#endif // WEBP_DECODE_VP8I_H_
+#endif // WEBP_DEC_VP8I_H_
diff --git a/third_party/libwebp/webp.c b/third_party/libwebp/dec/webp.c
index f9a0eb9..823eafe641 100644
--- a/third_party/libwebp/webp.c
+++ b/third_party/libwebp/dec/webp.c
@@ -11,6 +11,7 @@
#include <stdlib.h>
#include "vp8i.h"
+#include "webpi.h"
#include "yuv.h"
#if defined(__cplusplus) || defined(c_plusplus)
@@ -37,24 +38,28 @@ static inline uint32_t get_le32(const uint8_t* const data) {
}
// If a RIFF container is detected, validate it and skip over it.
-static uint32_t CheckRIFFHeader(const uint8_t** data_ptr,
- uint32_t *data_size_ptr) {
+uint32_t WebPCheckRIFFHeader(const uint8_t** data_ptr,
+ uint32_t *data_size_ptr) {
uint32_t chunk_size = 0xffffffffu;
if (*data_size_ptr >= 10 + 20 && !memcmp(*data_ptr, "RIFF", 4)) {
if (memcmp(*data_ptr + 8, "WEBP", 4)) {
return 0; // wrong image file signature
} else {
const uint32_t riff_size = get_le32(*data_ptr + 4);
+ if (riff_size < 12) {
+ return 0; // we should have at least one chunk
+ }
if (memcmp(*data_ptr + 12, "VP8 ", 4)) {
return 0; // invalid compression format
}
chunk_size = get_le32(*data_ptr + 16);
- if ((chunk_size > riff_size + 8) || (chunk_size & 1)) {
+ if (chunk_size > riff_size - 12) {
return 0; // inconsistent size information.
}
- // We have a IFF container. Skip it.
+ // We have a RIFF container. Skip it.
*data_ptr += 20;
*data_size_ptr -= 20;
+ // Note: we don't report error for odd-sized chunks.
}
return chunk_size;
}
@@ -64,10 +69,6 @@ static uint32_t CheckRIFFHeader(const uint8_t** data_ptr,
//-----------------------------------------------------------------------------
// Fancy upscaling
-typedef enum { MODE_RGB = 0, MODE_RGBA = 1,
- MODE_BGR = 2, MODE_BGRA = 3,
- MODE_YUV = 4 } CSP_MODE;
-
#ifdef FANCY_UPSCALING
// Given samples laid out in a square as:
@@ -149,7 +150,7 @@ void UpscaleLinePair(const uint8_t* top_y, const uint8_t* bottom_y,
const uint8_t* top_u, const uint8_t* top_v,
const uint8_t* cur_u, const uint8_t* cur_v,
uint8_t* top_dst, uint8_t* bottom_dst, int len,
- CSP_MODE mode) {
+ WEBP_CSP_MODE mode) {
if (mode == MODE_RGB) {
UpscaleRgbLinePair(top_y, bottom_y, top_u, top_v, cur_u, cur_v,
top_dst, bottom_dst, len);
@@ -174,24 +175,16 @@ void UpscaleLinePair(const uint8_t* top_y, const uint8_t* bottom_y,
//-----------------------------------------------------------------------------
// Main conversion driver.
-typedef struct {
- uint8_t* output; // rgb(a) or luma
- uint8_t *u, *v;
- uint8_t *top_y, *top_u, *top_v;
- int stride; // rgb(a) stride or luma stride
- int u_stride;
- int v_stride;
- CSP_MODE mode;
-} Params;
-
-static void CustomPut(const VP8Io* io) {
- Params *p = (Params*)io->opaque;
+static int CustomPut(const VP8Io* io) {
+ WebPDecParams *p = (WebPDecParams*)io->opaque;
const int w = io->width;
const int mb_h = io->mb_h;
const int uv_w = (w + 1) / 2;
assert(!(io->mb_y & 1));
- if (w <= 0 || mb_h <= 0) return;
+ if (w <= 0 || mb_h <= 0) {
+ return 0;
+ }
if (p->mode == MODE_YUV) {
uint8_t* const y_dst = p->output + io->mb_y * p->stride;
@@ -278,13 +271,14 @@ static void CustomPut(const VP8Io* io) {
}
}
}
+ return 1;
}
//-----------------------------------------------------------------------------
static int CustomSetup(VP8Io* io) {
#ifdef FANCY_UPSCALING
- Params *p = (Params*)io->opaque;
+ WebPDecParams *p = (WebPDecParams*)io->opaque;
p->top_y = p->top_u = p->top_v = NULL;
if (p->mode != MODE_YUV) {
const int uv_width = (io->width + 1) >> 1;
@@ -302,7 +296,7 @@ static int CustomSetup(VP8Io* io) {
static void CustomTeardown(const VP8Io* io) {
#ifdef FANCY_UPSCALING
- Params *p = (Params*)io->opaque;
+ WebPDecParams *p = (WebPDecParams*)io->opaque;
if (p->top_y) {
free(p->top_y);
p->top_y = p->top_u = p->top_v = NULL;
@@ -310,12 +304,89 @@ static void CustomTeardown(const VP8Io* io) {
#endif
}
+void WebPInitCustomIo(VP8Io* const io) {
+ io->put = CustomPut;
+ io->setup = CustomSetup;
+ io->teardown = CustomTeardown;
+}
+
+//-----------------------------------------------------------------------------
+// Init/Check/Free decoding parameters and buffer
+
+int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width,
+ int* height, WebPDecParams* const params) {
+ int w, h, stride;
+ int uv_size = 0;
+ int uv_stride = 0;
+ int size;
+ uint8_t* output;
+ WEBP_CSP_MODE mode = params->mode;
+
+ if (!WebPGetInfo(data, data_size, &w, &h)) {
+ return 0;
+ }
+ if (width) *width = w;
+ if (height) *height = h;
+
+ // initialize output buffer, now that dimensions are known.
+ stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w
+ : (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w
+ : w;
+ size = stride * h;
+
+ if (mode == MODE_YUV) {
+ uv_stride = (w + 1) / 2;
+ uv_size = uv_stride * ((h + 1) / 2);
+ }
+
+ output = (uint8_t*)malloc(size + 2 * uv_size);
+ if (!output) {
+ return 0;
+ }
+
+ params->output = output;
+ params->stride = stride;
+ if (mode == MODE_YUV) {
+ params->u = output + size;
+ params->u_stride = uv_stride;
+ params->v = output + size + uv_size;
+ params->v_stride = uv_stride;
+ }
+ return 1;
+}
+
+int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params,
+ int output_size, int output_u_size, int output_v_size) {
+ int ok = 1;
+ WEBP_CSP_MODE mode = params->mode;
+ ok &= (params->stride * io->height <= output_size);
+ if (mode == MODE_RGB || mode == MODE_BGR) {
+ ok &= (params->stride >= io->width * 3);
+ } else if (mode == MODE_RGBA || mode == MODE_BGRA) {
+ ok &= (params->stride >= io->width * 4);
+ } else {
+ // some extra checks for U/V
+ const int u_size = params->u_stride * ((io->height + 1) / 2);
+ const int v_size = params->v_stride * ((io->height + 1) / 2);
+ ok &= (params->stride >= io->width);
+ ok &= (params->u_stride >= (io->width + 1) / 2) &&
+ (params->v_stride >= (io->width + 1) / 2);
+ ok &= (u_size <= output_u_size && v_size <= output_v_size);
+ }
+ return ok;
+}
+
+void WebPClearDecParams(WebPDecParams* params) {
+ free(params->output);
+ memset(params, 0, sizeof(*params));
+}
+
//-----------------------------------------------------------------------------
// "Into" variants
-static uint8_t* DecodeInto(CSP_MODE mode,
+static uint8_t* DecodeInto(WEBP_CSP_MODE mode,
const uint8_t* data, uint32_t data_size,
- Params* params, int output_size,
+ WebPDecParams* params, int output_size,
int output_u_size, int output_v_size) {
VP8Decoder* dec = VP8New();
VP8Io io;
@@ -331,30 +402,16 @@ static uint8_t* DecodeInto(CSP_MODE mode,
params->mode = mode;
io.opaque = params;
- io.put = CustomPut;
- io.setup = CustomSetup;
- io.teardown = CustomTeardown;
+ WebPInitCustomIo(&io);
if (!VP8GetHeaders(dec, &io)) {
VP8Delete(dec);
return NULL;
}
- // check output buffers
- ok &= (params->stride * io.height <= output_size);
- if (mode == MODE_RGB || mode == MODE_BGR) {
- ok &= (params->stride >= io.width * 3);
- } else if (mode == MODE_RGBA || mode == MODE_BGRA) {
- ok &= (params->stride >= io.width * 4);
- } else {
- // some extra checks for U/V
- const int u_size = params->u_stride * ((io.height + 1) / 2);
- const int v_size = params->v_stride * ((io.height + 1) / 2);
- ok &= (params->stride >= io.width);
- ok &= (params->u_stride >= (io.width + 1) / 2) &&
- (params->v_stride >= (io.width + 1) / 2);
- ok &= (u_size <= output_u_size && v_size <= output_v_size);
- }
+ // check output buffers
+ ok = WebPCheckDecParams(&io, params, output_size,
+ output_u_size, output_v_size);
if (!ok) {
VP8Delete(dec);
return NULL;
@@ -372,7 +429,7 @@ static uint8_t* DecodeInto(CSP_MODE mode,
uint8_t* WebPDecodeRGBInto(const uint8_t* data, uint32_t data_size,
uint8_t* output, int output_size,
int output_stride) {
- Params params;
+ WebPDecParams params;
if (output == NULL) {
return NULL;
@@ -386,7 +443,7 @@ uint8_t* WebPDecodeRGBInto(const uint8_t* data, uint32_t data_size,
uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size,
uint8_t* output, int output_size,
int output_stride) {
- Params params;
+ WebPDecParams params;
if (output == NULL) {
return NULL;
@@ -400,7 +457,7 @@ uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size,
uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size,
uint8_t* output, int output_size,
int output_stride) {
- Params params;
+ WebPDecParams params;
if (output == NULL) {
return NULL;
@@ -414,7 +471,7 @@ uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size,
uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size,
uint8_t* output, int output_size,
int output_stride) {
- Params params;
+ WebPDecParams params;
if (output == NULL) {
return NULL;
@@ -429,7 +486,7 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size,
uint8_t* luma, int luma_size, int luma_stride,
uint8_t* u, int u_size, int u_stride,
uint8_t* v, int v_size, int v_stride) {
- Params params;
+ WebPDecParams params;
if (luma == NULL) {
return NULL;
@@ -447,47 +504,25 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size,
//-----------------------------------------------------------------------------
-static uint8_t* Decode(CSP_MODE mode, const uint8_t* data, uint32_t data_size,
- int* width, int* height, Params* params_out) {
- int w, h, stride;
+static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* data,
+ uint32_t data_size, int* width, int* height,
+ WebPDecParams* params_out) {
+ int size = 0;
int uv_size = 0;
- int uv_stride = 0;
- int size;
- uint8_t* output;
- Params params = { 0 };
+ WebPDecParams params = { 0 };
- if (!WebPGetInfo(data, data_size, &w, &h)) {
+ params.mode = mode;
+ if (!WebPInitDecParams(data, data_size, width, height, &params)) {
return NULL;
}
- if (width) *width = w;
- if (height) *height = h;
-
- // initialize output buffer, now that dimensions are known.
- stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w
- : (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w
- : w;
- size = stride * h;
- if (mode == MODE_YUV) {
- uv_stride = (w + 1) / 2;
- uv_size = uv_stride * ((h + 1) / 2);
- }
-
- output = (uint8_t*)malloc(size + 2 * uv_size);
- if (!output) {
- return NULL;
- }
-
- params.output = output;
- params.stride = stride;
- if (mode == MODE_YUV) {
- params.u = output + size;
- params.u_stride = uv_stride;
- params.v = output + size + uv_size;
- params.v_stride = uv_stride;
+ size = params.stride * (*height);
+ uv_size = params.u_stride * ((*height + 1) / 2);
+ if (!DecodeInto(mode, data, data_size, &params, size, uv_size, uv_size)) {
+ WebPClearDecParams(&params);
}
if (params_out) *params_out = params;
- return DecodeInto(mode, data, data_size, &params, size, uv_size, uv_size);
+ return params.output;
}
uint8_t* WebPDecodeRGB(const uint8_t* data, uint32_t data_size,
@@ -513,7 +548,7 @@ uint8_t* WebPDecodeBGRA(const uint8_t* data, uint32_t data_size,
uint8_t* WebPDecodeYUV(const uint8_t* data, uint32_t data_size,
int *width, int *height, uint8_t** u, uint8_t** v,
int *stride, int* uv_stride) {
- Params params;
+ WebPDecParams params;
uint8_t* const out = Decode(MODE_YUV, data, data_size,
width, height, &params);
@@ -532,7 +567,7 @@ uint8_t* WebPDecodeYUV(const uint8_t* data, uint32_t data_size,
int WebPGetInfo(const uint8_t* data, uint32_t data_size,
int *width, int *height) {
- const uint32_t chunk_size = CheckRIFFHeader(&data, &data_size);
+ const uint32_t chunk_size = WebPCheckRIFFHeader(&data, &data_size);
if (!chunk_size) {
return 0; // unsupported RIFF header
}
@@ -559,7 +594,7 @@ int WebPGetInfo(const uint8_t* data, uint32_t data_size,
if (!((bits >> 4) & 1)) {
return 0; // first frame is invisible!
}
- if (((bits >> 5)) >= chunk_size) { // partition_length
+ if (((bits >> 5)) >= chunk_size) { // partition_length
return 0; // inconsistent size information.
}
diff --git a/third_party/libwebp/dec/webpi.h b/third_party/libwebp/dec/webpi.h
new file mode 100644
index 0000000..7bbe4cf
--- /dev/null
+++ b/third_party/libwebp/dec/webpi.h
@@ -0,0 +1,63 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// Internal header: WebP decoding parameters and custom IO on buffer
+//
+// Author: somnath@google.com (Somnath Banerjee)
+
+#ifndef WEBP_DEC_WEBPI_H
+#define WEBP_DEC_WEBPI_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "../webp/decode_vp8.h"
+
+typedef enum { MODE_RGB = 0, MODE_RGBA = 1,
+ MODE_BGR = 2, MODE_BGRA = 3,
+ MODE_YUV = 4 } WEBP_CSP_MODE;
+
+ // Decoding output parameters.
+typedef struct {
+ uint8_t* output; // rgb(a) or luma
+ uint8_t *u, *v;
+ uint8_t *top_y, *top_u, *top_v;
+ int stride; // rgb(a) stride or luma stride
+ int u_stride;
+ int v_stride;
+ WEBP_CSP_MODE mode;
+} WebPDecParams;
+
+// If a RIFF container is detected, validate it and skip over it. Returns
+// VP8 bit-stream size if RIFF header is valid else returns 0
+uint32_t WebPCheckRIFFHeader(const uint8_t** data_ptr,
+ uint32_t *data_size_ptr);
+
+// Initializes VP8Io with custom setup, io and teardown functions
+void WebPInitCustomIo(VP8Io* const io);
+
+// Initializes params_out by allocating output buffer and setting the
+// stride information. It also outputs width and height information of
+// the WebP image. Returns 1 if succeeds.
+int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width,
+ int* height, WebPDecParams* const params_out);
+
+// Verifies various size configurations (e.g stride >= width, specified
+// output size <= stride * height etc.). Returns 0 if checks fail.
+int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params,
+ int output_size, int output_u_size, int output_v_size);
+
+// Deallocate memory allocated by WebPInitDecParams() and reset the
+// WebPDecParams object.
+void WebPClearDecParams(WebPDecParams* params);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
+
+#endif // WEBP_DEC_WEBPI_H
diff --git a/third_party/libwebp/yuv.c b/third_party/libwebp/dec/yuv.c
index 30f59c1..30f59c1 100644
--- a/third_party/libwebp/yuv.c
+++ b/third_party/libwebp/dec/yuv.c
diff --git a/third_party/libwebp/yuv.h b/third_party/libwebp/dec/yuv.h
index 5a9fce2..db31453 100644
--- a/third_party/libwebp/yuv.h
+++ b/third_party/libwebp/dec/yuv.h
@@ -9,10 +9,10 @@
//
// Author: Skal (pascal.massimino@gmail.com)
-#ifndef WEBP_DECODE_YUV_H_
-#define WEBP_DECODE_YUV_H_
+#ifndef WEBP_DEC_YUV_H_
+#define WEBP_DEC_YUV_H_
-#include "webp/decode_vp8.h"
+#include "../webp/decode_vp8.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
@@ -63,4 +63,4 @@ void VP8YUVInit();
} // extern "C"
#endif
-#endif // WEBP_DECODE_YUV_H_
+#endif // WEBP_DEC_YUV_H_
diff --git a/third_party/libwebp/enc/analysis.c b/third_party/libwebp/enc/analysis.c
new file mode 100644
index 0000000..41e12e8
--- /dev/null
+++ b/third_party/libwebp/enc/analysis.c
@@ -0,0 +1,399 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// Macroblock analysis
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "vp8enci.h"
+#include "cost.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define MAX_COEFF_THRESH 64
+#define MAX_ITERS_K_MEANS 6
+
+//-----------------------------------------------------------------------------
+// Compute susceptibility based on DCT-coeff histograms:
+// the higher, the "easier" the macroblock is to compress.
+
+static int ClipAlpha(int alpha) {
+ return alpha < 0 ? 0 : alpha > 255 ? 255 : alpha;
+}
+
+static int GetAlpha(const int histo[MAX_COEFF_THRESH]) {
+ int num = 0, den = 0, val = 0;
+ int k;
+ int alpha;
+ for (k = 0; k < MAX_COEFF_THRESH; ++k) {
+ if (histo[k]) {
+ val += histo[k];
+ num += val * (k + 1);
+ den += (k + 1) * (k + 1);
+ }
+ }
+ // we scale the value to a usable [0..255] range
+ alpha = den ? 10 * num / den - 5 : 0;
+ return ClipAlpha(alpha);
+}
+
+static int CollectHistogram(const uint8_t* ref, const uint8_t* pred,
+ int start_block, int end_block) {
+ int histo[MAX_COEFF_THRESH] = { 0 };
+ int16_t out[16];
+ int j, k;
+ for (j = start_block; j < end_block; ++j) {
+ VP8FTransform(ref + VP8Scan[j], pred + VP8Scan[j], out);
+ for (k = 0; k < 16; ++k) {
+ const int v = abs(out[k]) >> 2;
+ if (v) {
+ const int bin = (v > MAX_COEFF_THRESH) ? MAX_COEFF_THRESH : v;
+ histo[bin - 1]++;
+ }
+ }
+ }
+ return GetAlpha(histo);
+}
+
+//-----------------------------------------------------------------------------
+// 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* tmp = (uint8_t*)malloc(w * h * sizeof(uint8_t));
+
+ 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;
+ }
+ }
+ 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);
+}
+
+//-----------------------------------------------------------------------------
+// Finalize Segment probability based on the coding tree
+
+static int GetProba(int a, int b) {
+ int proba;
+ const int total = a + b;
+ if (total == 0) return 255; // that's the default probability.
+ proba = (255 * a + total / 2) / total;
+ return 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) {
+ 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;
+ }
+}
+
+static 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);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Simplified k-Means, to assign Nb segments based on alpha-histogram
+
+static void AssignSegments(VP8Encoder* const enc, const int alphas[256]) {
+ const int nb = enc->segment_hdr_.num_segments_;
+ int centers[NUM_MB_SEGMENTS];
+ int weighted_average;
+ int map[256];
+ int a, n, k;
+ int min_a = 0, max_a = 255, range_a;
+ // 'int' type is ok for histo, and won't overflow
+ int accum[NUM_MB_SEGMENTS], dist_accum[NUM_MB_SEGMENTS];
+
+ // bracket the input
+ for (n = 0; n < 256 && alphas[n] == 0; ++n) {}
+ min_a = n;
+ for (n = 255; n > min_a && alphas[n] == 0; --n) {}
+ max_a = n;
+ range_a = max_a - min_a;
+
+ // Spread initial centers evenly
+ for (n = 1, k = 0; n < 2 * nb; n += 2) {
+ 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 < nb - 1 && 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 a = mb->alpha_;
+ mb->segment_ = map[a];
+ mb->alpha_ = centers[map[a]]; // just for the record.
+ }
+
+ if (nb > 1) {
+ const int smooth = (enc->config_->preprocessing & 1);
+ if (smooth) SmoothSegmentMap(enc);
+ }
+
+ SetSegmentProbas(enc); // Assign final proba
+ 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,
+// we don't need to test all the possible modes during the analysis phase.
+#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_ >= 3) ? MAX_INTRA16_MODE : 4;
+ int mode;
+ int best_alpha = -1;
+ int best_mode = 0;
+
+ VP8MakeLuma16Preds(it);
+ for (mode = 0; mode < max_mode; ++mode) {
+ const int alpha = CollectHistogram(it->yuv_in_ + Y_OFF,
+ it->yuv_p_ + VP8I16ModeOffsets[mode],
+ 0, 16);
+ if (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) {
+ int modes[16];
+ const int max_mode = (it->enc_->method_ >= 3) ? MAX_INTRA4_MODE : NUM_BMODES;
+ int i4_alpha = 0;
+ VP8IteratorStartI4(it);
+ do {
+ int mode;
+ int best_mode_alpha = -1;
+ const uint8_t* const src = it->yuv_in_ + Y_OFF + VP8Scan[it->i4_];
+
+ VP8MakeIntra4Preds(it);
+ for (mode = 0; mode < max_mode; ++mode) {
+ const int alpha = CollectHistogram(src,
+ it->yuv_p_ + VP8I4ModeOffsets[mode],
+ 0, 1);
+ if (alpha > best_mode_alpha) {
+ best_mode_alpha = alpha;
+ modes[it->i4_] = mode;
+ }
+ }
+ i4_alpha += best_mode_alpha;
+ // Note: we reuse the original samples for predictors
+ } while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF));
+
+ if (i4_alpha > best_alpha) {
+ VP8SetIntra4Mode(it, modes);
+ best_alpha = ClipAlpha(i4_alpha);
+ }
+ return best_alpha;
+}
+
+static int MBAnalyzeBestUVMode(VP8EncIterator* const it) {
+ int best_alpha = -1;
+ int best_mode = 0;
+ const int max_mode = (it->enc_->method_ >= 3) ? MAX_UV_MODE : 4;
+ int mode;
+ VP8MakeChroma8Preds(it);
+ for (mode = 0; mode < max_mode; ++mode) {
+ const int alpha = CollectHistogram(it->yuv_in_ + U_OFF,
+ it->yuv_p_ + VP8UVModeOffsets[mode],
+ 16, 16 + 4 + 4);
+ if (alpha > best_alpha) {
+ best_alpha = alpha;
+ best_mode = mode;
+ }
+ }
+ VP8SetIntraUVMode(it, best_mode);
+ return best_alpha;
+}
+
+static void MBAnalyze(VP8EncIterator* const it,
+ int alphas[256], 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_ != 3) {
+ // 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 = (best_alpha + best_uv_alpha + 1) / 2;
+ alphas[best_alpha]++;
+ *uv_alpha += best_uv_alpha;
+ it->mb_->alpha_ = best_alpha; // Informative only.
+}
+
+//-----------------------------------------------------------------------------
+// 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.
+
+int VP8EncAnalyze(VP8Encoder* const enc) {
+ int alphas[256] = { 0 };
+ VP8EncIterator it;
+
+ VP8IteratorInit(enc, &it);
+ enc->uv_alpha_ = 0;
+ do {
+ VP8IteratorImport(&it);
+ MBAnalyze(&it, alphas, &enc->uv_alpha_);
+ // Let's pretend we have perfect lossless reconstruction.
+ } while (VP8IteratorNext(&it, it.yuv_in_));
+ enc->uv_alpha_ /= enc->mb_w_ * enc->mb_h_;
+ AssignSegments(enc, alphas);
+
+ return 1;
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
diff --git a/third_party/libwebp/enc/bit_writer.c b/third_party/libwebp/enc/bit_writer.c
new file mode 100644
index 0000000..3656a7e
--- /dev/null
+++ b/third_party/libwebp/enc/bit_writer.c
@@ -0,0 +1,175 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// Bit writing and boolean coder
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <stdlib.h>
+#include "vp8enci.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+//-----------------------------------------------------------------------------
+// VP8BitWriter
+
+static int BitWriterResize(VP8BitWriter* const bw, size_t extra_size) {
+ uint8_t* new_buf;
+ size_t new_size;
+ const size_t needed_size = bw->pos_ + extra_size;
+ if (needed_size <= bw->max_pos_) return 1;
+ new_size = 2 * bw->max_pos_;
+ if (new_size < needed_size)
+ new_size = needed_size;
+ if (new_size < 1024) new_size = 1024;
+ new_buf = (uint8_t*)malloc(new_size);
+ if (new_buf == NULL) {
+ bw->error_ = 1;
+ return 0;
+ }
+ if (bw->pos_ > 0) memcpy(new_buf, bw->buf_, bw->pos_);
+ free(bw->buf_);
+ bw->buf_ = new_buf;
+ bw->max_pos_ = new_size;
+ return 1;
+}
+
+static void kFlush(VP8BitWriter* const bw) {
+ const int s = 8 + bw->nb_bits_;
+ const int32_t bits = bw->value_ >> s;
+ assert(bw->nb_bits_ >= 0);
+ bw->value_ -= bits << s;
+ bw->nb_bits_ -= 8;
+ if ((bits & 0xff) != 0xff) {
+ size_t pos = bw->pos_;
+ if (pos + bw->run_ >= bw->max_pos_) { // reallocate
+ if (!BitWriterResize(bw, bw->run_ + 1)) {
+ return;
+ }
+ }
+ if (bits & 0x100) { // overflow -> propagate carry over pending 0xff's
+ if (pos > 0) bw->buf_[pos - 1]++;
+ }
+ if (bw->run_ > 0) {
+ const int value = (bits & 0x100) ? 0x00 : 0xff;
+ for (; bw->run_ > 0; --bw->run_) bw->buf_[pos++] = value;
+ }
+ bw->buf_[pos++] = bits;
+ bw->pos_ = pos;
+ } else {
+ bw->run_++; // delay writing of bytes 0xff, pending eventual carry.
+ }
+}
+
+//-----------------------------------------------------------------------------
+// renormalization
+
+static const uint8_t kNorm[128] = { // renorm_sizes[i] = 8 - log2(i)
+ 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0
+};
+
+// range = ((range + 1) << kVP8Log2Range[range]) - 1
+const uint8_t kNewRange[128] = {
+ 127, 127, 191, 127, 159, 191, 223, 127, 143, 159, 175, 191, 207, 223, 239,
+ 127, 135, 143, 151, 159, 167, 175, 183, 191, 199, 207, 215, 223, 231, 239,
+ 247, 127, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179,
+ 183, 187, 191, 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239,
+ 243, 247, 251, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149,
+ 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179,
+ 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209,
+ 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239,
+ 241, 243, 245, 247, 249, 251, 253, 127
+};
+
+int VP8PutBit(VP8BitWriter* const bw, int bit, int prob) {
+ const int split = (bw->range_ * prob) >> 8;
+ if (bit) {
+ bw->value_ += split + 1;
+ bw->range_ -= split + 1;
+ } else {
+ bw->range_ = split;
+ }
+ if (bw->range_ < 127) { // emit 'shift' bits out and renormalize
+ const int shift = kNorm[bw->range_];
+ bw->range_ = kNewRange[bw->range_];
+ bw->value_ <<= shift;
+ bw->nb_bits_ += shift;
+ if (bw->nb_bits_ > 0) kFlush(bw);
+ }
+ return bit;
+}
+
+int VP8PutBitUniform(VP8BitWriter* const bw, int bit) {
+ const int split = bw->range_ >> 1;
+ if (bit) {
+ bw->value_ += split + 1;
+ bw->range_ -= split + 1;
+ } else {
+ bw->range_ = split;
+ }
+ if (bw->range_ < 127) {
+ bw->range_ = kNewRange[bw->range_];
+ bw->value_ <<= 1;
+ bw->nb_bits_ += 1;
+ if (bw->nb_bits_ > 0) kFlush(bw);
+ }
+ return bit;
+}
+
+void VP8PutValue(VP8BitWriter* const bw, int value, int nb_bits) {
+ int mask;
+ for (mask = 1 << (nb_bits - 1); mask; mask >>= 1)
+ VP8PutBitUniform(bw, value & mask);
+}
+
+void VP8PutSignedValue(VP8BitWriter* const bw, int value, int nb_bits) {
+ if (!VP8PutBitUniform(bw, value != 0))
+ return;
+ if (value < 0) {
+ VP8PutValue(bw, ((-value) << 1) | 1, nb_bits + 1);
+ } else {
+ VP8PutValue(bw, value << 1, nb_bits + 1);
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size) {
+ bw->range_ = 255 - 1;
+ bw->value_ = 0;
+ bw->run_ = 0;
+ bw->nb_bits_ = -8;
+ bw->pos_ = 0;
+ bw->max_pos_ = 0;
+ bw->error_ = 0;
+ bw->buf_ = NULL;
+ return (expected_size > 0) ? BitWriterResize(bw, expected_size) : 1;
+}
+
+uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw) {
+ VP8PutValue(bw, 0, 9 - bw->nb_bits_);
+ bw->nb_bits_ = 0; // pad with zeroes
+ kFlush(bw);
+ return bw->buf_;
+}
+
+//-----------------------------------------------------------------------------
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
diff --git a/third_party/libwebp/enc/bit_writer.h b/third_party/libwebp/enc/bit_writer.h
new file mode 100644
index 0000000..3773c9c
--- /dev/null
+++ b/third_party/libwebp/enc/bit_writer.h
@@ -0,0 +1,61 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// Bit writing and boolean coder
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_ENC_BIT_WRITER_H_
+#define WEBP_ENC_BIT_WRITER_H_
+
+#include "vp8enci.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+//-----------------------------------------------------------------------------
+// Bit-writing
+
+typedef struct VP8BitWriter VP8BitWriter;
+struct VP8BitWriter {
+ int32_t range_; // range-1
+ int32_t value_;
+ int run_; // number of outstanding bits
+ int nb_bits_; // number of pending bits
+ uint8_t* buf_;
+ size_t pos_;
+ size_t max_pos_;
+ int error_; // true in case of error
+};
+
+int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size);
+uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw);
+int VP8PutBit(VP8BitWriter* const bw, int bit, int prob);
+int VP8PutBitUniform(VP8BitWriter* const bw, int bit);
+void VP8PutValue(VP8BitWriter* const bw, int value, int nb_bits);
+void VP8PutSignedValue(VP8BitWriter* const bw, int value, int nb_bits);
+
+// return approximate write position (in bits)
+static inline uint64_t VP8BitWriterPos(const VP8BitWriter* const bw) {
+ return (uint64_t)(bw->pos_ + bw->run_) * 8 + 8 + bw->nb_bits_;
+}
+
+static inline uint8_t* VP8BitWriterBuf(const VP8BitWriter* const bw) {
+ return bw->buf_;
+}
+static inline size_t VP8BitWriterSize(const VP8BitWriter* const bw) {
+ return bw->pos_;
+}
+
+//-----------------------------------------------------------------------------
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
+
+#endif // WEBP_ENC_BIT_WRITER_H_
diff --git a/third_party/libwebp/enc/config.c b/third_party/libwebp/enc/config.c
new file mode 100644
index 0000000..e79ba08
--- /dev/null
+++ b/third_party/libwebp/enc/config.c
@@ -0,0 +1,115 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// Coding tools configuration
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include "../webp/encode.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+//-----------------------------------------------------------------------------
+// WebPConfig
+//-----------------------------------------------------------------------------
+
+int WebPConfigInitInternal(WebPConfig* const config,
+ WebPPreset preset, float quality, int version) {
+ if (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 = 20; // default: light filtering
+ config->filter_sharpness = 0;
+ config->filter_type = 0; // default: simple
+ config->partitions = 0;
+ config->segments = 4;
+ config->pass = 1;
+ config->show_compressed = 0;
+ config->preprocessing = 0;
+ config->autofilter = 0;
+
+ // TODO(skal): tune.
+ switch (preset) {
+ case WEBP_PRESET_PICTURE:
+ config->sns_strength = 80;
+ config->filter_sharpness = 4;
+ config->filter_strength = 35;
+ break;
+ case WEBP_PRESET_PHOTO:
+ config->sns_strength = 80;
+ config->filter_sharpness = 3;
+ config->filter_strength = 30;
+ 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
+ break;
+ case WEBP_PRESET_TEXT:
+ config->sns_strength = 0;
+ config->filter_strength = 0; // disable filtering to retain sharpness
+ config->segments = 2;
+ break;
+ case WEBP_PRESET_DEFAULT:
+ default:
+ break;
+ }
+ return WebPValidateConfig(config);
+}
+
+int WebPValidateConfig(const WebPConfig* const 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 > 1)
+ return 0;
+ if (config->partitions < 0 || config->partitions > 3)
+ return 0;
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
diff --git a/third_party/libwebp/enc/cost.c b/third_party/libwebp/enc/cost.c
new file mode 100644
index 0000000..f765598
--- /dev/null
+++ b/third_party/libwebp/enc/cost.c
@@ -0,0 +1,491 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// Cost tables for level and modes
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+
+#include "cost.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+//-----------------------------------------------------------------------------
+// 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 given the pattern of contexts
+// to use for coding it (in [][0]) as well as the bit value to use for
+// each context (in [][1]).
+static const uint16_t kLevelCodes[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[2048] = {
+ 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 = kLevelCodes[level - 1][0];
+ int bits = kLevelCodes[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;
+ 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.
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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 };
+const uint16_t VP8FixedCostsI16[4] = { 663, 919, 872, 919 };
+const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES] = {
+ { { 251, 1362, 1934, 2085, 2314, 2230, 1839, 1988, 2437, 2348 },
+ { 403, 680, 1507, 1519, 2060, 2005, 1992, 1914, 1924, 1733 },
+ { 353, 1121, 973, 1895, 2060, 1787, 1671, 1516, 2012, 1868 },
+ { 770, 852, 1581, 632, 1393, 1780, 1823, 1936, 1074, 1218 },
+ { 510, 1270, 1467, 1319, 847, 1279, 1792, 2094, 1080, 1353 },
+ { 488, 1322, 918, 1573, 1300, 883, 1814, 1752, 1756, 1502 },
+ { 425, 992, 1820, 1514, 1843, 2440, 937, 1771, 1924, 1129 },
+ { 363, 1248, 1257, 1970, 2194, 2385, 1569, 953, 1951, 1601 },
+ { 723, 1257, 1631, 964, 963, 1508, 1697, 1824, 671, 1418 },
+ { 635, 1038, 1573, 930, 1673, 1413, 1410, 1687, 1410, 749 } },
+ { { 451, 613, 1345, 1702, 1870, 1716, 1728, 1766, 2190, 2310 },
+ { 678, 453, 1171, 1443, 1925, 1831, 2045, 1781, 1887, 1602 },
+ { 711, 666, 674, 1718, 1910, 1493, 1775, 1193, 2325, 2325 },
+ { 883, 854, 1583, 542, 1800, 1878, 1664, 2149, 1207, 1087 },
+ { 669, 994, 1248, 1122, 949, 1179, 1376, 1729, 1070, 1244 },
+ { 715, 1026, 715, 1350, 1430, 930, 1717, 1296, 1479, 1479 },
+ { 544, 841, 1656, 1450, 2094, 3883, 1010, 1759, 2076, 809 },
+ { 610, 855, 957, 1553, 2067, 1561, 1704, 824, 2066, 1226 },
+ { 833, 960, 1416, 819, 1277, 1619, 1501, 1617, 757, 1182 },
+ { 711, 964, 1252, 879, 1441, 1828, 1508, 1636, 1594, 734 } },
+ { { 605, 764, 734, 1713, 1747, 1192, 1819, 1353, 1877, 2392 },
+ { 866, 641, 586, 1622, 2072, 1431, 1888, 1346, 2189, 1764 },
+ { 901, 851, 456, 2165, 2281, 1405, 1739, 1193, 2183, 2443 },
+ { 770, 1045, 952, 1078, 1342, 1191, 1436, 1063, 1303, 995 },
+ { 901, 1086, 727, 1170, 884, 1105, 1267, 1401, 1739, 1337 },
+ { 951, 1162, 595, 1488, 1388, 703, 1790, 1366, 2057, 1724 },
+ { 534, 986, 1273, 1987, 3273, 1485, 1024, 1399, 1583, 866 },
+ { 699, 1182, 695, 1978, 1726, 1986, 1326, 714, 1750, 1672 },
+ { 951, 1217, 1209, 920, 1062, 1441, 1548, 999, 952, 932 },
+ { 733, 1284, 784, 1256, 1557, 1098, 1257, 1357, 1414, 908 } },
+ { { 316, 1075, 1653, 1220, 2145, 2051, 1730, 2131, 1884, 1790 },
+ { 745, 516, 1404, 894, 1599, 2375, 2013, 2105, 1475, 1381 },
+ { 516, 729, 1088, 1319, 1637, 3426, 1636, 1275, 1531, 1453 },
+ { 894, 943, 2138, 468, 1704, 2259, 2069, 1763, 1266, 1158 },
+ { 605, 1025, 1235, 871, 1170, 1767, 1493, 1500, 1104, 1258 },
+ { 739, 826, 1207, 1151, 1412, 846, 1305, 2726, 1014, 1569 },
+ { 558, 825, 1820, 1398, 3344, 1556, 1218, 1550, 1228, 878 },
+ { 429, 951, 1089, 1816, 3861, 3861, 1556, 969, 1568, 1828 },
+ { 883, 961, 1752, 769, 1468, 1810, 2081, 2346, 613, 1298 },
+ { 803, 895, 1372, 641, 1303, 1708, 1686, 1700, 1306, 1033 } },
+ { { 439, 1267, 1270, 1579, 963, 1193, 1723, 1729, 1198, 1993 },
+ { 705, 725, 1029, 1153, 1176, 1103, 1821, 1567, 1259, 1574 },
+ { 723, 859, 802, 1253, 972, 1202, 1407, 1665, 1520, 1674 },
+ { 894, 960, 1254, 887, 1052, 1607, 1344, 1349, 865, 1150 },
+ { 833, 1312, 1337, 1205, 572, 1288, 1414, 1529, 1088, 1430 },
+ { 842, 1279, 1068, 1861, 862, 688, 1861, 1630, 1039, 1381 },
+ { 766, 938, 1279, 1546, 3338, 1550, 1031, 1542, 1288, 640 },
+ { 715, 1090, 835, 1609, 1100, 1100, 1603, 1019, 1102, 1617 },
+ { 894, 1813, 1500, 1188, 789, 1194, 1491, 1919, 617, 1333 },
+ { 610, 1076, 1644, 1281, 1283, 975, 1179, 1688, 1434, 889 } },
+ { { 544, 971, 1146, 1849, 1221, 740, 1857, 1621, 1683, 2430 },
+ { 723, 705, 961, 1371, 1426, 821, 2081, 2079, 1839, 1380 },
+ { 783, 857, 703, 2145, 1419, 814, 1791, 1310, 1609, 2206 },
+ { 997, 1000, 1153, 792, 1229, 1162, 1810, 1418, 942, 979 },
+ { 901, 1226, 883, 1289, 793, 715, 1904, 1649, 1319, 3108 },
+ { 979, 1478, 782, 2216, 1454, 455, 3092, 1591, 1997, 1664 },
+ { 663, 1110, 1504, 1114, 1522, 3311, 676, 1522, 1530, 1024 },
+ { 605, 1138, 1153, 1314, 1569, 1315, 1157, 804, 1574, 1320 },
+ { 770, 1216, 1218, 1227, 869, 1384, 1232, 1375, 834, 1239 },
+ { 775, 1007, 843, 1216, 1225, 1074, 2527, 1479, 1149, 975 } },
+ { { 477, 817, 1309, 1439, 1708, 1454, 1159, 1241, 1945, 1672 },
+ { 577, 796, 1112, 1271, 1618, 1458, 1087, 1345, 1831, 1265 },
+ { 663, 776, 753, 1940, 1690, 1690, 1227, 1097, 3149, 1361 },
+ { 766, 1299, 1744, 1161, 1565, 1106, 1045, 1230, 1232, 707 },
+ { 915, 1026, 1404, 1182, 1184, 851, 1428, 2425, 1043, 789 },
+ { 883, 1456, 790, 1082, 1086, 985, 1083, 1484, 1238, 1160 },
+ { 507, 1345, 2261, 1995, 1847, 3636, 653, 1761, 2287, 933 },
+ { 553, 1193, 1470, 2057, 2059, 2059, 833, 779, 2058, 1263 },
+ { 766, 1275, 1515, 1039, 957, 1554, 1286, 1540, 1289, 705 },
+ { 499, 1378, 1496, 1385, 1850, 1850, 1044, 2465, 1515, 720 } },
+ { { 553, 930, 978, 2077, 1968, 1481, 1457, 761, 1957, 2362 },
+ { 694, 864, 905, 1720, 1670, 1621, 1429, 718, 2125, 1477 },
+ { 699, 968, 658, 3190, 2024, 1479, 1865, 750, 2060, 2320 },
+ { 733, 1308, 1296, 1062, 1576, 1322, 1062, 1112, 1172, 816 },
+ { 920, 927, 1052, 939, 947, 1156, 1152, 1073, 3056, 1268 },
+ { 723, 1534, 711, 1547, 1294, 892, 1553, 928, 1815, 1561 },
+ { 663, 1366, 1583, 2111, 1712, 3501, 522, 1155, 2130, 1133 },
+ { 614, 1731, 1188, 2343, 1944, 3733, 1287, 487, 3546, 1758 },
+ { 770, 1585, 1312, 826, 884, 2673, 1185, 1006, 1195, 1195 },
+ { 758, 1333, 1273, 1023, 1621, 1162, 1351, 833, 1479, 862 } },
+ { { 376, 1193, 1446, 1149, 1545, 1577, 1870, 1789, 1175, 1823 },
+ { 803, 633, 1136, 1058, 1350, 1323, 1598, 2247, 1072, 1252 },
+ { 614, 1048, 943, 981, 1152, 1869, 1461, 1020, 1618, 1618 },
+ { 1107, 1085, 1282, 592, 1779, 1933, 1648, 2403, 691, 1246 },
+ { 851, 1309, 1223, 1243, 895, 1593, 1792, 2317, 627, 1076 },
+ { 770, 1216, 1030, 1125, 921, 981, 1629, 1131, 1049, 1646 },
+ { 626, 1469, 1456, 1081, 1489, 3278, 981, 1232, 1498, 733 },
+ { 617, 1201, 812, 1220, 1476, 1476, 1478, 970, 1228, 1488 },
+ { 1179, 1393, 1540, 999, 1243, 1503, 1916, 1925, 414, 1614 },
+ { 943, 1088, 1490, 682, 1112, 1372, 1756, 1505, 966, 966 } },
+ { { 322, 1142, 1589, 1396, 2144, 1859, 1359, 1925, 2084, 1518 },
+ { 617, 625, 1241, 1234, 2121, 1615, 1524, 1858, 1720, 1004 },
+ { 553, 851, 786, 1299, 1452, 1560, 1372, 1561, 1967, 1713 },
+ { 770, 977, 1396, 568, 1893, 1639, 1540, 2108, 1430, 1013 },
+ { 684, 1120, 1375, 982, 930, 2719, 1638, 1643, 933, 993 },
+ { 553, 1103, 996, 1356, 1361, 1005, 1507, 1761, 1184, 1268 },
+ { 419, 1247, 1537, 1554, 1817, 3606, 1026, 1666, 1829, 923 },
+ { 439, 1139, 1101, 1257, 3710, 1922, 1205, 1040, 1931, 1529 },
+ { 979, 935, 1269, 847, 1202, 1286, 1530, 1535, 827, 1036 },
+ { 516, 1378, 1569, 1110, 1798, 1798, 1198, 2199, 1543, 712 } },
+};
+
+//-----------------------------------------------------------------------------
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
diff --git a/third_party/libwebp/enc/cost.h b/third_party/libwebp/enc/cost.h
new file mode 100644
index 0000000..b80bb10
--- /dev/null
+++ b/third_party/libwebp/enc/cost.h
@@ -0,0 +1,52 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// 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"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern const uint16_t VP8LevelFixedCosts[2048]; // approximate cost per level
+extern const uint16_t VP8EntropyCost[256]; // 8bit fixed-point log(p)
+
+// Cost of coding one event with probability 'proba'.
+static inline int VP8BitCost(int bit, uint8_t proba) {
+ return !bit ? VP8EntropyCost[proba] : VP8EntropyCost[255 - proba];
+}
+
+// Cost of coding 'nb' 1's and 'total-nb' 0's using 'proba' probability.
+static inline uint64_t VP8BranchCost(uint64_t nb, uint64_t total, uint8_t proba) {
+ return nb * VP8BitCost(1, proba) + (total - nb) * VP8BitCost(0, proba);
+}
+
+// Level cost calculations
+void VP8CalculateLevelCosts(VP8Proba* const proba);
+static 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];
+
+//-----------------------------------------------------------------------------
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
+
+#endif // WEBP_ENC_COST_H_
diff --git a/third_party/libwebp/enc/dsp.c b/third_party/libwebp/enc/dsp.c
new file mode 100644
index 0000000..1365a23
--- /dev/null
+++ b/third_party/libwebp/enc/dsp.c
@@ -0,0 +1,614 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// speed-critical functions.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include "vp8enci.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+//-----------------------------------------------------------------------------
+// run-time tables (~4k)
+
+static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255]
+
+static int tables_ok = 0;
+
+static void InitTables() {
+ if (!tables_ok) {
+ int i;
+ for (i = -255; i <= 255 + 255; ++i) {
+ clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i;
+ }
+ tables_ok = 1;
+ }
+}
+
+static inline uint8_t clip_8b(int v) {
+ return (!(v & ~0xff)) ? v : v < 0 ? 0 : 255;
+}
+
+//-----------------------------------------------------------------------------
+// Transforms (Paragraph 14.4)
+
+#define STORE(x, y, v) \
+ dst[(x) + (y) * BPS] = clip_8b(ref[(x) + (y) * BPS] + ((v) >> 3))
+
+static const int kC1 = 20091 + (1 << 16);
+static const int kC2 = 35468;
+#define MUL(a, b) (((a) * (b)) >> 16)
+
+static void ITransform(const uint8_t* ref, const int16_t* in, uint8_t* dst) {
+ int C[4 * 4], *tmp;
+ int i;
+ tmp = C;
+ for (i = 0; i < 4; ++i) { // vertical pass
+ const int a = in[0] + in[8];
+ const int b = in[0] - in[8];
+ const int c = MUL(in[4], kC2) - MUL(in[12], kC1);
+ const int d = MUL(in[4], kC1) + MUL(in[12], kC2);
+ tmp[0] = a + d;
+ tmp[1] = b + c;
+ tmp[2] = b - c;
+ tmp[3] = a - d;
+ tmp += 4;
+ in++;
+ }
+
+ tmp = C;
+ for (i = 0; i < 4; ++i) { // horizontal pass
+ const int dc = tmp[0] + 4;
+ const int a = dc + tmp[8];
+ const int b = dc - tmp[8];
+ const int c = MUL(tmp[4], kC2) - MUL(tmp[12], kC1);
+ const int d = MUL(tmp[4], kC1) + MUL(tmp[12], kC2);
+ STORE(0, i, a + d);
+ STORE(1, i, b + c);
+ STORE(2, i, b - c);
+ STORE(3, i, a - d);
+ tmp++;
+ }
+}
+
+void FTransform(const uint8_t* src, const uint8_t* ref, int16_t* out) {
+ int i;
+ int tmp[16];
+ for (i = 0; i < 4; ++i, src += BPS, ref += BPS) {
+ const int d0 = src[0] - ref[0];
+ const int d1 = src[1] - ref[1];
+ const int d2 = src[2] - ref[2];
+ const int d3 = src[3] - ref[3];
+ const int a0 = (d0 + d3) << 3;
+ const int a1 = (d1 + d2) << 3;
+ const int a2 = (d1 - d2) << 3;
+ const int a3 = (d0 - d3) << 3;
+ tmp[0 + i * 4] = (a0 + a1);
+ tmp[1 + i * 4] = (a2 * 2217 + a3 * 5352 + 14500) >> 12;
+ tmp[2 + i * 4] = (a0 - a1);
+ tmp[3 + i * 4] = (a3 * 2217 - a2 * 5352 + 7500) >> 12;
+ }
+ for (i = 0; i < 4; ++i) {
+ const int a0 = (tmp[0 + i] + tmp[12 + i]);
+ const int a1 = (tmp[4 + i] + tmp[ 8 + i]);
+ const int a2 = (tmp[4 + i] - tmp[ 8 + i]);
+ const int a3 = (tmp[0 + i] - tmp[12 + i]);
+ out[0 + i] = (a0 + a1 + 7) >> 4;
+ out[4 + i] = ((a2 * 2217 + a3 * 5352 + 12000) >> 16) + (a3 != 0);
+ out[8 + i] = (a0 - a1 + 7) >> 4;
+ out[12+ i] = ((a3 * 2217 - a2 * 5352 + 51000) >> 16);
+ }
+}
+
+static void ITransformWHT(const int16_t* in, int16_t* out) {
+ int tmp[16];
+ int i;
+ for (i = 0; i < 4; ++i) {
+ const int a0 = in[0 + i] + in[12 + i];
+ const int a1 = in[4 + i] + in[ 8 + i];
+ const int a2 = in[4 + i] - in[ 8 + i];
+ const int a3 = in[0 + i] - in[12 + i];
+ tmp[0 + i] = a0 + a1;
+ tmp[8 + i] = a0 - a1;
+ tmp[4 + i] = a3 + a2;
+ tmp[12 + i] = a3 - a2;
+ }
+ for (i = 0; i < 4; ++i) {
+ const int dc = tmp[0 + i * 4] + 3; // w/ rounder
+ const int a0 = dc + tmp[3 + i * 4];
+ const int a1 = tmp[1 + i * 4] + tmp[2 + i * 4];
+ const int a2 = tmp[1 + i * 4] - tmp[2 + i * 4];
+ const int a3 = dc - tmp[3 + i * 4];
+ out[ 0] = (a0 + a1) >> 3;
+ out[16] = (a3 + a2) >> 3;
+ out[32] = (a0 - a1) >> 3;
+ out[48] = (a3 - a2) >> 3;
+ out += 64;
+ }
+}
+
+static void FTransformWHT(const int16_t* in, int16_t* out) {
+ int tmp[16];
+ int i;
+ for (i = 0; i < 4; ++i, in += 64) {
+ const int a0 = (in[0 * 16] + in[2 * 16]) << 2;
+ const int a1 = (in[1 * 16] + in[3 * 16]) << 2;
+ const int a2 = (in[1 * 16] - in[3 * 16]) << 2;
+ const int a3 = (in[0 * 16] - in[2 * 16]) << 2;
+ tmp[0 + i * 4] = (a0 + a1) + (a0 != 0);
+ tmp[1 + i * 4] = a3 + a2;
+ tmp[2 + i * 4] = a3 - a2;
+ tmp[3 + i * 4] = a0 - a1;
+ }
+ for (i = 0; i < 4; ++i) {
+ const int a0 = (tmp[0 + i] + tmp[8 + i]);
+ const int a1 = (tmp[4 + i] + tmp[12+ i]);
+ const int a2 = (tmp[4 + i] - tmp[12+ i]);
+ const int a3 = (tmp[0 + i] - tmp[8 + i]);
+ const int b0 = a0 + a1;
+ const int b1 = a3 + a2;
+ const int b2 = a3 - a2;
+ const int b3 = a0 - a1;
+ out[ 0 + i] = (b0 + (b0 > 0) + 3) >> 3;
+ out[ 4 + i] = (b1 + (b1 > 0) + 3) >> 3;
+ out[ 8 + i] = (b2 + (b2 > 0) + 3) >> 3;
+ out[12 + i] = (b3 + (b3 > 0) + 3) >> 3;
+ }
+}
+
+// default C implementations:
+VP8Idct VP8ITransform = ITransform;
+VP8Fdct VP8FTransform = FTransform;
+VP8WHT VP8ITransformWHT = ITransformWHT;
+VP8WHT VP8FTransformWHT = FTransformWHT;
+
+#undef MUL
+#undef STORE
+
+//-----------------------------------------------------------------------------
+// Intra predictions
+
+#define OUT(x, y) dst[(x) + (y) * BPS]
+
+static inline void Fill(uint8_t* dst, int value, int size) {
+ int j;
+ for (j = 0; j < size; ++j) {
+ memset(dst + j * BPS, value, size);
+ }
+}
+
+static inline void VerticalPred(uint8_t* dst, const uint8_t* top, int size) {
+ int j;
+ if (top) {
+ for (j = 0; j < size; ++j) memcpy(dst + j * BPS, top, size);
+ } else {
+ Fill(dst, 127, size);
+ }
+}
+
+static inline void HorizontalPred(uint8_t* dst, const uint8_t* left, int size) {
+ if (left) {
+ int j;
+ for (j = 0; j < size; ++j) {
+ memset(dst + j * BPS, left[j], size);
+ }
+ } else {
+ Fill(dst, 129, size);
+ }
+}
+
+static inline void TrueMotion(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top, int size) {
+ int y;
+ if (left) {
+ if (top) {
+ const uint8_t* const clip = clip1 + 255 - left[-1];
+ for (y = 0; y < size; ++y) {
+ const uint8_t* const clip_table = clip + left[y];
+ int x;
+ for (x = 0; x < size; ++x) {
+ dst[x] = clip_table[top[x]];
+ }
+ dst += BPS;
+ }
+ } else {
+ HorizontalPred(dst, left, size);
+ }
+ } else {
+ // true motion without left samples (hence: with default 129 value)
+ // is equivalent to VE prediction where you just copy the top samples.
+ // Note that if top samples are not available, the default value is
+ // then 129, and not 127 as in the VerticalPred case.
+ if (top) {
+ VerticalPred(dst, top, size);
+ } else {
+ Fill(dst, 129, size);
+ }
+ }
+}
+
+static inline void DCMode(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top,
+ int size, int round, int shift) {
+ int DC = 0;
+ int j;
+ if (top) {
+ for (j = 0; j < size; ++j) DC += top[j];
+ if (left) { // top and left present
+ for (j = 0; j < size; ++j) DC += left[j];
+ } else { // top, but no left
+ DC += DC;
+ }
+ DC = (DC + round) >> shift;
+ } else if (left) { // left but no top
+ for (j = 0; j < size; ++j) DC += left[j];
+ DC += DC;
+ DC = (DC + round) >> shift;
+ } else { // no top, no left, nothing.
+ DC = 0x80;
+ }
+ Fill(dst, DC, size);
+}
+
+//-----------------------------------------------------------------------------
+// Chroma 8x8 prediction (paragraph 12.2)
+
+static void IntraChromaPreds(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ // U block
+ DCMode(C8DC8 + dst, left, top, 8, 8, 4);
+ VerticalPred(C8VE8 + dst, top, 8);
+ HorizontalPred(C8HE8 + dst, left, 8);
+ TrueMotion(C8TM8 + dst, left, top, 8);
+ // V block
+ dst += 8;
+ if (top) top += 8;
+ if (left) left += 16;
+ DCMode(C8DC8 + dst, left, top, 8, 8, 4);
+ VerticalPred(C8VE8 + dst, top, 8);
+ HorizontalPred(C8HE8 + dst, left, 8);
+ TrueMotion(C8TM8 + dst, left, top, 8);
+}
+
+//-----------------------------------------------------------------------------
+// luma 16x16 prediction (paragraph 12.3)
+
+static void Intra16Preds(uint8_t* dst,
+ const uint8_t* left, const uint8_t* top) {
+ DCMode(I16DC16 + dst, left, top, 16, 16, 5);
+ VerticalPred(I16VE16 + dst, top, 16);
+ HorizontalPred(I16HE16 + dst, left, 16);
+ TrueMotion(I16TM16 + dst, left, top, 16);
+}
+
+//-----------------------------------------------------------------------------
+// luma 4x4 prediction
+
+#define AVG3(a, b, c) (((a) + 2 * (b) + (c) + 2) >> 2)
+#define AVG2(a, b) (((a) + (b) + 1) >> 1)
+
+static void VE4(uint8_t* dst, const uint8_t* top) { // vertical
+ const uint8_t vals[4] = {
+ AVG3(top[-1], top[0], top[1]),
+ AVG3(top[ 0], top[1], top[2]),
+ AVG3(top[ 1], top[2], top[3]),
+ AVG3(top[ 2], top[3], top[4])
+ };
+ int i;
+ for (i = 0; i < 4; ++i) {
+ memcpy(dst + i * BPS, vals, 4);
+ }
+}
+
+static void HE4(uint8_t* dst, const uint8_t* top) { // horizontal
+ const int X = top[-1];
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int L = top[-5];
+ *(uint32_t*)(dst + 0 * BPS) = 0x01010101U * AVG3(X, I, J);
+ *(uint32_t*)(dst + 1 * BPS) = 0x01010101U * AVG3(I, J, K);
+ *(uint32_t*)(dst + 2 * BPS) = 0x01010101U * AVG3(J, K, L);
+ *(uint32_t*)(dst + 3 * BPS) = 0x01010101U * AVG3(K, L, L);
+}
+
+static void DC4(uint8_t* dst, const uint8_t* top) {
+ uint32_t dc = 4;
+ int i;
+ for (i = 0; i < 4; ++i) dc += top[i] + top[-5 + i];
+ Fill(dst, dc >> 3, 4);
+}
+
+static void RD4(uint8_t* dst, const uint8_t* top) {
+ const int X = top[-1];
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int L = top[-5];
+ const int A = top[0];
+ const int B = top[1];
+ const int C = top[2];
+ const int D = top[3];
+ OUT(0, 3) = AVG3(J, K, L);
+ OUT(0, 2) = OUT(1, 3) = AVG3(I, J, K);
+ OUT(0, 1) = OUT(1, 2) = OUT(2, 3) = AVG3(X, I, J);
+ OUT(0, 0) = OUT(1, 1) = OUT(2, 2) = OUT(3, 3) = AVG3(A, X, I);
+ OUT(1, 0) = OUT(2, 1) = OUT(3, 2) = AVG3(B, A, X);
+ OUT(2, 0) = OUT(3, 1) = AVG3(C, B, A);
+ OUT(3, 0) = AVG3(D, C, B);
+}
+
+static void LD4(uint8_t* dst, const uint8_t* top) {
+ const int A = top[0];
+ const int B = top[1];
+ const int C = top[2];
+ const int D = top[3];
+ const int E = top[4];
+ const int F = top[5];
+ const int G = top[6];
+ const int H = top[7];
+ OUT(0, 0) = AVG3(A, B, C);
+ OUT(1, 0) = OUT(0, 1) = AVG3(B, C, D);
+ OUT(2, 0) = OUT(1, 1) = OUT(0, 2) = AVG3(C, D, E);
+ OUT(3, 0) = OUT(2, 1) = OUT(1, 2) = OUT(0, 3) = AVG3(D, E, F);
+ OUT(3, 1) = OUT(2, 2) = OUT(1, 3) = AVG3(E, F, G);
+ OUT(3, 2) = OUT(2, 3) = AVG3(F, G, H);
+ OUT(3, 3) = AVG3(G, H, H);
+}
+
+static void VR4(uint8_t* dst, const uint8_t* top) {
+ const int X = top[-1];
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int A = top[0];
+ const int B = top[1];
+ const int C = top[2];
+ const int D = top[3];
+ OUT(0, 0) = OUT(1, 2) = AVG2(X, A);
+ OUT(1, 0) = OUT(2, 2) = AVG2(A, B);
+ OUT(2, 0) = OUT(3, 2) = AVG2(B, C);
+ OUT(3, 0) = AVG2(C, D);
+
+ OUT(0, 3) = AVG3(K, J, I);
+ OUT(0, 2) = AVG3(J, I, X);
+ OUT(0, 1) = OUT(1, 3) = AVG3(I, X, A);
+ OUT(1, 1) = OUT(2, 3) = AVG3(X, A, B);
+ OUT(2, 1) = OUT(3, 3) = AVG3(A, B, C);
+ OUT(3, 1) = AVG3(B, C, D);
+}
+
+static void VL4(uint8_t* dst, const uint8_t* top) {
+ const int A = top[0];
+ const int B = top[1];
+ const int C = top[2];
+ const int D = top[3];
+ const int E = top[4];
+ const int F = top[5];
+ const int G = top[6];
+ const int H = top[7];
+ OUT(0, 0) = AVG2(A, B);
+ OUT(1, 0) = OUT(0, 2) = AVG2(B, C);
+ OUT(2, 0) = OUT(1, 2) = AVG2(C, D);
+ OUT(3, 0) = OUT(2, 2) = AVG2(D, E);
+
+ OUT(0, 1) = AVG3(A, B, C);
+ OUT(1, 1) = OUT(0, 3) = AVG3(B, C, D);
+ OUT(2, 1) = OUT(1, 3) = AVG3(C, D, E);
+ OUT(3, 1) = OUT(2, 3) = AVG3(D, E, F);
+ OUT(3, 2) = AVG3(E, F, G);
+ OUT(3, 3) = AVG3(F, G, H);
+}
+
+static void HU4(uint8_t* dst, const uint8_t* top) {
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int L = top[-5];
+ OUT(0, 0) = AVG2(I, J);
+ OUT(2, 0) = OUT(0, 1) = AVG2(J, K);
+ OUT(2, 1) = OUT(0, 2) = AVG2(K, L);
+ OUT(1, 0) = AVG3(I, J, K);
+ OUT(3, 0) = OUT(1, 1) = AVG3(J, K, L);
+ OUT(3, 1) = OUT(1, 2) = AVG3(K, L, L);
+ OUT(3, 2) = OUT(2, 2) =
+ OUT(0, 3) = OUT(1, 3) = OUT(2, 3) = OUT(3, 3) = L;
+}
+
+static void HD4(uint8_t* dst, const uint8_t* top) {
+ const int X = top[-1];
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int L = top[-5];
+ const int A = top[0];
+ const int B = top[1];
+ const int C = top[2];
+
+ OUT(0, 0) = OUT(2, 1) = AVG2(I, X);
+ OUT(0, 1) = OUT(2, 2) = AVG2(J, I);
+ OUT(0, 2) = OUT(2, 3) = AVG2(K, J);
+ OUT(0, 3) = AVG2(L, K);
+
+ OUT(3, 0) = AVG3(A, B, C);
+ OUT(2, 0) = AVG3(X, A, B);
+ OUT(1, 0) = OUT(3, 1) = AVG3(I, X, A);
+ OUT(1, 1) = OUT(3, 2) = AVG3(J, I, X);
+ OUT(1, 2) = OUT(3, 3) = AVG3(K, J, I);
+ OUT(1, 3) = AVG3(L, K, J);
+}
+
+static void TM4(uint8_t* dst, const uint8_t* top) {
+ int x, y;
+ const uint8_t* const clip = clip1 + 255 - top[-1];
+ for (y = 0; y < 4; ++y) {
+ const uint8_t* const clip_table = clip + top[-2 - y];
+ for (x = 0; x < 4; ++x) {
+ dst[x] = clip_table[top[x]];
+ }
+ dst += BPS;
+ }
+}
+
+#undef AVG3
+#undef AVG2
+
+// Left samples are top[-5 .. -2], top_left is top[-1], top are
+// located at top[0..3], and top right is top[4..7]
+static void Intra4Preds(uint8_t* dst, const uint8_t* top) {
+ DC4(I4DC4 + dst, top);
+ TM4(I4TM4 + dst, top);
+ VE4(I4VE4 + dst, top);
+ HE4(I4HE4 + dst, top);
+ RD4(I4RD4 + dst, top);
+ VR4(I4VR4 + dst, top);
+ LD4(I4LD4 + dst, top);
+ VL4(I4VL4 + dst, top);
+ HD4(I4HD4 + dst, top);
+ HU4(I4HU4 + dst, top);
+}
+
+// default C implementations
+VP8Intra4Preds VP8EncPredLuma4 = Intra4Preds;
+VP8IntraPreds VP8EncPredLuma16 = Intra16Preds;
+VP8IntraPreds VP8EncPredChroma8 = IntraChromaPreds;
+
+//-----------------------------------------------------------------------------
+// Metric
+
+static inline int GetSSE(const uint8_t* a, const uint8_t* b, int w, int h) {
+ int count = 0;
+ int y, x;
+ for (y = 0; y < h; ++y) {
+ for (x = 0; x < w; ++x) {
+ const int diff = (int)a[x] - b[x];
+ count += diff * diff;
+ }
+ a += BPS;
+ b += BPS;
+ }
+ return count;
+}
+
+static int SSE16x16(const uint8_t* a, const uint8_t* b) {
+ return GetSSE(a, b, 16, 16);
+}
+static int SSE16x8(const uint8_t* a, const uint8_t* b) {
+ return GetSSE(a, b, 16, 8);
+}
+static int SSE8x8(const uint8_t* a, const uint8_t* b) {
+ return GetSSE(a, b, 8, 8);
+}
+static int SSE4x4(const uint8_t* a, const uint8_t* b) {
+ return GetSSE(a, b, 4, 4);
+}
+
+// default C implementations
+VP8Metric VP8SSE16x16 = SSE16x16;
+VP8Metric VP8SSE8x8 = SSE8x8;
+VP8Metric VP8SSE16x8 = SSE16x8;
+VP8Metric VP8SSE4x4 = SSE4x4;
+
+//-----------------------------------------------------------------------------
+// Texture distortion
+//
+// We try to match the spectral content (weighted) between source and
+// reconstructed samples.
+
+// Hadamard transform
+static void TTransform(const uint8_t* in, int16_t* out) {
+ int tmp[16];
+ int i;
+ for (i = 0; i < 4; ++i, in += BPS) {
+ const int a0 = (in[0] + in[2]) << 2;
+ const int a1 = (in[1] + in[3]) << 2;
+ const int a2 = (in[1] - in[3]) << 2;
+ const int a3 = (in[0] - in[2]) << 2;
+ tmp[0 + i * 4] = a0 + a1 + (a0 != 0);
+ tmp[1 + i * 4] = a3 + a2;
+ tmp[2 + i * 4] = a3 - a2;
+ tmp[3 + i * 4] = a0 - a1;
+ }
+ for (i = 0; i < 4; ++i) {
+ const int a0 = (tmp[0 + i] + tmp[8 + i]);
+ const int a1 = (tmp[4 + i] + tmp[12+ i]);
+ const int a2 = (tmp[4 + i] - tmp[12+ i]);
+ const int a3 = (tmp[0 + i] - tmp[8 + i]);
+ const int b0 = a0 + a1;
+ const int b1 = a3 + a2;
+ const int b2 = a3 - a2;
+ const int b3 = a0 - a1;
+ out[ 0 + i] = (b0 + (b0 < 0) + 3) >> 3;
+ out[ 4 + i] = (b1 + (b1 < 0) + 3) >> 3;
+ out[ 8 + i] = (b2 + (b2 < 0) + 3) >> 3;
+ out[12 + i] = (b3 + (b3 < 0) + 3) >> 3;
+ }
+}
+
+static int Disto4x4(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ int16_t tmp1[16], tmp2[16];
+ int k;
+ int D;
+ TTransform(a, tmp1);
+ TTransform(b, tmp2);
+ D = 0;
+ for (k = 0; k < 16; ++k)
+ D += w[k] * (abs(tmp2[k]) - abs(tmp1[k]));
+ return (abs(D) + 8) >> 4;
+}
+
+static int Disto16x16(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ int D = 0;
+ int x, y;
+ for (y = 0; y < 16 * BPS; y += 4 * BPS) {
+ for (x = 0; x < 16; x += 4) {
+ D += Disto4x4(a + x + y, b + x + y, w);
+ }
+ }
+ return D;
+}
+
+VP8WMetric VP8TDisto4x4 = Disto4x4;
+VP8WMetric VP8TDisto16x16 = Disto16x16;
+
+//-----------------------------------------------------------------------------
+// Block copy
+
+static inline void Copy(const uint8_t* src, uint8_t* dst, int size) {
+ int y;
+ for (y = 0; y < size; ++y) {
+ memcpy(dst, src, size);
+ src += BPS;
+ dst += BPS;
+ }
+}
+
+static void Copy4x4(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 4); }
+static void Copy8x8(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 8); }
+static void Copy16x16(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 16); }
+
+// default C implementations
+VP8BlockCopy VP8Copy4x4 = Copy4x4;
+VP8BlockCopy VP8Copy8x8 = Copy8x8;
+VP8BlockCopy VP8Copy16x16 = Copy16x16;
+
+//-----------------------------------------------------------------------------
+
+void VP8EncDspInit() {
+ InitTables();
+ // later we'll plug some SSE2 variant here
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
diff --git a/third_party/libwebp/enc/filter.c b/third_party/libwebp/enc/filter.c
new file mode 100644
index 0000000..5a243b1
--- /dev/null
+++ b/third_party/libwebp/enc/filter.c
@@ -0,0 +1,378 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// Selecting filter level
+//
+// Author: somnath@google.com (Somnath Banerjee)
+
+#include <math.h>
+#include "vp8enci.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+// 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() {
+ 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 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 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 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 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 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 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 };
+typedef struct {
+ double w, xm, ym, xxm, xym, yym;
+} SSIMStats;
+
+static void Accumulate(const uint8_t* src1, int stride1,
+ const uint8_t* src2, int stride2,
+ int xo, int yo, int W, int H,
+ SSIMStats* 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;
+ }
+ }
+}
+
+static double GetSSIM(const SSIMStats* 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 : 0.;
+}
+
+static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) {
+ int x, y;
+ SSIMStats 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++) {
+ Accumulate(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++) {
+ Accumulate(yuv1 + U_OFF, BPS, yuv2 + U_OFF, BPS, x, y, 8, 8, &s);
+ Accumulate(yuv1 + V_OFF, BPS, yuv2 + V_OFF, BPS, x, y, 8, 8, &s);
+ }
+ }
+ return GetSSIM(&s);
+}
+
+//-----------------------------------------------------------------------------
+// Exposed APIs: Encoder should call the following 3 functions to adjust
+// loop filter strength
+
+void VP8InitFilter(VP8EncIterator* const it) {
+ int s, i;
+ if (!it->lf_stats_) return;
+
+ 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;
+ const int s = it->mb_->segment_;
+ const int level0 = it->enc_->dqm_[s].fstrength_; // TODO: ref_lf_delta[]
+
+ // explore +/-quant range of values around level0
+ const int delta_min = -it->enc_->dqm_[s].quant_;
+ const int delta_max = it->enc_->dqm_[s].quant_;
+ const int step_size = (delta_max - delta_min >= 4) ? 4 : 1;
+
+ if (!it->lf_stats_) 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) {
+ int s;
+ VP8Encoder* const enc = it->enc_;
+
+ if (!it->lf_stats_) {
+ return;
+ }
+ 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;
+ }
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
diff --git a/third_party/libwebp/enc/frame.c b/third_party/libwebp/enc/frame.c
new file mode 100644
index 0000000..9864c1d
--- /dev/null
+++ b/third_party/libwebp/enc/frame.c
@@ -0,0 +1,695 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// frame coding and analysis
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+
+#include "vp8enci.h"
+#include "cost.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#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;
+
+//-----------------------------------------------------------------------------
+// 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
+};
+
+static const uint8_t kCat3[] = { 173, 148, 140 };
+static const uint8_t kCat4[] = { 176, 155, 140, 135 };
+static const uint8_t kCat5[] = { 180, 157, 141, 134, 130 };
+static const uint8_t kCat6[] =
+ { 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, int precalc_cost) {
+ VP8Proba* const proba = &enc->proba_;
+ if (precalc_cost) VP8CalculateLevelCosts(proba);
+ proba->nb_skip_ = 0;
+ proba->nb_i4_ = 0;
+ proba->nb_i16_ = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Skip decision probability
+
+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_ < 250);
+ 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, uint64_t* const stats) {
+ stats[0] += bit;
+ stats[1] += 1;
+ return bit;
+}
+
+// Simulate block coding, but only record statistics.
+// Note: no need to record the fixed probas.
+static int RecordCoeffs(int ctx, VP8Residual* res) {
+ int n = res->first;
+ uint64_t (*s)[2] = res->stats[VP8EncBands[n]][ctx];
+ if (!Record(res->last >= 0, s[0])) {
+ return 0;
+ }
+
+ while (1) {
+ const int v = abs(res->coeffs[n++]);
+ if (!Record(v != 0, s[1])) {
+ s = res->stats[VP8EncBands[n]][0];
+ continue;
+ }
+ if (!Record(v > 1, s[2])) {
+ s = res->stats[VP8EncBands[n]][1];
+ } else {
+ 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]);
+ }
+ s = res->stats[VP8EncBands[n]][2];
+ }
+ if (n == 16 || !Record(n <= res->last, 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(uint64_t nb, uint64_t total) {
+ return (int)(nb ? ((total - nb) * 255 + total / 2) / total : 255);
+}
+
+static int FinalizeTokenProbas(VP8Encoder* const enc) {
+ VP8Proba* const proba = &enc->proba_;
+ 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 uint64_t* const cnt = proba->stats_[t][b][c][p];
+ const int update_proba = VP8CoeffsUpdateProba[t][b][c][p];
+ const int old_p = VP8CoeffsProba0[t][b][c][p];
+ const int new_p = CalcTokenProba(cnt[0], cnt[1]);
+ const uint64_t old_cost = VP8BranchCost(cnt[0], cnt[1], old_p)
+ + VP8BitCost(0, update_proba);
+ const uint64_t new_cost = VP8BranchCost(cnt[0], cnt[1], 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;
+ size += 8 * 256;
+ } else {
+ proba->coeffs_[t][b][c][p] = old_p;
+ }
+ }
+ }
+ }
+ }
+ return size;
+}
+
+//-----------------------------------------------------------------------------
+// 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 ctx, const VP8Residual* const res) {
+ int n = res->first;
+ const uint8_t* p = res->prob[VP8EncBands[n]][ctx];
+ const uint16_t *t = res->cost[VP8EncBands[n]][ctx];
+ int cost;
+
+ cost = VP8BitCost(res->last >= 0, p[0]);
+ if (res->last < 0) {
+ return cost;
+ }
+ while (n <= res->last) {
+ const int v = abs(res->coeffs[n++]);
+ cost += VP8LevelCost(t, v);
+ if (v == 0) {
+ p = res->prob[VP8EncBands[n]][0];
+ t = res->cost[VP8EncBands[n]][0];
+ continue;
+ } else if (v == 1) {
+ p = res->prob[VP8EncBands[n]][1];
+ t = res->cost[VP8EncBands[n]][1];
+ } else {
+ p = res->prob[VP8EncBands[n]][2];
+ t = res->cost[VP8EncBands[n]][2];
+ }
+ if (n < 16) {
+ cost += VP8BitCost(n <= res->last, p[0]);
+ }
+ }
+ return cost;
+}
+
+int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]) {
+ const int x = (it->i4_ & 3), y = (it->i4_ >> 2);
+ VP8Residual res;
+ int R = 0;
+ int ctx;
+
+ InitResidual(0, 3, it->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;
+ int x, y;
+ int R = 0;
+
+ VP8IteratorNzToBytes(it); // re-import the non-zero context
+
+ // DC
+ InitResidual(0, 1, it->enc_, &res);
+ SetResidualCoeffs(rd->y_dc_levels, &res);
+ R += GetResidualCost(it->top_nz_[8] + it->left_nz_[8], &res);
+
+ // AC
+ InitResidual(1, 0, it->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;
+ int ch, x, y;
+ int R = 0;
+
+ VP8IteratorNzToBytes(it); // re-import the non-zero context
+
+ InitResidual(0, 2, it->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;
+ const uint8_t* p = res->prob[VP8EncBands[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)) { // kCat3 (3b)
+ VP8PutBit(bw, 0, p[8]);
+ VP8PutBit(bw, 0, p[9]);
+ v -= 3 + (8 << 0);
+ mask = 1 << 2;
+ tab = kCat3;
+ } else if (v < 3 + (8 << 2)) { // kCat4 (4b)
+ VP8PutBit(bw, 0, p[8]);
+ VP8PutBit(bw, 1, p[9]);
+ v -= 3 + (8 << 1);
+ mask = 1 << 3;
+ tab = kCat4;
+ } else if (v < 3 + (8 << 3)) { // kCat5 (5b)
+ VP8PutBit(bw, 1, p[8]);
+ VP8PutBit(bw, 0, p[10]);
+ v -= 3 + (8 << 2);
+ mask = 1 << 4;
+ tab = kCat5;
+ } else { // kCat6 (11b)
+ VP8PutBit(bw, 1, p[8]);
+ VP8PutBit(bw, 1, p[10]);
+ v -= 3 + (8 << 3);
+ mask = 1 << 10;
+ tab = kCat6;
+ }
+ 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_;
+
+ VP8IteratorNzToBytes(it);
+
+ pos1 = VP8BitWriterPos(bw);
+ if (i16) {
+ InitResidual(0, 1, it->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, it->enc_, &res);
+ } else {
+ InitResidual(0, 3, it->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, it->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;
+
+ VP8IteratorNzToBytes(it);
+
+ if (it->mb_->type_ == 1) { // i16x16
+ InitResidual(0, 1, it->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, it->enc_, &res);
+ } else {
+ InitResidual(0, 3, it->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, it->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);
+}
+
+//-----------------------------------------------------------------------------
+// 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) {
+ memset(enc->sse_, 0, sizeof(enc->sse_));
+ 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) {
+ 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) {
+ 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;
+ }
+ 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
+}
+
+//-----------------------------------------------------------------------------
+// Main loops
+//
+// 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) {
+ int i, s, p;
+ VP8EncIterator it;
+ VP8ModeScore info;
+ const int dont_use_skip = !enc->proba_.use_skip_proba_;
+ const int rd_opt = enc->rd_opt_level_;
+ const int kAverageBytesPerMB = 5; // TODO: have a kTable[quality/10]
+ const int bytes_per_parts =
+ enc->mb_w_ * enc->mb_h_ * kAverageBytesPerMB / enc->num_parts_;
+
+ // Initialize the bit-writers
+ for (p = 0; p < enc->num_parts_; ++p) {
+ VP8BitWriterInit(enc->parts_ + p, bytes_per_parts);
+ }
+
+ ResetStats(enc, rd_opt != 0);
+ ResetSSE(enc);
+
+ VP8IteratorInit(enc, &it);
+ VP8InitFilter(&it);
+ do {
+ VP8IteratorImport(&it);
+ // 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);
+ }
+ StoreSideInfo(&it);
+ VP8StoreFilterStats(&it);
+ VP8IteratorExport(&it);
+ } while (VP8IteratorNext(&it, it.yuv_out_));
+ VP8AdjustFilterStrength(&it);
+
+ // Finalize the partitions
+ for (p = 0; p < enc->num_parts_; ++p) {
+ VP8BitWriterFinish(enc->parts_ + p);
+ }
+ // and byte counters
+ if (enc->pic_->stats) {
+ 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);
+ }
+ }
+ }
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// VP8StatLoop(): 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, PNSR)
+// was specified.
+
+#define kHeaderSizeEstimate (15 + 20 + 10) // TODO: fix better
+
+static int OneStatPass(VP8Encoder* const enc, float q, int rd_opt, int nb_mbs,
+ float* const PSNR) {
+ VP8EncIterator it;
+ uint64_t size = 0;
+ uint64_t distortion = 0;
+ const uint64_t pixel_count = nb_mbs * 384;
+
+ // Make sure the quality parameter is inside valid bounds
+ if (q < 0.) {
+ q = 0;
+ } else if (q > 100.) {
+ q = 100;
+ }
+
+ VP8SetSegmentParams(enc, q); // setup segment quantizations and filters
+
+ ResetStats(enc, rd_opt != 0);
+ ResetTokenStats(enc);
+
+ VP8IteratorInit(enc, &it);
+ do {
+ VP8ModeScore info;
+ VP8IteratorImport(&it);
+ 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;
+ distortion += info.D;
+ } while (VP8IteratorNext(&it, it.yuv_out_) && --nb_mbs > 0);
+ size += FinalizeSkipProba(enc);
+ size += FinalizeTokenProbas(enc);
+ size += enc->segment_hdr_.size_;
+ size = ((size + 1024) >> 11) + kHeaderSizeEstimate;
+
+ if (PSNR) {
+ *PSNR = (float)(10.* log10(255. * 255. * pixel_count / distortion));
+ }
+ return (int)size;
+}
+
+// successive refinement increments.
+static const int dqs[] = { 20, 15, 10, 8, 6, 4, 2, 1, 0 };
+
+int VP8StatLoop(VP8Encoder* const enc) {
+ const int do_search =
+ (enc->config_->target_size > 0 || enc->config_->target_PSNR > 0);
+ const int fast_probe = (enc->method_ < 2 && !do_search);
+ float q = enc->config_->quality;
+ int pass;
+ int nb_mbs;
+
+ // Fast mode: quick analysis pass over few mbs. Better than nothing.
+ nb_mbs = enc->mb_w_ * enc->mb_h_;
+ if (fast_probe && nb_mbs > 100) nb_mbs = 100;
+
+ // No target size: just do several pass without changing 'q'
+ if (!do_search) {
+ for (pass = 0; pass < enc->config_->pass; ++pass) {
+ const int rd_opt = (enc->method_ > 2);
+ OneStatPass(enc, q, rd_opt, nb_mbs, NULL);
+ }
+ return 1;
+ }
+
+ // binary search for a size close to target
+ for (pass = 0; pass < enc->config_->pass || (dqs[pass] > 0); ++pass) {
+ const int rd_opt = 1;
+ float PSNR;
+ int criterion;
+ const int size = OneStatPass(enc, q, rd_opt, nb_mbs, &PSNR);
+#if DEBUG_SEARCH
+ printf("#%d size=%d PSNR=%.2f q=%.2f\n", pass, size, PSNR, q);
+#endif
+
+ if (enc->config_->target_PSNR > 0) {
+ criterion = (PSNR < enc->config_->target_PSNR);
+ } else {
+ criterion = (size < enc->config_->target_size);
+ }
+ // dichotomize
+ if (criterion) {
+ q += dqs[pass];
+ } else {
+ q -= dqs[pass];
+ }
+ }
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
diff --git a/third_party/libwebp/enc/iterator.c b/third_party/libwebp/enc/iterator.c
new file mode 100644
index 0000000..7cd9f06
--- /dev/null
+++ b/third_party/libwebp/enc/iterator.c
@@ -0,0 +1,406 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// VP8Iterator: block iterator
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <stdlib.h>
+#include <string.h>
+#include "vp8enci.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+//-----------------------------------------------------------------------------
+// VP8Iterator
+//-----------------------------------------------------------------------------
+
+static void InitLeft(VP8EncIterator* const it) {
+ const VP8Encoder* const enc = it->enc_;
+ enc->y_left_[-1] = enc->u_left_[-1] = enc->v_left_[-1] =
+ (it->y_) > 0 ? 129 : 127;
+ memset(enc->y_left_, 129, 16);
+ memset(enc->u_left_, 129, 8);
+ memset(enc->v_left_, 129, 8);
+ it->left_nz_[8] = 0;
+}
+
+static void InitTop(VP8EncIterator* const it) {
+ const VP8Encoder* const enc = it->enc_;
+ const int 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 VP8IteratorReset(VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+ it->x_ = 0;
+ it->y_ = 0;
+ it->y_offset_ = 0;
+ it->uv_offset_ = 0;
+ it->mb_ = enc->mb_info_;
+ it->preds_ = enc->preds_;
+ it->nz_ = enc->nz_;
+ it->bw_ = &enc->parts_[0];
+ it->done_ = enc->mb_w_* enc->mb_h_;
+ InitTop(it);
+ InitLeft(it);
+ memset(it->bit_count_, 0, sizeof(it->bit_count_));
+ it->do_trellis_ = 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;
+ // TODO(later): for multithreading, these should be owned by 'it'.
+ it->yuv_in_ = enc->yuv_in_;
+ it->yuv_out_ = enc->yuv_out_;
+ it->yuv_out2_ = enc->yuv_out2_;
+ it->yuv_p_ = enc->yuv_p_;
+ it->lf_stats_ = enc->lf_stats_;
+ VP8IteratorReset(it);
+}
+
+//-----------------------------------------------------------------------------
+// Import the source samples into the cache. Takes care of replicating
+// boundary pixels if necessary.
+
+void VP8IteratorImport(const VP8EncIterator* const it) {
+ const VP8Encoder* const enc = it->enc_;
+ const int x = it->x_, y = it->y_;
+ const WebPPicture* const pic = enc->pic_;
+ const uint8_t* ysrc = pic->y + (y * pic->y_stride + x) * 16;
+ const uint8_t* usrc = pic->u + (y * pic->uv_stride + x) * 8;
+ const uint8_t* vsrc = pic->v + (y * pic->uv_stride + x) * 8;
+ uint8_t* ydst = it->yuv_in_ + Y_OFF;
+ uint8_t* udst = it->yuv_in_ + U_OFF;
+ uint8_t* vdst = it->yuv_in_ + V_OFF;
+ int w = (pic->width - x * 16);
+ int h = (pic->height - y * 16);
+ int i;
+
+ if (w > 16) w = 16;
+ if (h > 16) h = 16;
+ // Luma plane
+ for (i = 0; i < h; ++i) {
+ memcpy(ydst, ysrc, w);
+ if (w < 16) memset(ydst + w, ydst[w - 1], 16 - w);
+ ydst += BPS;
+ ysrc += pic->y_stride;
+ }
+ for (i = h; i < 16; ++i) {
+ memcpy(ydst, ydst - BPS, 16);
+ ydst += BPS;
+ }
+ // U/V plane
+ w = (w + 1) / 2;
+ h = (h + 1) / 2;
+ for (i = 0; i < h; ++i) {
+ memcpy(udst, usrc, w);
+ memcpy(vdst, vsrc, w);
+ if (w < 8) {
+ memset(udst + w, udst[w - 1], 8 - w);
+ memset(vdst + w, vdst[w - 1], 8 - w);
+ }
+ udst += BPS;
+ vdst += BPS;
+ usrc += pic->uv_stride;
+ vsrc += pic->uv_stride;
+ }
+ for (i = h; i < 8; ++i) {
+ memcpy(udst, udst - BPS, 8);
+ memcpy(vdst, vdst - BPS, 8);
+ udst += BPS;
+ vdst += BPS;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Copy back the compressed samples into user space if requested.
+
+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* ydst = pic->y + (y * pic->y_stride + x) * 16;
+ uint8_t* udst = pic->u + (y * pic->uv_stride + x) * 8;
+ uint8_t* vdst = pic->v + (y * pic->uv_stride + x) * 8;
+ int w = (pic->width - x * 16);
+ int h = (pic->height - y * 16);
+ int i;
+
+ if (w > 16) w = 16;
+ if (h > 16) h = 16;
+
+ // Luma plane
+ for (i = 0; i < h; ++i) {
+ memcpy(ydst + i * pic->y_stride, ysrc + i * BPS, w);
+ }
+ // U/V plane
+ w = (w + 1) / 2;
+ h = (h + 1) / 2;
+ for (i = 0; i < h; ++i) {
+ memcpy(udst + i * pic->uv_stride, usrc + i * BPS, w);
+ memcpy(vdst + i * pic->uv_stride, vsrc + i * BPS, w);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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];
+
+ // Top-Y
+ it->top_nz_[0] = BIT(tnz, 12);
+ it->top_nz_[1] = BIT(tnz, 13);
+ it->top_nz_[2] = BIT(tnz, 14);
+ it->top_nz_[3] = BIT(tnz, 15);
+ // Top-U
+ it->top_nz_[4] = BIT(tnz, 18);
+ it->top_nz_[5] = BIT(tnz, 19);
+ // Top-V
+ it->top_nz_[6] = BIT(tnz, 22);
+ it->top_nz_[7] = BIT(tnz, 23);
+ // DC
+ it->top_nz_[8] = BIT(tnz, 24);
+
+ // left-Y
+ it->left_nz_[0] = BIT(lnz, 3);
+ it->left_nz_[1] = BIT(lnz, 7);
+ it->left_nz_[2] = BIT(lnz, 11);
+ it->left_nz_[3] = BIT(lnz, 15);
+ // left-U
+ it->left_nz_[4] = BIT(lnz, 17);
+ it->left_nz_[5] = BIT(lnz, 19);
+ // left-V
+ it->left_nz_[6] = BIT(lnz, 21);
+ it->left_nz_[7] = BIT(lnz, 23);
+ // left-DC is special, iterated separately
+}
+
+void VP8IteratorBytesToNz(VP8EncIterator* const it) {
+ uint32_t nz = 0;
+ // top
+ nz |= (it->top_nz_[0] << 12) | (it->top_nz_[1] << 13);
+ nz |= (it->top_nz_[2] << 14) | (it->top_nz_[3] << 15);
+ nz |= (it->top_nz_[4] << 18) | (it->top_nz_[5] << 19);
+ nz |= (it->top_nz_[6] << 22) | (it->top_nz_[7] << 23);
+ nz |= (it->top_nz_[8] << 24); // we propagate the _top_ bit, esp. for intra4
+ // left
+ nz |= (it->left_nz_[0] << 3) | (it->left_nz_[1] << 7) | (it->left_nz_[2] << 11);
+ nz |= (it->left_nz_[4] << 17) | (it->left_nz_[6] << 21);
+
+ *it->nz_ = nz;
+}
+
+#undef BIT
+
+//-----------------------------------------------------------------------------
+// Advance to the next position, doing the bookeeping.
+
+int VP8IteratorNext(VP8EncIterator* const it,
+ const uint8_t* const block_to_save) {
+ VP8Encoder* const enc = it->enc_;
+ if (block_to_save) {
+ const int x = it->x_, y = it->y_;
+ const uint8_t* const ysrc = block_to_save + Y_OFF;
+ const uint8_t* const usrc = block_to_save + U_OFF;
+ if (x < enc->mb_w_ - 1) { // left
+ int i;
+ for (i = 0; i < 16; ++i) {
+ enc->y_left_[i] = ysrc[15 + i * BPS];
+ }
+ for (i = 0; i < 8; ++i) {
+ enc->u_left_[i] = usrc[7 + i * BPS];
+ enc->v_left_[i] = usrc[15 + i * BPS];
+ }
+ // top-left (before 'top'!)
+ enc->y_left_[-1] = enc->y_top_[x * 16 + 15];
+ enc->u_left_[-1] = enc->uv_top_[x * 16 + 0 + 7];
+ enc->v_left_[-1] = enc->uv_top_[x * 16 + 8 + 7];
+ }
+ if (y < enc->mb_h_ - 1) { // top
+ memcpy(enc->y_top_ + x * 16, ysrc + 15 * BPS, 16);
+ memcpy(enc->uv_top_ + x * 16, usrc + 7 * BPS, 8 + 8);
+ }
+ }
+
+ it->mb_++;
+ it->preds_ += 4;
+ it->nz_++;
+ it->x_++;
+ if (it->x_ == enc->mb_w_) {
+ it->x_ = 0;
+ it->y_++;
+ it->bw_ = &enc->parts_[it->y_ & (enc->num_parts_ - 1)];
+ it->preds_ = enc->preds_ + it->y_ * 4 * enc->preds_w_;
+ it->nz_ = enc->nz_;
+ InitLeft(it);
+ }
+ return (0 < --it->done_);
+}
+
+//-----------------------------------------------------------------------------
+// Helper function to set mode properties
+
+void VP8SetIntra16Mode(const VP8EncIterator* it, int mode) {
+ int y;
+ uint8_t* preds = it->preds_;
+ 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, int modes[16]) {
+ int x, y;
+ uint8_t* preds = it->preds_;
+ for (y = 0; y < 4; ++y) {
+ for (x = 0; x < 4; ++x) {
+ preds[x] = modes[x + y * 4];
+ }
+ preds += it->enc_->preds_w_;
+ }
+ 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) {
+ 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] = enc->y_left_[15 - i];
+ }
+ for (i = 0; i < 16; ++i) { // top
+ it->i4_boundary_[17 + i] = enc->y_top_[it->x_ * 16 + 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] = enc->y_top_[it->x_ * 16 + 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;
+}
+
+//-----------------------------------------------------------------------------
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
diff --git a/third_party/libwebp/enc/picture.c b/third_party/libwebp/enc/picture.c
new file mode 100644
index 0000000..5b8d98b
--- /dev/null
+++ b/third_party/libwebp/enc/picture.c
@@ -0,0 +1,316 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// WebPPicture utils: colorspace conversion, crop, ...
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <stdlib.h>
+#include "vp8enci.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+//-----------------------------------------------------------------------------
+// WebPPicture
+//-----------------------------------------------------------------------------
+
+int WebPPictureAlloc(WebPPicture* const picture) {
+ if (picture) {
+ const int width = picture->width;
+ const int height = picture->height;
+ const int uv_width = (width + 1) / 2;
+ const int uv_height = (height + 1) / 2;
+ const uint64_t y_size = (uint64_t)width * height;
+ const uint64_t uv_size = (uint64_t)uv_width * uv_height;
+ const uint64_t total_size = y_size + 2 * uv_size;
+ // Security and validation checks
+ if (uv_width <= 0 || uv_height <= 0 || // check param error
+ y_size >= (1ULL << 40) || // check for reasonable global size
+ (size_t)total_size != total_size) { // check for overflow on 32bit
+ return 0;
+ }
+ picture->y_stride = width;
+ picture->uv_stride = uv_width;
+ WebPPictureFree(picture); // erase previous buffer
+ picture->y = (uint8_t*)malloc(total_size);
+ if (picture->y == NULL) return 0;
+ picture->u = picture->y + y_size;
+ picture->v = picture->u + uv_size;
+ }
+ return 1;
+}
+
+void WebPPictureFree(WebPPicture* const picture) {
+ if (picture) {
+ free(picture->y);
+ picture->y = picture->u = picture->v = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst) {
+ int y;
+ if (src == NULL || dst == NULL) return 0;
+ if (src == dst) return 1;
+ *dst = *src;
+ dst->y = NULL;
+ if (!WebPPictureAlloc(dst)) return 0;
+ for (y = 0; y < dst->height; ++y) {
+ memcpy(dst->y + y * dst->y_stride, src->y + y * src->y_stride, src->width);
+ }
+ for (y = 0; y < (dst->height + 1) / 2; ++y) {
+ memcpy(dst->u + y * dst->uv_stride,
+ src->u + y * src->uv_stride, (src->width + 1) / 2);
+ memcpy(dst->v + y * dst->uv_stride,
+ src->v + y * src->uv_stride, (src->width + 1) / 2);
+ }
+ return 1;
+}
+
+int WebPPictureCrop(WebPPicture* const pic,
+ int left, int top, int width, int height) {
+ WebPPicture tmp;
+ int y;
+
+ if (pic == NULL) return 0;
+ if (width <= 0 || height <= 0) return 0;
+ if (left < 0 || ((left + width + 1) & ~1) > pic->width) return 0;
+ if (top < 0 || ((top + height + 1) & ~1) > pic->height) return 0;
+
+ tmp = *pic;
+ tmp.y = NULL;
+ tmp.width = width;
+ tmp.height = height;
+ if (!WebPPictureAlloc(&tmp)) return 0;
+
+ for (y = 0; y < height; ++y) {
+ memcpy(tmp.y + y * tmp.y_stride,
+ pic->y + (top + y) * pic->y_stride + left, width);
+ }
+ for (y = 0; y < (height + 1) / 2; ++y) {
+ const int offset = (y + top / 2) * pic->uv_stride + left / 2;
+ memcpy(tmp.u + y * tmp.uv_stride, pic->u + offset, (width + 1) / 2);
+ memcpy(tmp.v + y * tmp.uv_stride, pic->v + offset, (width + 1) / 2);
+ }
+ WebPPictureFree(pic);
+ *pic = tmp;
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Write-to-memory
+
+typedef struct {
+ uint8_t** mem;
+ size_t max_size;
+ size_t* size;
+} WebPMemoryWriter;
+
+static void InitMemoryWriter(WebPMemoryWriter* const writer) {
+ *writer->mem = NULL;
+ *writer->size = 0;
+ writer->max_size = 0;
+}
+
+static int WebPMemoryWrite(const uint8_t* data, size_t data_size,
+ const WebPPicture* const picture) {
+ WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr;
+ size_t next_size;
+ if (w == NULL) {
+ return 1;
+ }
+ next_size = (*w->size) + data_size;
+ if (next_size > w->max_size) {
+ uint8_t* new_mem;
+ size_t next_max_size = w->max_size * 2;
+ if (next_max_size < next_size) next_max_size = next_size;
+ if (next_max_size < 8192) next_max_size = 8192;
+ new_mem = (uint8_t*)malloc(next_max_size);
+ if (new_mem == NULL) {
+ return 0;
+ }
+ if ((*w->size) > 0) {
+ memcpy(new_mem, *w->mem, *w->size);
+ }
+ free(*w->mem);
+ *w->mem = new_mem;
+ w->max_size = next_max_size;
+ }
+ if (data_size) {
+ memcpy((*w->mem) + (*w->size), data, data_size);
+ *w->size += data_size;
+ }
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// RGB -> YUV conversion
+// The exact naming is Y'CbCr, following the ITU-R BT.601 standard.
+// More information at: http://en.wikipedia.org/wiki/YCbCr
+// Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16
+// U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128
+// V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128
+// We use 16bit fixed point operations.
+
+enum { YUV_FRAC = 16 };
+
+static inline int clip_uv(int v) {
+ v = (v + (257 << (YUV_FRAC + 2 - 1))) >> (YUV_FRAC + 2);
+ return ((v & ~0xff) == 0) ? v : (v < 0) ? 0u : 255u;
+}
+
+static inline int rgb_to_y(int r, int g, int b) {
+ const int kRound = (1 << (YUV_FRAC - 1)) + (16 << YUV_FRAC);
+ const int luma = 16839 * r + 33059 * g + 6420 * b;
+ return (luma + kRound) >> YUV_FRAC; // no need to clip
+}
+
+static inline int rgb_to_u(int r, int g, int b) {
+ return clip_uv(-9719 * r - 19081 * g + 28800 * b);
+}
+
+static inline int rgb_to_v(int r, int g, int b) {
+ return clip_uv(+28800 * r - 24116 * g - 4684 * b);
+}
+
+// TODO: we can do better than simply 2x2 averaging on U/V samples.
+#define SUM4(ptr) ((ptr)[0] + (ptr)[step] + \
+ (ptr)[rgb_stride] + (ptr)[rgb_stride + step])
+#define SUM2H(ptr) (2 * (ptr)[0] + 2 * (ptr)[step])
+#define SUM2V(ptr) (2 * (ptr)[0] + 2 * (ptr)[rgb_stride])
+#define SUM1(ptr) (4 * (ptr)[0])
+#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] = rgb_to_u(r, g, b); \
+ picture->v[dst] = rgb_to_v(r, g, b); \
+}
+
+static int Import(WebPPicture* const picture,
+ const uint8_t* const rgb, int rgb_stride,
+ int step, int swap) {
+ int x, y;
+ const uint8_t* const r_ptr = rgb + (swap ? 2 : 0);
+ const uint8_t* const g_ptr = rgb + 1;
+ const uint8_t* const b_ptr = rgb + (swap ? 0 : 2);
+
+ for (y = 0; y < picture->height; ++y) {
+ for (x = 0; x < picture->width; ++x) {
+ const int offset = step * x + y * rgb_stride;
+ picture->y[x + y * picture->y_stride] =
+ rgb_to_y(r_ptr[offset], g_ptr[offset], b_ptr[offset]);
+ }
+ }
+ for (y = 0; y < (picture->height >> 1); ++y) {
+ for (x = 0; x < (picture->width >> 1); ++x) {
+ RGB_TO_UV(x, y, SUM4);
+ }
+ if (picture->width & 1) {
+ RGB_TO_UV(x, y, SUM2V);
+ }
+ }
+ if (picture->height & 1) {
+ for (x = 0; x < (picture->width >> 1); ++x) {
+ RGB_TO_UV(x, y, SUM2H);
+ }
+ if (picture->width & 1) {
+ RGB_TO_UV(x, y, SUM1);
+ }
+ }
+ return 1;
+}
+#undef SUM4
+#undef SUM2V
+#undef SUM2H
+#undef SUM1
+#undef RGB_TO_UV
+
+int WebPPictureImportRGB(WebPPicture* const picture,
+ const uint8_t* const rgb, int rgb_stride) {
+ if (!WebPPictureAlloc(picture)) return 0;
+ return Import(picture, rgb, rgb_stride, 3, 0);
+}
+
+int WebPPictureImportBGR(WebPPicture* const picture,
+ const uint8_t* const rgb, int rgb_stride) {
+ if (!WebPPictureAlloc(picture)) return 0;
+ return Import(picture, rgb, rgb_stride, 3, 1);
+}
+
+int WebPPictureImportRGBA(WebPPicture* const picture,
+ const uint8_t* const rgba, int rgba_stride) {
+ if (!WebPPictureAlloc(picture)) return 0;
+ return Import(picture, rgba, rgba_stride, 4, 0);
+}
+
+int WebPPictureImportBGRA(WebPPicture* const picture,
+ const uint8_t* const rgba, int rgba_stride) {
+ if (!WebPPictureAlloc(picture)) return 0;
+ return Import(picture, rgba, rgba_stride, 4, 1);
+}
+
+//-----------------------------------------------------------------------------
+// Simplest call:
+
+typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int);
+
+static size_t Encode(const uint8_t* rgb, int width, int height, int stride,
+ Importer import, float quality_factor, uint8_t** output) {
+ size_t output_size = 0;
+ 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
+ }
+
+ pic.width = width;
+ pic.height = height;
+ pic.writer = WebPMemoryWrite;
+ pic.custom_ptr = &wrt;
+
+ wrt.mem = output;
+ wrt.size = &output_size;
+ InitMemoryWriter(&wrt);
+
+ ok = import(&pic, rgb, stride) && WebPEncode(&config, &pic);
+ WebPPictureFree(&pic);
+ if (!ok) {
+ free(*output);
+ *output = NULL;
+ return 0;
+ }
+ return output_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, out); \
+}
+
+ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB);
+ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR);
+ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA);
+ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA);
+
+#undef ENCODE_FUNC
+
+//-----------------------------------------------------------------------------
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
diff --git a/third_party/libwebp/enc/quant.c b/third_party/libwebp/enc/quant.c
new file mode 100644
index 0000000..f3b99a0
--- /dev/null
+++ b/third_party/libwebp/enc/quant.c
@@ -0,0 +1,956 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// Quantization
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <math.h>
+
+#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 meaninful 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 MULT_8B(a, b) (((a) * (b) + 128) >> 8)
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+//-----------------------------------------------------------------------------
+
+static 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
+};
+
+#define QFIX 17
+#define BIAS(b) ((b) << (QFIX - 8))
+static const uint16_t kCoeffThresh[16] = {
+ 0, 10, 20, 30,
+ 10, 20, 30, 30,
+ 20, 30, 30, 30,
+ 30, 30, 30, 30
+};
+
+// TODO(skal): tune more. Coeff thresholding?
+static const uint8_t kBiasMatrices[3][16] = { // [3] = [luma-ac,luma-dc,chroma]
+ { 96, 96, 96, 96,
+ 96, 96, 96, 96,
+ 96, 96, 96, 96,
+ 96, 96, 96, 96 },
+ { 96, 96, 96, 96,
+ 96, 96, 96, 96,
+ 96, 96, 96, 96,
+ 96, 96, 96, 96 },
+ { 96, 96, 96, 96,
+ 96, 96, 96, 96,
+ 96, 96, 96, 96,
+ 96, 96, 96, 96 }
+};
+
+// Sharpening by (slightly) raising the hi-frequency coeffs (only for trellis).
+// Hack-ish but helpful for mid-bitrate range. Use with care.
+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;
+ int sum = 0;
+ for (i = 2; i < 16; ++i) {
+ m->q_[i] = m->q_[1];
+ }
+ for (i = 0; i < 16; ++i) {
+ const int j = kZigzag[i];
+ const int bias = kBiasMatrices[type][j];
+ m->iq_[j] = (1 << QFIX) / m->q_[j];
+ m->bias_[j] = BIAS(bias);
+ // TODO(skal): tune kCoeffThresh[]
+ m->zthresh_[j] = ((256 /*+ kCoeffThresh[j]*/ - bias) * m->q_[j] + 127) >> 8;
+ m->sharpen_[j] = (kFreqSharpening[j] * m->q_[j]) >> 11;
+ sum += m->q_[j];
+ }
+ 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);
+
+ // TODO: Switch to kLambda*[] tables?
+ {
+ 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;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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 3
+
+static void SetupFilterStrength(VP8Encoder* const enc) {
+ int i;
+ const int level0 = enc->config_->filter_strength;
+ for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
+ // Segments with lower quantizer will be less filtered. TODO: tune (wrt SNS)
+ const int level = level0 * 256 * enc->dqm_[i].quant_ / 128;
+ const int f = level / (256 + enc->dqm_[i].beta_);
+ enc->dqm_[i].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 q) {
+ const double c = q / 100.;
+ return (c < 0.75) ? c * (2. / 3.) : 2. * c - 1.;
+}
+
+void VP8SetSegmentParams(VP8Encoder* const enc, float quality) {
+ int i;
+ int dq_uv_ac, dq_uv_dc;
+ const int num_segments = enc->config_->segments;
+ const double amp = SNS_TO_DQ * enc->config_->sns_strength / 100. / 128.;
+ const double c_base = QualityToCompression(quality);
+ for (i = 0; i < num_segments; ++i) {
+ // 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 modelling for high-quant would make use of kAcTable[]
+ // more explicitely.
+ // Additionally, we modulate the base exponent 1/3 to accommodate for the
+ // quantization susceptibility and allow denser segments to be quantized
+ // more.
+ const double expn = (1. - amp * enc->dqm_[i].alpha_) / 3.;
+ 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;
+
+ SetupMatrices(enc);
+
+ SetupFilterStrength(enc); // initialize segments' filtering, eventually
+}
+
+//-----------------------------------------------------------------------------
+// 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) {
+ VP8Encoder* const enc = it->enc_;
+ const uint8_t* left = it->x_ ? enc->y_left_ : NULL;
+ const uint8_t* top = it->y_ ? enc->y_top_ + it->x_ * 16 : NULL;
+ VP8EncPredLuma16(it->yuv_p_, left, top);
+}
+
+void VP8MakeChroma8Preds(const VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+ const uint8_t* left = it->x_ ? enc->u_left_ : NULL;
+ const uint8_t* top = it->y_ ? enc->uv_top_ + it->x_ * 16 : 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->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->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->nz |= src->nz; // here, new nz bits are accumulated.
+ dst->score += src->score;
+}
+
+//-----------------------------------------------------------------------------
+// Performs simple and trellis-optimized quantization.
+
+// Fun fact: this is the _only_ line where we're actually being lossy and
+// discarding bits.
+static int DIV(int n, int iQ, int B) {
+ return (n * iQ + B) >> QFIX;
+}
+
+// Simple quantization
+static int QuantizeBlock(int16_t in[16], int16_t out[16],
+ int n, const VP8Matrix* const mtx) {
+ int last = -1;
+ for (; n < 16; ++n) {
+ const int j = kZigzag[n];
+ const int sign = (in[j] < 0);
+ int coeff = (sign ? -in[j] : in[j]) + mtx->sharpen_[j];
+ if (coeff > 2047) coeff = 2047;
+ if (coeff > mtx->zthresh_[j]) {
+ const int Q = mtx->q_[j];
+ const int iQ = mtx->iq_[j];
+ const int B = mtx->bias_[j];
+ out[n] = DIV(coeff, iQ, B);
+ if (sign) out[n] = -out[n];
+ in[j] = out[n] * Q;
+ if (out[n]) last = n;
+ } else {
+ out[n] = 0;
+ in[j] = 0;
+ }
+ }
+ return (last >= 0);
+}
+
+// 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 inline void SetRDScore(int lambda, VP8ModeScore* const rd) {
+ // TODO: incorporate the "* 256" in the tables?
+ rd->score = rd->R * lambda + 256 * (rd->D + rd->SD);
+}
+
+static 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);
+ int coeff0 = (sign ? -in[j] : in[j]) + mtx->sharpen_[j];
+ int level0;
+ if (coeff0 > 2047) coeff0 = 2047;
+
+ level0 = DIV(coeff0, iQ, B);
+ // 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 >= 2048 || 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) {
+ const 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;
+ const 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 |= QuantizeBlock(dc_tmp, rd->y_dc_levels, 0, &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 |= QuantizeBlock(tmp[n], rd->y_ac_levels[n], 1, &dqm->y1_) << n;
+ }
+ }
+
+ // Transform back
+ VP8ITransformWHT(dc_tmp, tmp[0]);
+ for (n = 0; n < 16; ++n) {
+ VP8ITransform(ref + VP8Scan[n], tmp[n], yuv_out + VP8Scan[n]);
+ }
+
+ 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 = QuantizeBlock(tmp, levels, 0, &dqm->y1_);
+ }
+ VP8ITransform(ref, tmp, yuv_out);
+ 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 |= QuantizeBlock(tmp[n], rd->uv_levels[n], 0, &dqm->uv_) << n;
+ }
+ }
+
+ for (n = 0; n < 8; ++n) {
+ VP8ITransform(ref + VP8Scan[16 + n], tmp[n], yuv_out + VP8Scan[16 + n]);
+ }
+ return (nz << 16);
+}
+
+//-----------------------------------------------------------------------------
+// RD-opt decision. Reconstruct each modes, evalue distortion and bit-cost.
+// Pick the mode is lower RD-cost = Rate + lamba * Distortion.
+
+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 void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* const rd) {
+ VP8Encoder* const enc = it->enc_;
+ const 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 < 4; ++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.R = VP8GetCostLuma16(it, &rd16);
+ rd16.R += VP8FixedCostsI16[mode];
+
+ // 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);
+}
+
+//-----------------------------------------------------------------------------
+
+// return the cost array corresponding to the surrounding prediction modes.
+static const uint16_t* GetCostModeI4(VP8EncIterator* const it,
+ const int 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) {
+ 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;
+ VP8ModeScore rd_best;
+
+ InitScore(&rd_best);
+ rd_best.score = 0;
+ VP8IteratorStartI4(it);
+ do {
+ 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.R = VP8GetCostLuma4(it, tmp_levels);
+ rd_tmp.R += mode_costs[mode];
+
+ 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;
+ }
+ // 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) {
+ 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 < 4; ++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.R = VP8GetCostUV(it, &rd_uv);
+ rd_uv.R += VP8FixedCostsUV[mode];
+
+ 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 i16 = (it->mb_->type_ == 1);
+ int nz = 0;
+
+ if (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;
+}
+
+//-----------------------------------------------------------------------------
+// Entry point
+
+int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, int rd_opt) {
+ int is_skipped;
+
+ InitScore(rd);
+
+ // We can perform predictions for Luma16x16 and Chroma8x8 already.
+ // Luma4x4 predictions needs to be done as-we-go.
+ VP8MakeLuma16Preds(it);
+ VP8MakeChroma8Preds(it);
+
+ // for rd_opt = 2, we perform trellis-quant on the final decision only.
+ // for rd_opt > 2, we use it for every scoring (=much slower).
+ if (rd_opt > 0) {
+ it->do_trellis_ = (rd_opt > 2);
+ PickBestIntra16(it, rd);
+ if (it->enc_->method_ >= 2) {
+ PickBestIntra4(it, rd);
+ }
+ PickBestUV(it, rd);
+ if (rd_opt == 2) {
+ it->do_trellis_ = 1;
+ SimpleQuantize(it, rd);
+ }
+ } else {
+ // TODO: for method_ == 2, pick the best intra4/intra16 based on SSE
+ it->do_trellis_ = (it->enc_->method_ == 2);
+ SimpleQuantize(it, rd);
+ }
+ is_skipped = (rd->nz == 0);
+ VP8SetSkip(it, is_skipped);
+ return is_skipped;
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
diff --git a/third_party/libwebp/enc/syntax.c b/third_party/libwebp/enc/syntax.c
new file mode 100644
index 0000000..a788f3c
--- /dev/null
+++ b/third_party/libwebp/enc/syntax.c
@@ -0,0 +1,237 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// Header syntax writing
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <math.h>
+
+#include "vp8enci.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define KSIGNATURE 0x9d012a
+#define KHEADER_SIZE 10
+#define KRIFF_SIZE 20
+#define KSIZE_OFFSET (KRIFF_SIZE - 8)
+
+#define MAX_PARTITION0_SIZE (1 << 19) // max size of mode partition
+#define MAX_PARTITION_SIZE (1 << 24) // max size for token partition
+
+//-----------------------------------------------------------------------------
+// Writers for header's various pieces (in order of appearance)
+
+// Main keyframe header
+
+static void PutLE32(uint8_t* const data, uint32_t val) {
+ data[0] = (val >> 0) & 0xff;
+ data[1] = (val >> 8) & 0xff;
+ data[2] = (val >> 16) & 0xff;
+ data[3] = (val >> 24) & 0xff;
+}
+
+static int PutHeader(int profile, size_t size0, size_t total_size,
+ const WebPPicture* const pic) {
+ uint8_t buf[KHEADER_SIZE];
+ uint8_t RIFF[KRIFF_SIZE] = {
+ 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P', 'V', 'P', '8', ' '
+ };
+ uint32_t bits;
+
+ if (size0 >= MAX_PARTITION0_SIZE) {
+ return 0; // partition #0 is too big to fit
+ }
+
+ PutLE32(RIFF + 4, total_size + KSIZE_OFFSET);
+ PutLE32(RIFF + 16, total_size);
+ if (!pic->writer(RIFF, sizeof(RIFF), pic))
+ return 0;
+
+ bits = 0 // keyframe (1b)
+ | (profile << 1) // profile (3b)
+ | (1 << 4) // visible (1b)
+ | (size0 << 5); // partition length (19b)
+ buf[0] = bits & 0xff;
+ buf[1] = (bits >> 8) & 0xff;
+ buf[2] = (bits >> 16) & 0xff;
+ // signature
+ buf[3] = (KSIGNATURE >> 16) & 0xff;
+ buf[4] = (KSIGNATURE >> 8) & 0xff;
+ buf[5] = (KSIGNATURE >> 0) & 0xff;
+ // dimensions
+ buf[6] = pic->width & 0xff;
+ buf[7] = pic->width >> 8;
+ buf[8] = pic->height & 0xff;
+ buf[9] = pic->height >> 8;
+
+ return pic->writer(buf, sizeof(buf), pic);
+}
+
+// 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,
+ const 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 >= MAX_PARTITION_SIZE) {
+ return 0; // partition is too big to fit
+ }
+ 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;
+}
+
+//-----------------------------------------------------------------------------
+
+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;
+
+ pos1 = VP8BitWriterPos(bw);
+ VP8BitWriterInit(bw, mb_size * 7 / 8); // ~7 bits per macroblock
+ VP8PutBitUniform(bw, 0); // colorspace
+ VP8PutBitUniform(bw, 0); // clamp type
+
+ PutSegmentHeader(bw, enc);
+ PutFilterHeader(bw, &enc->filter_hdr_);
+ VP8PutValue(bw, enc->config_->partitions, 2);
+ PutQuant(bw, enc);
+ VP8PutBitUniform(bw, 0); // no proba update
+ VP8WriteProbas(bw, &enc->proba_);
+ pos2 = VP8BitWriterPos(bw);
+ VP8CodeIntraModes(enc);
+ VP8BitWriterFinish(bw);
+ 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);
+ }
+ return !bw->error_;
+}
+
+int VP8EncWrite(VP8Encoder* const enc) {
+ WebPPicture* const pic = enc->pic_;
+ VP8BitWriter* const bw = &enc->bw_;
+ int ok = 0;
+ size_t coded_size, pad;
+ int p;
+
+ // Partition #0 with header and partition sizes
+ ok = GeneratePartition0(enc);
+
+ // Compute total size (for the RIFF header)
+ coded_size = KHEADER_SIZE + VP8BitWriterSize(bw) + 3 * (enc->num_parts_ - 1);
+ for (p = 0; p < enc->num_parts_; ++p) {
+ coded_size += VP8BitWriterSize(enc->parts_ + p);
+ }
+ pad = coded_size & 1;
+ coded_size += pad;
+
+ // Emit headers and partition #0
+ {
+ const uint8_t* const part0 = VP8BitWriterBuf(bw);
+ const size_t size0 = VP8BitWriterSize(bw);
+ ok = ok && PutHeader(enc->profile_, size0, coded_size, pic)
+ && pic->writer(part0, size0, pic)
+ && EmitPartitionsSize(enc, pic);
+ free((void*)part0);
+ }
+
+ // 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);
+ free((void*)buf);
+ }
+
+ // Padding byte
+ if (ok && pad) {
+ const uint8_t pad_byte[1] = { 0 };
+ ok = pic->writer(pad_byte, 1, pic);
+ }
+
+ enc->coded_size_ = coded_size + KRIFF_SIZE;
+ return ok;
+}
+
+//-----------------------------------------------------------------------------
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
diff --git a/third_party/libwebp/enc/tree.c b/third_party/libwebp/enc/tree.c
new file mode 100644
index 0000000..b1a9aa4
--- /dev/null
+++ b/third_party/libwebp/enc/tree.c
@@ -0,0 +1,507 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// Token probabilities
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "vp8enci.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+//-----------------------------------------------------------------------------
+// Default probabilities
+
+// Paragraph 13.5
+const uint8_t
+ VP8CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
+ // genereated using vp8_default_coef_probs() in entropy.c:129
+ { { { 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_;
+ memset(probas->segments_, 255u, sizeof(probas->segments_));
+ memcpy(probas->coeffs_, VP8CoeffsProba0, sizeof(VP8CoeffsProba0));
+ probas->use_skip_proba_ = 0;
+}
+
+// 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* 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, 0));
+}
+
+//-----------------------------------------------------------------------------
+// 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);
+ }
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
diff --git a/third_party/libwebp/enc/vp8enci.h b/third_party/libwebp/enc/vp8enci.h
new file mode 100644
index 0000000..b488922
--- /dev/null
+++ b/third_party/libwebp/enc/vp8enci.h
@@ -0,0 +1,445 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// 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 "bit_writer.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+//-----------------------------------------------------------------------------
+// Various defines and enums
+
+// 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
+ };
+
+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
+ };
+
+// 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 (enc->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)
+
+//-----------------------------------------------------------------------------
+// Headers
+
+typedef uint8_t ProbaArray[NUM_CTX][NUM_PROBAS];
+typedef uint64_t StatsArray[NUM_CTX][NUM_PROBAS][2];
+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]; // 7.4k
+ CostArray level_cost_[NUM_TYPES][NUM_BANDS]; // 11.4k
+ int use_skip_proba_; // Note: we always use skip_proba for now.
+ int nb_skip_, nb_i4_, nb_i16_; // block type counters
+} 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
+ uint8_t type_:2; // 0=i4x4, 1=i16x16
+ uint8_t uv_mode_:2;
+ uint8_t skip_:1;
+ uint8_t segment_:2;
+ uint8_t alpha_; // quantization-susceptibility
+} VP8MBInfo;
+
+typedef struct {
+ 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
+ // 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 transcient struct to accumulate score and info during RD-optimization
+// and mode evaluation.
+typedef struct {
+ score_t D, SD, R, score; // Distortion, spectral distortion, 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
+ int 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_offset_, uv_offset_; // offset to the luma / chroma planes
+ int y_stride_, uv_stride_; // respective strides
+ uint8_t* yuv_in_; // borrowed from enc_ (for now)
+ uint8_t* yuv_out_; // ''
+ uint8_t* yuv_out2_; // ''
+ uint8_t* yuv_p_; // ''
+ 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 done_; // true when scan is finished
+} VP8EncIterator;
+
+ // in iterator.c
+// must be called first.
+void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it);
+// restart a scan.
+void VP8IteratorReset(VP8EncIterator* const it);
+// import samples from source
+void VP8IteratorImport(const VP8EncIterator* const it);
+// export decimated samples
+void VP8IteratorExport(const VP8EncIterator* const it);
+// go to next macroblock. Returns !done_. If *block_to_save is non-null, will
+// save the boundary values to top_/left_ arrays. block_to_save can be
+// it->yuv_out_ or it->yuv_in_.
+int VP8IteratorNext(VP8EncIterator* const it,
+ const uint8_t* const block_to_save);
+// 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, int modes[16]);
+void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode);
+void VP8SetSkip(const VP8EncIterator* const it, int skip);
+void VP8SetSegment(const VP8EncIterator* const it, int segment);
+void VP8IteratorResetCosts(VP8EncIterator* const it);
+
+//-----------------------------------------------------------------------------
+// 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
+
+ // 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 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_[3]; // sum of Y/U/V 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.
+ int rd_opt_level_; // Deduced from method_.
+
+ // 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* yuv_in_; // input samples
+ uint8_t* yuv_out_; // output samples
+ uint8_t* yuv_out2_; // secondary scratch out-buffer. swapped with yuv_out_.
+ uint8_t* yuv_p_; // scratch buffer for prediction
+ uint8_t *y_top_; // top luma samples.
+ uint8_t *uv_top_; // top u/v samples.
+ // U and V are packed into 16 pixels (8 U + 8 V)
+ uint8_t *y_left_; // left luma samples (adressable from index -1 to 15).
+ uint8_t *u_left_; // left u samples (adressable from index -1 to 7)
+ uint8_t *v_left_; // left v samples (adressable from index -1 to 7)
+
+ 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);
+
+ // in frame.c
+extern const uint8_t VP8EncBands[16 + 1];
+// 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 stat / coding passes
+int VP8EncLoop(VP8Encoder* const enc);
+int VP8StatLoop(VP8Encoder* const enc);
+
+ // 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, int rd_opt);
+
+ // in dsp.c
+// Transforms
+typedef void (*VP8Idct)(const uint8_t* ref, const int16_t* in, uint8_t* dst);
+typedef void (*VP8Fdct)(const uint8_t* src, const uint8_t* ref, int16_t* out);
+typedef void (*VP8WHT)(const int16_t* in, int16_t* out);
+extern VP8Idct VP8ITransform;
+extern VP8Fdct VP8FTransform;
+extern VP8WHT VP8ITransformWHT;
+extern VP8WHT VP8FTransformWHT;
+// Predictions
+// *dst is the destination block. *top, *top_right and *left can be NULL.
+typedef void (*VP8IntraPreds)(uint8_t *dst, const uint8_t* left,
+ const uint8_t* top);
+typedef void (*VP8Intra4Preds)(uint8_t *dst, const uint8_t* top);
+extern VP8Intra4Preds VP8EncPredLuma4;
+extern VP8IntraPreds VP8EncPredLuma16;
+extern VP8IntraPreds VP8EncPredChroma8;
+
+typedef int (*VP8Metric)(const uint8_t* pix, const uint8_t* ref);
+extern VP8Metric VP8SSE16x16, VP8SSE16x8, VP8SSE8x8, VP8SSE4x4;
+typedef int (*VP8WMetric)(const uint8_t* pix, const uint8_t* ref,
+ const uint16_t* const weights);
+extern VP8WMetric VP8TDisto4x4, VP8TDisto16x16;
+
+typedef void (*VP8BlockCopy)(const uint8_t* src, uint8_t* dst);
+extern VP8BlockCopy VP8Copy4x4;
+extern VP8BlockCopy VP8Copy8x8;
+extern VP8BlockCopy VP8Copy16x16;
+
+void VP8EncDspInit(); // must be called before using anything from the above.
+
+ // in filter.c
+extern void VP8InitFilter(VP8EncIterator* const it);
+extern void VP8StoreFilterStats(VP8EncIterator* const it);
+extern void VP8AdjustFilterStrength(VP8EncIterator* const it);
+
+//-----------------------------------------------------------------------------
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
+
+#endif // WEBP_ENC_VP8ENCI_H_
diff --git a/third_party/libwebp/enc/webpenc.c b/third_party/libwebp/enc/webpenc.c
new file mode 100644
index 0000000..5ca445e
--- /dev/null
+++ b/third_party/libwebp/enc/webpenc.c
@@ -0,0 +1,307 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// WebP encoder: main entry point
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "vp8enci.h"
+
+// #define PRINT_MEMORY_INFO
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#ifdef PRINT_MEMORY_INFO
+#include <stdio.h>
+#endif
+
+#define MAX_DIMENSION 16384 // maximum width/height allowed by the spec
+
+//-----------------------------------------------------------------------------
+// WebPPicture
+//-----------------------------------------------------------------------------
+
+static int DummyWriter(const uint8_t* data, size_t data_size,
+ const WebPPicture* const picture) {
+ return 1;
+}
+
+int WebPPictureInitInternal(WebPPicture* const picture, int version) {
+ if (version != WEBP_ENCODER_ABI_VERSION) {
+ return 0; // caller/system version mismatch!
+ }
+ if (picture) {
+ memset(picture, 0, sizeof(*picture));
+ picture->writer = DummyWriter;
+ }
+ 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
+}
+
+// Map configured quality level to coding tools used.
+//-------------+---+---+---+---+---+---+
+// Quality | 0 | 1 | 2 | 3 | 4 | 5 +
+//-------------+---+---+---+---+---+---+
+// dynamic prob| ~ | x | x | x | x | x |
+//-------------+---+---+---+---+---+---+
+// rd-opt modes| | | x | x | x | x |
+//-------------+---+---+---+---+---+---+
+// fast i4/i16 | x | x | | | | |
+//-------------+---+---+---+---+---+---+
+// rd-opt i4/16| | | x | x | x | x |
+//-------------+---+---+---+---+---+---+
+// Trellis | | x | | | x | x |
+//-------------+---+---+---+---+---+---+
+// full-SNS | | | | | | x |
+//-------------+---+---+---+---+---+---+
+
+static void MapConfigToTools(VP8Encoder* const enc) {
+ const int method = enc->config_->method;
+ enc->method_ = method;
+ enc->rd_opt_level_ = (method >= 6) ? 3
+ : (method >= 5) ? 2
+ : (method >= 3) ? 1
+ : 0;
+}
+
+// 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
+// Transcient object sizes:
+// VP8EncIterator: 352
+// VP8ModeScore: 912
+// VP8SegmentInfo: 532
+// VP8Proba: 31032
+// LFStats: 2048
+// Picture size (yuv): 589824
+
+static VP8Encoder* InitEncoder(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);
+ const size_t cache_size = (3 * YUV_SIZE + PRED_SIZE) * sizeof(uint8_t);
+ const size_t info_size = mb_w * mb_h * sizeof(VP8MBInfo);
+ const size_t samples_size = (2 * top_stride + // top-luma/u/v
+ 16 + 16 + 16 + 8 + 1 + // left y/u/v
+ 2 * ALIGN_CST) // align all
+ * sizeof(uint8_t);
+ const size_t lf_stats_size = config->autofilter ? sizeof(LFStats) : 0;
+ VP8Encoder* enc;
+ uint8_t* mem;
+ size_t size = sizeof(VP8Encoder) + ALIGN_CST // main struct
+ + cache_size // working caches
+ + 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"
+ " block cache: %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, cache_size, info_size,
+ preds_size, samples_size, nz_size, lf_stats_size, size);
+ printf("Transcient 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*)malloc(size);
+ if (mem == NULL) 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->yuv_in_ = (uint8_t*)mem;
+ mem += YUV_SIZE;
+ enc->yuv_out_ = (uint8_t*)mem;
+ mem += YUV_SIZE;
+ enc->yuv_out2_ = (uint8_t*)mem;
+ mem += YUV_SIZE;
+ enc->yuv_p_ = (uint8_t*)mem;
+ mem += PRED_SIZE;
+ 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*)mem;
+ mem += nz_size;
+ enc->lf_stats_ = lf_stats_size ? (LFStats*)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;
+ mem = (uint8_t*)DO_ALIGN(mem + 1);
+ enc->y_left_ = (uint8_t*)mem;
+ mem += 16 + 16;
+ enc->u_left_ = (uint8_t*)mem;
+ mem += 16;
+ enc->v_left_ = (uint8_t*)mem;
+ mem += 8;
+
+ enc->config_ = config;
+ enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2;
+ enc->pic_ = picture;
+
+ MapConfigToTools(enc);
+ VP8EncDspInit();
+ VP8DefaultProbas(enc);
+ ResetSegmentHeader(enc);
+ ResetFilterHeader(enc);
+ ResetBoundaryPredictions(enc);
+
+ return enc;
+}
+
+static void DeleteEncoder(VP8Encoder* enc) {
+ free(enc);
+}
+
+//-----------------------------------------------------------------------------
+
+static double GetPSNR(uint64_t err, uint64_t size) {
+ return err ? 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);
+}
+
+static void StoreStats(VP8Encoder* const enc) {
+ WebPAuxStats* const stats = enc->pic_->stats;
+ if (stats) {
+ 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];
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) {
+ VP8Encoder* enc;
+ int ok;
+
+ if (config == NULL || pic == NULL)
+ return 0; // bad params
+ if (!WebPValidateConfig(config))
+ return 0; // invalid config.
+ if (pic->width <= 0 || pic->height <= 0)
+ return 0; // invalid parameters
+ if (pic->y == NULL || pic->u == NULL || pic->v == NULL)
+ return 0; // invalid parameters
+ if (pic->width >= MAX_DIMENSION || pic->height >= MAX_DIMENSION)
+ return 0; // image is too big
+
+ enc = InitEncoder(config, pic);
+ if (enc == NULL) return 0;
+ ok = VP8EncAnalyze(enc)
+ && VP8StatLoop(enc)
+ && VP8EncLoop(enc)
+ && VP8EncWrite(enc);
+ StoreStats(enc);
+ DeleteEncoder(enc);
+ return ok;
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
diff --git a/third_party/libwebp/libwebp.gyp b/third_party/libwebp/libwebp.gyp
index 7fb1cbb..f63fa5f 100644
--- a/third_party/libwebp/libwebp.gyp
+++ b/third_party/libwebp/libwebp.gyp
@@ -10,22 +10,47 @@
['use_system_libwebp==0', {
'targets': [
{
- 'target_name': 'libwebp',
+ 'target_name': 'libwebp_enc',
+ 'type': '<(library)',
+ 'sources': [
+ 'enc/analysis.c',
+ 'enc/bit_writer.c',
+ 'enc/config.c',
+ 'enc/cost.c',
+ 'enc/dsp.c',
+ 'enc/filter.c',
+ 'enc/frame.c',
+ 'enc/iterator.c',
+ 'enc/picture.c',
+ 'enc/quant.c',
+ 'enc/syntax.c',
+ 'enc/tree.c',
+ 'enc/webpenc.c'
+ ],
+ },
+ {
+ 'target_name': 'libwebp_dec',
'type': '<(library)',
'sources': [
- 'bits.c',
- 'dsp.c',
- 'frame.c',
- 'quant.c',
- 'tree.c',
- 'vp8.c',
- 'webp.c',
- 'yuv.c',
+ 'dec/bits.c',
+ 'dec/dsp.c',
+ 'dec/frame.c',
+ 'dec/quant.c',
+ 'dec/tree.c',
+ 'dec/vp8.c',
+ 'dec/webp.c',
+ 'dec/yuv.c',
+ ],
+ },
+ {
+ 'target_name': 'libwebp',
+ 'type': '<(library)',
+ 'dependencies' : [
+ 'libwebp_enc',
+ 'libwebp_dec',
],
'direct_dependent_settings': {
- 'include_dirs': [
- '.', './webp',
- ],
+ 'include_dirs': ['.'],
},
'conditions': [
['OS!="win"', {'product_name': 'webp'}],
diff --git a/third_party/libwebp/webp/decode.h b/third_party/libwebp/webp/decode.h
index 6ecaa00..78b9016 100644
--- a/third_party/libwebp/webp/decode.h
+++ b/third_party/libwebp/webp/decode.h
@@ -9,22 +9,10 @@
//
// Author: Skal (pascal.massimino@gmail.com)
-#ifndef WEBP_DECODE_WEBP_DECODE_H_
-#define WEBP_DECODE_WEBP_DECODE_H_
-
-#ifndef _MSC_VER
-#include <inttypes.h>
-#else
-typedef signed char int8_t;
-typedef unsigned char uint8_t;
-typedef signed short int16_t;
-typedef unsigned short uint16_t;
-typedef signed int int32_t;
-typedef unsigned int uint32_t;
-typedef unsigned long long int uint64_t;
-#define inline __forceinline
-#endif
+#ifndef WEBP_WEBP_DECODE_H_
+#define WEBP_WEBP_DECODE_H_
+#include "../webp/types.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
@@ -108,4 +96,4 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size,
} // extern "C"
#endif
-#endif // WEBP_DECODE_WEBP_DECODE_H_
+#endif /* WEBP_WEBP_DECODE_H_ */
diff --git a/third_party/libwebp/webp/decode_vp8.h b/third_party/libwebp/webp/decode_vp8.h
index 2e1b657..08c6f69 100644
--- a/third_party/libwebp/webp/decode_vp8.h
+++ b/third_party/libwebp/webp/decode_vp8.h
@@ -9,8 +9,8 @@
//
// Author: Skal (pascal.massimino@gmail.com)
-#ifndef WEBP_DECODE_WEBP_DECODE_VP8_H_
-#define WEBP_DECODE_WEBP_DECODE_VP8_H_
+#ifndef WEBP_WEBP_DECODE_VP8_H_
+#define WEBP_WEBP_DECODE_VP8_H_
#include "decode.h"
@@ -18,6 +18,8 @@
extern "C" {
#endif
+#define WEBP_DECODER_ABI_VERSION 0x0001
+
//-----------------------------------------------------------------------------
// Lower-level API
//
@@ -53,8 +55,9 @@ struct VP8Io {
// called when fresh samples are available. Currently, samples are in
// YUV420 format, and can be up to width x 24 in size (depending on the
- // in-loop filtering level, e.g.).
- void (*put)(const VP8Io* io);
+ // in-loop filtering level, e.g.). Should return false in case of error
+ // or abort request.
+ int (*put)(const VP8Io* io);
// called just before starting to decode the blocks.
// Should returns 0 in case of error.
@@ -71,30 +74,51 @@ struct VP8Io {
// Input buffer.
uint32_t data_size;
const uint8_t* data;
+
+ // If true, in-loop filtering will not be performed even if present in the
+ // bitstream. Switching off filtering may speed up decoding at the expense
+ // of more visible blocking. Note that output will also be non-compliant
+ // with the VP8 specifications.
+ int bypass_filtering;
};
+// Internal, version-checked, entry point
+extern int VP8InitIoInternal(VP8Io* const, int);
+
// Main decoding object. This is an opaque structure.
typedef struct VP8Decoder VP8Decoder;
// Create a new decoder object.
VP8Decoder* VP8New();
-// Can be called to make sure 'io' is initialized properly.
-void VP8InitIo(VP8Io* const io);
+// Must be called to make sure 'io' is initialized properly.
+// Returns false in case of version mismatch. Upon such failure, no other
+// decoding function should be called (VP8Decode, VP8GetHeaders, ...)
+static inline int VP8InitIo(VP8Io* const io) {
+ return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION);
+}
// Start decoding a new picture. Returns true if ok.
int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io);
// Decode a picture. Will call VP8GetHeaders() if it wasn't done already.
+// Returns false in case of error.
int VP8Decode(VP8Decoder* const dec, VP8Io* const io);
+// Enumeration of the codes returned by VP8Status()
+typedef enum {
+ VP8_STATUS_OK = 0,
+ VP8_STATUS_OUT_OF_MEMORY,
+ VP8_STATUS_INVALID_PARAM,
+ VP8_STATUS_BITSTREAM_ERROR,
+ VP8_STATUS_UNSUPPORTED_FEATURE,
+ VP8_STATUS_SUSPENDED,
+ VP8_STATUS_USER_ABORT,
+ VP8_STATUS_NOT_ENOUGH_DATA,
+} VP8StatusCode;
+
// Return current status of the decoder:
-// 0 = OK
-// 1 = OUT_OF_MEMORY
-// 2 = INVALID_PARAM
-// 3 = BITSTREAM_ERROR
-// 4 = UNSUPPORTED_FEATURE
-int VP8Status(VP8Decoder* const dec);
+VP8StatusCode VP8Status(VP8Decoder* const dec);
// return readable string corresponding to the last status.
const char* VP8StatusMessage(VP8Decoder* const dec);
@@ -112,4 +136,4 @@ void VP8Delete(VP8Decoder* const dec);
} // extern "C"
#endif
-#endif // WEBP_DECODE_WEBP_DECODE_VP8_H_
+#endif /* WEBP_WEBP_DECODE_VP8_H_ */
diff --git a/third_party/libwebp/webp/encode.h b/third_party/libwebp/webp/encode.h
new file mode 100644
index 0000000..8fb8ff4
--- /dev/null
+++ b/third_party/libwebp/webp/encode.h
@@ -0,0 +1,216 @@
+// Copyright 2011 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// WebP encoder: main interface
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_WEBP_ENCODE_H_
+#define WEBP_WEBP_ENCODE_H_
+
+#include <stdlib.h>
+
+#include "../webp/types.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define WEBP_ENCODER_ABI_VERSION 0x0001
+
+//-----------------------------------------------------------------------------
+// One-stop-shop call! No questions asked:
+
+// Returns the size of the compressed data (pointed to by *output), or 0 if
+// an error occurred. The compressed data must be released by the caller
+// using the call 'free(*output)'.
+// Currently, alpha values are discarded.
+size_t WebPEncodeRGB(const uint8_t* rgb, int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+size_t WebPEncodeBGR(const uint8_t* bgr, int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+size_t WebPEncodeRGBA(const uint8_t* rgba, int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+size_t WebPEncodeBGRA(const uint8_t* bgra, int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+
+//-----------------------------------------------------------------------------
+// Coding parameters
+
+typedef struct {
+ float quality; // between 0 (smallest file) and 100 (biggest)
+ int target_size; // if non-zero, set the desired target size in bytes.
+ // Takes precedence over the 'compression' parameter.
+ float target_PSNR; // if non-zero, specifies the minimal distortion to
+ // try to achieve. Takes precedence over target_size.
+ int method; // quality/speed trade-off (0=fast, 6=slower-better)
+ int segments; // maximum number of segments to use, in [1..4]
+ int sns_strength; // Spatial Noise Shaping. 0=off, 100=maximum.
+ int filter_strength; // range: [0 = off .. 100 = strongest]
+ int filter_sharpness; // range: [0 = off .. 7 = least sharp]
+ int filter_type; // filtering type: 0 = simple, 1 = strong
+ // (only used if filter_strength > 0 or autofilter > 0)
+ int autofilter; // Auto adjust filter's strength [0 = off, 1 = on]
+ int pass; // number of entropy-analysis passes (in [1..10]).
+
+ int show_compressed; // if true, export the compressed picture back.
+ // In-loop filtering is not applied.
+ int preprocessing; // preprocessing filter (0=none, 1=segment-smooth)
+ int partitions; // log2(number of token partitions) in [0..3]
+ // Default is set to 0 for easier progressive decoding.
+} WebPConfig;
+
+// Enumerate some predefined settings for WebPConfig, depending on the type
+// of source picture. These presets are used when calling WebPConfigPreset().
+typedef enum {
+ WEBP_PRESET_DEFAULT = 0, // default preset.
+ WEBP_PRESET_PICTURE, // digital picture, like portrait, inner shot
+ WEBP_PRESET_PHOTO, // outdoor photograph, with natural lighting
+ WEBP_PRESET_DRAWING, // hand or line drawing, with high-contrast details
+ WEBP_PRESET_ICON, // small-sized colorful images
+ WEBP_PRESET_TEXT // text-like
+} WebPPreset;
+
+// Internal, version-checked, entry point
+int WebPConfigInitInternal(WebPConfig* const, WebPPreset, float, int);
+
+// Should always be called, to initialize a fresh WebPConfig structure before
+// modification. Returns 0 in case of version mismatch. WebPConfigInit() must
+// have succeeded before using the 'config' object.
+static inline int WebPConfigInit(WebPConfig* const config) {
+ return WebPConfigInitInternal(config, WEBP_PRESET_DEFAULT, 75.f,
+ WEBP_ENCODER_ABI_VERSION);
+}
+
+// This function will initialize the configuration according to a predefined
+// set of parameters (referred to by 'preset') and a given quality factor.
+// This function can be called as a replacement to WebPConfigInit(). Will
+// return 0 in case of error.
+static inline int WebPConfigPreset(WebPConfig* const config,
+ WebPPreset preset, float quality) {
+ return WebPConfigInitInternal(config, preset, quality,
+ WEBP_ENCODER_ABI_VERSION);
+}
+
+// Returns 1 if all parameters are in valid range and the configuration is OK.
+int WebPValidateConfig(const WebPConfig* const config);
+
+//-----------------------------------------------------------------------------
+// Input / Output
+
+typedef struct WebPPicture WebPPicture; // main structure for I/O
+
+// non-essential structure for storing auxilliary statistics
+typedef struct {
+ float PSNR[4]; // peak-signal-to-noise ratio for Y/U/V/All
+ int coded_size; // final size
+ int block_count[3]; // number of intra4/intra16/skipped macroblocks
+ int header_bytes[2]; // approximative number of bytes spent for header
+ // and mode-partition #0
+ int residual_bytes[3][4]; // approximative number of bytes spent for
+ // DC/AC/uv coefficients for each (0..3) segments.
+ int segment_size[4]; // number of macroblocks in each segments
+ int segment_quant[4]; // quantizer values for each segments
+ int segment_level[4]; // filtering strength for each segments [0..63]
+} WebPAuxStats;
+
+// Signature for output function. Should return 1 if writing was successful.
+// data/data_size is the segment of data to write, and 'picture' is for
+// reference (and so one can make use of picture->custom_ptr).
+typedef int (*WebPWriterFunction)(const uint8_t* data, size_t data_size,
+ const WebPPicture* const picture);
+
+struct WebPPicture {
+ // input
+ int colorspace; // colorspace: should be 0 for now (=Y'CbCr).
+ int width, height; // dimensions.
+ uint8_t *y, *u, *v; // pointers to luma/chroma planes.
+ int y_stride, uv_stride; // luma/chroma strides.
+ uint8_t *a; // pointer to the alpha plane (unused for now).
+
+ // output
+ WebPWriterFunction writer; // can be NULL
+ void* custom_ptr; // can be used by the writer.
+
+ // map for extra information
+ int extra_info_type; // 1: intra type, 2: segment, 3: quant
+ // 4: intra-16 prediction mode,
+ // 5: chroma prediction mode,
+ // 6: bit cost, 7: distortion
+ uint8_t* extra_info; // if not NULL, points to an array of size
+ // ((width + 15) / 16) * ((height + 15) / 16) that
+ // will be filled with a macroblock map, depending
+ // on extra_info_type.
+
+ // where to store statistics, if not NULL:
+ WebPAuxStats* stats;
+};
+
+// Internal, version-checked, entry point
+int WebPPictureInitInternal(WebPPicture* const, int);
+
+// Should always be called, to initialize the structure. Returns 0 in case of
+// version mismatch. WebPPictureInit() must have succeeded before using the
+// 'picture' object.
+static inline int WebPPictureInit(WebPPicture* const picture) {
+ return WebPPictureInitInternal(picture, WEBP_ENCODER_ABI_VERSION);
+}
+
+//-----------------------------------------------------------------------------
+// WebPPicture utils
+
+// Convenience allocation / deallocation based on picture->width/height:
+// Allocate y/u/v buffers as per width/height specification.
+// Note! This function will free the previous buffer if needed.
+// Returns 0 in case of memory error.
+int WebPPictureAlloc(WebPPicture* const picture);
+
+// Release memory allocated by WebPPictureAlloc() or WebPPictureImport*()
+// Note that this function does _not_ free the memory pointed to by 'picture'.
+void WebPPictureFree(WebPPicture* const picture);
+
+// Copy the pixels of *src into *dst, using WebPPictureAlloc.
+// Returns 0 in case of memory allocation error.
+int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst);
+
+// self-crops a picture to the rectangle defined by top/left/width/height.
+// Returns 0 in case of memory allocation error, or if the rectangle is
+// outside of the source picture.
+int WebPPictureCrop(WebPPicture* const picture,
+ int left, int top, int width, int height);
+
+// Colorspace conversion function. Previous buffer will be free'd, if any.
+// *rgb buffer should have a size of at least height * rgb_stride.
+// Returns 0 in case of memory error.
+int WebPPictureImportRGB(WebPPicture* const picture,
+ const uint8_t* const rgb, int rgb_stride);
+// Same, but for RGBA buffer. Alpha information is ignored.
+int WebPPictureImportRGBA(WebPPicture* const picture,
+ const uint8_t* const rgba, int rgba_stride);
+
+// Variant of the above, but taking BGR input:
+int WebPPictureImportBGR(WebPPicture* const picture,
+ const uint8_t* const bgr, int bgr_stride);
+int WebPPictureImportBGRA(WebPPicture* const picture,
+ const uint8_t* const bgra, int bgra_stride);
+
+//-----------------------------------------------------------------------------
+// Main call
+
+// Main encoding call, after config and picture have been initialiazed.
+// 'picture' must be less than 16384x16384 in dimension, and the 'config' object
+// must be a valid one.
+// Returns false in case of error, true otherwise.
+int WebPEncode(const WebPConfig* const config, WebPPicture* const picture);
+
+//-----------------------------------------------------------------------------
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
+
+#endif /* WEBP_WEBP_ENCODE_H_ */
diff --git a/third_party/libwebp/webp/types.h b/third_party/libwebp/webp/types.h
new file mode 100644
index 0000000..a6d2cd4
--- /dev/null
+++ b/third_party/libwebp/webp/types.h
@@ -0,0 +1,29 @@
+// Copyright 2010 Google Inc.
+//
+// This code is licensed under the same terms as WebM:
+// Software License Agreement: http://www.webmproject.org/license/software/
+// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
+// -----------------------------------------------------------------------------
+//
+// Common types
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_WEBP_TYPES_H_
+#define WEBP_WEBP_TYPES_H_
+
+#ifndef _MSC_VER
+#include <inttypes.h>
+#else
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef signed short int16_t;
+typedef unsigned short uint16_t;
+typedef signed int int32_t;
+typedef unsigned int uint32_t;
+typedef unsigned long long int uint64_t;
+typedef long long int int64_t;
+#define inline __forceinline
+#endif /* _MSC_VER */
+
+#endif /* WEBP_WEBP_TYPES_H_ */