summaryrefslogtreecommitdiff
path: root/thirdparty/libwebp/src/enc
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/libwebp/src/enc')
-rw-r--r--thirdparty/libwebp/src/enc/alpha_enc.c8
-rw-r--r--thirdparty/libwebp/src/enc/analysis_enc.c60
-rw-r--r--thirdparty/libwebp/src/enc/backward_references_enc.c201
-rw-r--r--thirdparty/libwebp/src/enc/backward_references_enc.h20
-rw-r--r--thirdparty/libwebp/src/enc/config_enc.c5
-rw-r--r--thirdparty/libwebp/src/enc/frame_enc.c20
-rw-r--r--thirdparty/libwebp/src/enc/histogram_enc.c11
-rw-r--r--thirdparty/libwebp/src/enc/histogram_enc.h6
-rw-r--r--thirdparty/libwebp/src/enc/picture_csp_enc.c14
-rw-r--r--thirdparty/libwebp/src/enc/picture_rescale_enc.c61
-rw-r--r--thirdparty/libwebp/src/enc/picture_tools_enc.c31
-rw-r--r--thirdparty/libwebp/src/enc/syntax_enc.c2
-rw-r--r--thirdparty/libwebp/src/enc/vp8i_enc.h13
-rw-r--r--thirdparty/libwebp/src/enc/vp8l_enc.c669
-rw-r--r--thirdparty/libwebp/src/enc/vp8li_enc.h4
-rw-r--r--thirdparty/libwebp/src/enc/webp_enc.c2
16 files changed, 697 insertions, 430 deletions
diff --git a/thirdparty/libwebp/src/enc/alpha_enc.c b/thirdparty/libwebp/src/enc/alpha_enc.c
index dce9ca957d..0b54f3e6ec 100644
--- a/thirdparty/libwebp/src/enc/alpha_enc.c
+++ b/thirdparty/libwebp/src/enc/alpha_enc.c
@@ -303,7 +303,7 @@ static int EncodeAlpha(VP8Encoder* const enc,
int ok = 1;
const int reduce_levels = (quality < 100);
- // quick sanity checks
+ // quick correctness checks
assert((uint64_t)data_size == (uint64_t)width * height); // as per spec
assert(enc != NULL && pic != NULL && pic->a != NULL);
assert(output != NULL && output_size != NULL);
@@ -361,7 +361,7 @@ static int EncodeAlpha(VP8Encoder* const enc,
//------------------------------------------------------------------------------
// Main calls
-static int CompressAlphaJob(void* arg1, void* dummy) {
+static int CompressAlphaJob(void* arg1, void* unused) {
VP8Encoder* const enc = (VP8Encoder*)arg1;
const WebPConfig* config = enc->config_;
uint8_t* alpha_data = NULL;
@@ -375,13 +375,13 @@ static int CompressAlphaJob(void* arg1, void* dummy) {
filter, effort_level, &alpha_data, &alpha_size)) {
return 0;
}
- if (alpha_size != (uint32_t)alpha_size) { // Sanity check.
+ if (alpha_size != (uint32_t)alpha_size) { // Soundness check.
WebPSafeFree(alpha_data);
return 0;
}
enc->alpha_data_size_ = (uint32_t)alpha_size;
enc->alpha_data_ = alpha_data;
- (void)dummy;
+ (void)unused;
return 1;
}
diff --git a/thirdparty/libwebp/src/enc/analysis_enc.c b/thirdparty/libwebp/src/enc/analysis_enc.c
index 687757ae03..ebb784261c 100644
--- a/thirdparty/libwebp/src/enc/analysis_enc.c
+++ b/thirdparty/libwebp/src/enc/analysis_enc.c
@@ -126,16 +126,6 @@ static void InitHistogram(VP8Histogram* const histo) {
histo->last_non_zero = 1;
}
-static void MergeHistograms(const VP8Histogram* const in,
- VP8Histogram* const out) {
- if (in->max_value > out->max_value) {
- out->max_value = in->max_value;
- }
- if (in->last_non_zero > out->last_non_zero) {
- out->last_non_zero = in->last_non_zero;
- }
-}
-
//------------------------------------------------------------------------------
// Simplified k-Means, to assign Nb segments based on alpha-histogram
@@ -285,49 +275,6 @@ static int FastMBAnalyze(VP8EncIterator* const it) {
return 0;
}
-static int MBAnalyzeBestIntra4Mode(VP8EncIterator* const it,
- int best_alpha) {
- uint8_t modes[16];
- const int max_mode = MAX_INTRA4_MODE;
- int i4_alpha;
- VP8Histogram total_histo;
- int cur_histo = 0;
- InitHistogram(&total_histo);
-
- VP8IteratorStartI4(it);
- do {
- int mode;
- int best_mode_alpha = DEFAULT_ALPHA;
- VP8Histogram histos[2];
- const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC + VP8Scan[it->i4_];
-
- VP8MakeIntra4Preds(it);
- for (mode = 0; mode < max_mode; ++mode) {
- int alpha;
-
- InitHistogram(&histos[cur_histo]);
- VP8CollectHistogram(src, it->yuv_p_ + VP8I4ModeOffsets[mode],
- 0, 1, &histos[cur_histo]);
- alpha = GetAlpha(&histos[cur_histo]);
- if (IS_BETTER_ALPHA(alpha, best_mode_alpha)) {
- best_mode_alpha = alpha;
- modes[it->i4_] = mode;
- cur_histo ^= 1; // keep track of best histo so far.
- }
- }
- // accumulate best histogram
- MergeHistograms(&histos[cur_histo ^ 1], &total_histo);
- // Note: we reuse the original samples for predictors
- } while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF_ENC));
-
- i4_alpha = GetAlpha(&total_histo);
- if (IS_BETTER_ALPHA(i4_alpha, best_alpha)) {
- VP8SetIntra4Mode(it, modes);
- best_alpha = i4_alpha;
- }
- return best_alpha;
-}
-
static int MBAnalyzeBestUVMode(VP8EncIterator* const it) {
int best_alpha = DEFAULT_ALPHA;
int smallest_alpha = 0;
@@ -371,13 +318,6 @@ static void MBAnalyze(VP8EncIterator* const it,
best_alpha = FastMBAnalyze(it);
} else {
best_alpha = MBAnalyzeBestIntra16Mode(it);
- if (enc->method_ >= 5) {
- // We go and make a fast decision for intra4/intra16.
- // It's usually not a good and definitive pick, but helps seeding the
- // stats about level bit-cost.
- // TODO(skal): improve criterion.
- best_alpha = MBAnalyzeBestIntra4Mode(it, best_alpha);
- }
}
best_uv_alpha = MBAnalyzeBestUVMode(it);
diff --git a/thirdparty/libwebp/src/enc/backward_references_enc.c b/thirdparty/libwebp/src/enc/backward_references_enc.c
index d445b40fc5..519b36a091 100644
--- a/thirdparty/libwebp/src/enc/backward_references_enc.c
+++ b/thirdparty/libwebp/src/enc/backward_references_enc.c
@@ -11,13 +11,14 @@
//
#include <assert.h>
+#include <float.h>
#include <math.h>
-#include "src/enc/backward_references_enc.h"
-#include "src/enc/histogram_enc.h"
+#include "src/dsp/dsp.h"
#include "src/dsp/lossless.h"
#include "src/dsp/lossless_common.h"
-#include "src/dsp/dsp.h"
+#include "src/enc/backward_references_enc.h"
+#include "src/enc/histogram_enc.h"
#include "src/utils/color_cache_utils.h"
#include "src/utils/utils.h"
@@ -103,6 +104,20 @@ void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs) {
}
}
+// Swaps the content of two VP8LBackwardRefs.
+static void BackwardRefsSwap(VP8LBackwardRefs* const refs1,
+ VP8LBackwardRefs* const refs2) {
+ const int point_to_refs1 =
+ (refs1->tail_ != NULL && refs1->tail_ == &refs1->refs_);
+ const int point_to_refs2 =
+ (refs2->tail_ != NULL && refs2->tail_ == &refs2->refs_);
+ const VP8LBackwardRefs tmp = *refs1;
+ *refs1 = *refs2;
+ *refs2 = tmp;
+ if (point_to_refs2) refs1->tail_ = &refs1->refs_;
+ if (point_to_refs1) refs2->tail_ = &refs2->refs_;
+}
+
void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size) {
assert(refs != NULL);
memset(refs, 0, sizeof(*refs));
@@ -154,6 +169,22 @@ static PixOrCopyBlock* BackwardRefsNewBlock(VP8LBackwardRefs* const refs) {
return b;
}
+// Return 1 on success, 0 on error.
+static int BackwardRefsClone(const VP8LBackwardRefs* const from,
+ VP8LBackwardRefs* const to) {
+ const PixOrCopyBlock* block_from = from->refs_;
+ VP8LClearBackwardRefs(to);
+ while (block_from != NULL) {
+ PixOrCopyBlock* const block_to = BackwardRefsNewBlock(to);
+ if (block_to == NULL) return 0;
+ memcpy(block_to->start_, block_from->start_,
+ block_from->size_ * sizeof(PixOrCopy));
+ block_to->size_ = block_from->size_;
+ block_from = block_from->next_;
+ }
+ return 1;
+}
+
extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,
const PixOrCopy v);
void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,
@@ -753,12 +784,18 @@ static int CalculateBestCacheSize(const uint32_t* argb, int quality,
}
}
} else {
+ int code, extra_bits, extra_bits_value;
// We should compute the contribution of the (distance,length)
// histograms but those are the same independently from the cache size.
// As those constant contributions are in the end added to the other
- // histogram contributions, we can safely ignore them.
+ // histogram contributions, we can ignore them, except for the length
+ // prefix that is part of the literal_ histogram.
int len = PixOrCopyLength(v);
uint32_t argb_prev = *argb ^ 0xffffffffu;
+ VP8LPrefixEncode(len, &code, &extra_bits, &extra_bits_value);
+ for (i = 0; i <= cache_bits_max; ++i) {
+ ++histos[i]->literal_[NUM_LITERAL_CODES + code];
+ }
// Update the color caches.
do {
if (*argb != argb_prev) {
@@ -842,16 +879,21 @@ extern int VP8LBackwardReferencesTraceBackwards(
int xsize, int ysize, const uint32_t* const argb, int cache_bits,
const VP8LHashChain* const hash_chain,
const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst);
-static VP8LBackwardRefs* GetBackwardReferences(
- int width, int height, const uint32_t* const argb, int quality,
- int lz77_types_to_try, int* const cache_bits,
- const VP8LHashChain* const hash_chain, VP8LBackwardRefs* best,
- VP8LBackwardRefs* worst) {
- const int cache_bits_initial = *cache_bits;
- double bit_cost_best = -1;
+static int GetBackwardReferences(int width, int height,
+ const uint32_t* const argb, int quality,
+ int lz77_types_to_try, int cache_bits_max,
+ int do_no_cache,
+ const VP8LHashChain* const hash_chain,
+ VP8LBackwardRefs* const refs,
+ int* const cache_bits_best) {
VP8LHistogram* histo = NULL;
- int lz77_type, lz77_type_best = 0;
+ int i, lz77_type;
+ // Index 0 is for a color cache, index 1 for no cache (if needed).
+ int lz77_types_best[2] = {0, 0};
+ double bit_costs_best[2] = {DBL_MAX, DBL_MAX};
VP8LHashChain hash_chain_box;
+ VP8LBackwardRefs* const refs_tmp = &refs[do_no_cache ? 2 : 1];
+ int status = 0;
memset(&hash_chain_box, 0, sizeof(hash_chain_box));
histo = VP8LAllocateHistogram(MAX_COLOR_CACHE_BITS);
@@ -860,86 +902,129 @@ static VP8LBackwardRefs* GetBackwardReferences(
for (lz77_type = 1; lz77_types_to_try;
lz77_types_to_try &= ~lz77_type, lz77_type <<= 1) {
int res = 0;
- double bit_cost;
- int cache_bits_tmp = cache_bits_initial;
+ double bit_cost = 0.;
if ((lz77_types_to_try & lz77_type) == 0) continue;
switch (lz77_type) {
case kLZ77RLE:
- res = BackwardReferencesRle(width, height, argb, 0, worst);
+ res = BackwardReferencesRle(width, height, argb, 0, refs_tmp);
break;
case kLZ77Standard:
// Compute LZ77 with no cache (0 bits), as the ideal LZ77 with a color
// cache is not that different in practice.
- res = BackwardReferencesLz77(width, height, argb, 0, hash_chain, worst);
+ res = BackwardReferencesLz77(width, height, argb, 0, hash_chain,
+ refs_tmp);
break;
case kLZ77Box:
if (!VP8LHashChainInit(&hash_chain_box, width * height)) goto Error;
res = BackwardReferencesLz77Box(width, height, argb, 0, hash_chain,
- &hash_chain_box, worst);
+ &hash_chain_box, refs_tmp);
break;
default:
assert(0);
}
if (!res) goto Error;
- // Next, try with a color cache and update the references.
- if (!CalculateBestCacheSize(argb, quality, worst, &cache_bits_tmp)) {
- goto Error;
- }
- if (cache_bits_tmp > 0) {
- if (!BackwardRefsWithLocalCache(argb, cache_bits_tmp, worst)) {
- goto Error;
+ // Start with the no color cache case.
+ for (i = 1; i >= 0; --i) {
+ int cache_bits = (i == 1) ? 0 : cache_bits_max;
+
+ if (i == 1 && !do_no_cache) continue;
+
+ if (i == 0) {
+ // Try with a color cache.
+ if (!CalculateBestCacheSize(argb, quality, refs_tmp, &cache_bits)) {
+ goto Error;
+ }
+ if (cache_bits > 0) {
+ if (!BackwardRefsWithLocalCache(argb, cache_bits, refs_tmp)) {
+ goto Error;
+ }
+ }
}
- }
- // Keep the best backward references.
- VP8LHistogramCreate(histo, worst, cache_bits_tmp);
- bit_cost = VP8LHistogramEstimateBits(histo);
- if (lz77_type_best == 0 || bit_cost < bit_cost_best) {
- VP8LBackwardRefs* const tmp = worst;
- worst = best;
- best = tmp;
- bit_cost_best = bit_cost;
- *cache_bits = cache_bits_tmp;
- lz77_type_best = lz77_type;
+ if (i == 0 && do_no_cache && cache_bits == 0) {
+ // No need to re-compute bit_cost as it was computed at i == 1.
+ } else {
+ VP8LHistogramCreate(histo, refs_tmp, cache_bits);
+ bit_cost = VP8LHistogramEstimateBits(histo);
+ }
+
+ if (bit_cost < bit_costs_best[i]) {
+ if (i == 1) {
+ // Do not swap as the full cache analysis would have the wrong
+ // VP8LBackwardRefs to start with.
+ if (!BackwardRefsClone(refs_tmp, &refs[1])) goto Error;
+ } else {
+ BackwardRefsSwap(refs_tmp, &refs[0]);
+ }
+ bit_costs_best[i] = bit_cost;
+ lz77_types_best[i] = lz77_type;
+ if (i == 0) *cache_bits_best = cache_bits;
+ }
}
}
- assert(lz77_type_best > 0);
+ assert(lz77_types_best[0] > 0);
+ assert(!do_no_cache || lz77_types_best[1] > 0);
// Improve on simple LZ77 but only for high quality (TraceBackwards is
// costly).
- if ((lz77_type_best == kLZ77Standard || lz77_type_best == kLZ77Box) &&
- quality >= 25) {
- const VP8LHashChain* const hash_chain_tmp =
- (lz77_type_best == kLZ77Standard) ? hash_chain : &hash_chain_box;
- if (VP8LBackwardReferencesTraceBackwards(width, height, argb, *cache_bits,
- hash_chain_tmp, best, worst)) {
- double bit_cost_trace;
- VP8LHistogramCreate(histo, worst, *cache_bits);
- bit_cost_trace = VP8LHistogramEstimateBits(histo);
- if (bit_cost_trace < bit_cost_best) best = worst;
+ for (i = 1; i >= 0; --i) {
+ if (i == 1 && !do_no_cache) continue;
+ if ((lz77_types_best[i] == kLZ77Standard ||
+ lz77_types_best[i] == kLZ77Box) &&
+ quality >= 25) {
+ const VP8LHashChain* const hash_chain_tmp =
+ (lz77_types_best[i] == kLZ77Standard) ? hash_chain : &hash_chain_box;
+ const int cache_bits = (i == 1) ? 0 : *cache_bits_best;
+ if (VP8LBackwardReferencesTraceBackwards(width, height, argb, cache_bits,
+ hash_chain_tmp, &refs[i],
+ refs_tmp)) {
+ double bit_cost_trace;
+ VP8LHistogramCreate(histo, refs_tmp, cache_bits);
+ bit_cost_trace = VP8LHistogramEstimateBits(histo);
+ if (bit_cost_trace < bit_costs_best[i]) {
+ BackwardRefsSwap(refs_tmp, &refs[i]);
+ }
+ }
}
- }
- BackwardReferences2DLocality(width, best);
+ BackwardReferences2DLocality(width, &refs[i]);
+
+ if (i == 1 && lz77_types_best[0] == lz77_types_best[1] &&
+ *cache_bits_best == 0) {
+ // If the best cache size is 0 and we have the same best LZ77, just copy
+ // the data over and stop here.
+ if (!BackwardRefsClone(&refs[1], &refs[0])) goto Error;
+ break;
+ }
+ }
+ status = 1;
Error:
VP8LHashChainClear(&hash_chain_box);
VP8LFreeHistogram(histo);
- return best;
+ return status;
}
-VP8LBackwardRefs* VP8LGetBackwardReferences(
+WebPEncodingError VP8LGetBackwardReferences(
int width, int height, const uint32_t* const argb, int quality,
- int low_effort, int lz77_types_to_try, int* const cache_bits,
- const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1,
- VP8LBackwardRefs* const refs_tmp2) {
+ int low_effort, int lz77_types_to_try, int cache_bits_max, int do_no_cache,
+ const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs,
+ int* const cache_bits_best) {
if (low_effort) {
- return GetBackwardReferencesLowEffort(width, height, argb, cache_bits,
- hash_chain, refs_tmp1);
+ VP8LBackwardRefs* refs_best;
+ *cache_bits_best = cache_bits_max;
+ refs_best = GetBackwardReferencesLowEffort(
+ width, height, argb, cache_bits_best, hash_chain, refs);
+ if (refs_best == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
+ // Set it in first position.
+ BackwardRefsSwap(refs_best, &refs[0]);
} else {
- return GetBackwardReferences(width, height, argb, quality,
- lz77_types_to_try, cache_bits, hash_chain,
- refs_tmp1, refs_tmp2);
+ if (!GetBackwardReferences(width, height, argb, quality, lz77_types_to_try,
+ cache_bits_max, do_no_cache, hash_chain, refs,
+ cache_bits_best)) {
+ return VP8_ENC_ERROR_OUT_OF_MEMORY;
+ }
}
+ return VP8_ENC_OK;
}
diff --git a/thirdparty/libwebp/src/enc/backward_references_enc.h b/thirdparty/libwebp/src/enc/backward_references_enc.h
index 103ddfdcb7..4c0267b41e 100644
--- a/thirdparty/libwebp/src/enc/backward_references_enc.h
+++ b/thirdparty/libwebp/src/enc/backward_references_enc.h
@@ -16,6 +16,7 @@
#include <assert.h>
#include <stdlib.h>
#include "src/webp/types.h"
+#include "src/webp/encode.h"
#include "src/webp/format_constants.h"
#ifdef __cplusplus
@@ -218,14 +219,19 @@ enum VP8LLZ77Type {
// Evaluates best possible backward references for specified quality.
// The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache
// bits to use (passing 0 implies disabling the local color cache).
-// The optimal cache bits is evaluated and set for the *cache_bits parameter.
-// The return value is the pointer to the best of the two backward refs viz,
-// refs[0] or refs[1].
-VP8LBackwardRefs* VP8LGetBackwardReferences(
+// The optimal cache bits is evaluated and set for the *cache_bits_best
+// parameter with the matching refs_best.
+// If do_no_cache == 0, refs is an array of 2 values and the best
+// VP8LBackwardRefs is put in the first element.
+// If do_no_cache != 0, refs is an array of 3 values and the best
+// VP8LBackwardRefs is put in the first element, the best value with no-cache in
+// the second element.
+// In both cases, the last element is used as temporary internally.
+WebPEncodingError VP8LGetBackwardReferences(
int width, int height, const uint32_t* const argb, int quality,
- int low_effort, int lz77_types_to_try, int* const cache_bits,
- const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1,
- VP8LBackwardRefs* const refs_tmp2);
+ int low_effort, int lz77_types_to_try, int cache_bits_max, int do_no_cache,
+ const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs,
+ int* const cache_bits_best);
#ifdef __cplusplus
}
diff --git a/thirdparty/libwebp/src/enc/config_enc.c b/thirdparty/libwebp/src/enc/config_enc.c
index 9d4828978e..3518b41403 100644
--- a/thirdparty/libwebp/src/enc/config_enc.c
+++ b/thirdparty/libwebp/src/enc/config_enc.c
@@ -39,6 +39,8 @@ int WebPConfigInitInternal(WebPConfig* config,
config->partitions = 0;
config->segments = 4;
config->pass = 1;
+ config->qmin = 0;
+ config->qmax = 100;
config->show_compressed = 0;
config->preprocessing = 0;
config->autofilter = 0;
@@ -106,6 +108,9 @@ int WebPValidateConfig(const WebPConfig* config) {
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->qmin < 0 || config->qmax > 100 || config->qmin > config->qmax) {
+ return 0;
+ }
if (config->show_compressed < 0 || config->show_compressed > 1) return 0;
if (config->preprocessing < 0 || config->preprocessing > 7) return 0;
if (config->partitions < 0 || config->partitions > 3) return 0;
diff --git a/thirdparty/libwebp/src/enc/frame_enc.c b/thirdparty/libwebp/src/enc/frame_enc.c
index 1aec376e44..af538d83ba 100644
--- a/thirdparty/libwebp/src/enc/frame_enc.c
+++ b/thirdparty/libwebp/src/enc/frame_enc.c
@@ -31,10 +31,15 @@
// we allow 2k of extra head-room in PARTITION0 limit.
#define PARTITION0_SIZE_LIMIT ((VP8_MAX_PARTITION0_SIZE - 2048ULL) << 11)
+static float Clamp(float v, float min, float max) {
+ return (v < min) ? min : (v > max) ? max : v;
+}
+
typedef struct { // struct for organizing convergence in either size or PSNR
int is_first;
float dq;
float q, last_q;
+ float qmin, qmax;
double value, last_value; // PSNR or size
double target;
int do_size_search;
@@ -47,7 +52,9 @@ static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) {
s->is_first = 1;
s->dq = 10.f;
- s->q = s->last_q = enc->config_->quality;
+ s->qmin = 1.f * enc->config_->qmin;
+ s->qmax = 1.f * enc->config_->qmax;
+ s->q = s->last_q = Clamp(enc->config_->quality, s->qmin, s->qmax);
s->target = do_size_search ? (double)target_size
: (target_PSNR > 0.) ? target_PSNR
: 40.; // default, just in case
@@ -56,10 +63,6 @@ static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) {
return do_size_search;
}
-static float Clamp(float v, float min, float max) {
- return (v < min) ? min : (v > max) ? max : v;
-}
-
static float ComputeNextQ(PassStats* const s) {
float dq;
if (s->is_first) {
@@ -75,7 +78,7 @@ static float ComputeNextQ(PassStats* const s) {
s->dq = Clamp(dq, -30.f, 30.f);
s->last_q = s->q;
s->last_value = s->value;
- s->q = Clamp(s->q + s->dq, 0.f, 100.f);
+ s->q = Clamp(s->q + s->dq, s->qmin, s->qmax);
return s->q;
}
@@ -848,9 +851,10 @@ int VP8EncTokenLoop(VP8Encoder* const enc) {
}
#if (DEBUG_SEARCH > 0)
- printf("#%2d metric:%.1lf -> %.1lf last_q=%.2lf q=%.2lf dq=%.2lf\n",
+ printf("#%2d metric:%.1lf -> %.1lf last_q=%.2lf q=%.2lf dq=%.2lf "
+ " range:[%.1f, %.1f]\n",
num_pass_left, stats.last_value, stats.value,
- stats.last_q, stats.q, stats.dq);
+ stats.last_q, stats.q, stats.dq, stats.qmin, stats.qmax);
#endif
if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) {
++num_pass_left;
diff --git a/thirdparty/libwebp/src/enc/histogram_enc.c b/thirdparty/libwebp/src/enc/histogram_enc.c
index a4e6bf3a98..38a0cebcab 100644
--- a/thirdparty/libwebp/src/enc/histogram_enc.c
+++ b/thirdparty/libwebp/src/enc/histogram_enc.c
@@ -208,6 +208,7 @@ void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
} else if (PixOrCopyIsCacheIdx(v)) {
const int literal_ix =
NUM_LITERAL_CODES + NUM_LENGTH_CODES + PixOrCopyCacheIdx(v);
+ assert(histo->palette_code_bits_ != 0);
++histo->literal_[literal_ix];
} else {
int code, extra_bits;
@@ -1170,13 +1171,15 @@ static void RemoveEmptyHistograms(VP8LHistogramSet* const image_histo) {
int VP8LGetHistoImageSymbols(int xsize, int ysize,
const VP8LBackwardRefs* const refs,
int quality, int low_effort,
- int histo_bits, int cache_bits,
+ int histogram_bits, int cache_bits,
VP8LHistogramSet* const image_histo,
VP8LHistogram* const tmp_histo,
uint16_t* const histogram_symbols) {
int ok = 0;
- const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1;
- const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1;
+ const int histo_xsize =
+ histogram_bits ? VP8LSubSampleSize(xsize, histogram_bits) : 1;
+ const int histo_ysize =
+ histogram_bits ? VP8LSubSampleSize(ysize, histogram_bits) : 1;
const int image_histo_raw_size = histo_xsize * histo_ysize;
VP8LHistogramSet* const orig_histo =
VP8LAllocateHistogramSet(image_histo_raw_size, cache_bits);
@@ -1192,7 +1195,7 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
if (orig_histo == NULL || map_tmp == NULL) goto Error;
// Construct the histograms from backward references.
- HistogramBuild(xsize, histo_bits, refs, orig_histo);
+ HistogramBuild(xsize, histogram_bits, refs, orig_histo);
// Copies the histograms and computes its bit_cost.
// histogram_symbols is optimized
HistogramCopyAndAnalyze(orig_histo, image_histo, &num_used,
diff --git a/thirdparty/libwebp/src/enc/histogram_enc.h b/thirdparty/libwebp/src/enc/histogram_enc.h
index 54c2d21783..c3428b5d55 100644
--- a/thirdparty/libwebp/src/enc/histogram_enc.h
+++ b/thirdparty/libwebp/src/enc/histogram_enc.h
@@ -64,8 +64,8 @@ void VP8LHistogramCreate(VP8LHistogram* const p,
const VP8LBackwardRefs* const refs,
int palette_code_bits);
-// Return the size of the histogram for a given palette_code_bits.
-int VP8LGetHistogramSize(int palette_code_bits);
+// Return the size of the histogram for a given cache_bits.
+int VP8LGetHistogramSize(int cache_bits);
// Set the palette_code_bits and reset the stats.
// If init_arrays is true, the arrays are also filled with 0's.
@@ -110,7 +110,7 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
const VP8LBackwardRefs* const refs,
int quality, int low_effort,
int histogram_bits, int cache_bits,
- VP8LHistogramSet* const image_in,
+ VP8LHistogramSet* const image_histo,
VP8LHistogram* const tmp_histo,
uint16_t* const histogram_symbols);
diff --git a/thirdparty/libwebp/src/enc/picture_csp_enc.c b/thirdparty/libwebp/src/enc/picture_csp_enc.c
index 718e014ed2..35eede9635 100644
--- a/thirdparty/libwebp/src/enc/picture_csp_enc.c
+++ b/thirdparty/libwebp/src/enc/picture_csp_enc.c
@@ -61,16 +61,14 @@ static int CheckNonOpaque(const uint8_t* alpha, int width, int height,
// Checking for the presence of non-opaque alpha.
int WebPPictureHasTransparency(const WebPPicture* picture) {
if (picture == NULL) return 0;
- if (!picture->use_argb) {
- return CheckNonOpaque(picture->a, picture->width, picture->height,
- 1, picture->a_stride);
- } else {
+ if (picture->use_argb) {
const int alpha_offset = ALPHA_OFFSET;
return CheckNonOpaque((const uint8_t*)picture->argb + alpha_offset,
picture->width, picture->height,
4, picture->argb_stride * sizeof(*picture->argb));
}
- return 0;
+ return CheckNonOpaque(picture->a, picture->width, picture->height,
+ 1, picture->a_stride);
}
//------------------------------------------------------------------------------
@@ -90,8 +88,9 @@ int WebPPictureHasTransparency(const WebPPicture* picture) {
static int kLinearToGammaTab[kGammaTabSize + 1];
static uint16_t kGammaToLinearTab[256];
static volatile int kGammaTablesOk = 0;
+static void InitGammaTables(void);
-static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTables(void) {
+WEBP_DSP_INIT_FUNC(InitGammaTables) {
if (!kGammaTablesOk) {
int v;
const double scale = (double)(1 << kGammaTabFix) / kGammaScale;
@@ -181,8 +180,9 @@ static uint32_t kLinearToGammaTabS[kGammaTabSize + 2];
#define GAMMA_TO_LINEAR_BITS 14
static uint32_t kGammaToLinearTabS[MAX_Y_T + 1]; // size scales with Y_FIX
static volatile int kGammaTablesSOk = 0;
+static void InitGammaTablesS(void);
-static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesS(void) {
+WEBP_DSP_INIT_FUNC(InitGammaTablesS) {
assert(2 * GAMMA_TO_LINEAR_BITS < 32); // we use uint32_t intermediate values
if (!kGammaTablesSOk) {
int v;
diff --git a/thirdparty/libwebp/src/enc/picture_rescale_enc.c b/thirdparty/libwebp/src/enc/picture_rescale_enc.c
index 58a6ae7b9d..a75f5d9c06 100644
--- a/thirdparty/libwebp/src/enc/picture_rescale_enc.c
+++ b/thirdparty/libwebp/src/enc/picture_rescale_enc.c
@@ -164,22 +164,25 @@ int WebPPictureCrop(WebPPicture* pic,
//------------------------------------------------------------------------------
// Simple picture rescaler
-static void RescalePlane(const uint8_t* src,
- int src_width, int src_height, int src_stride,
- uint8_t* dst,
- int dst_width, int dst_height, int dst_stride,
- rescaler_t* const work,
- int num_channels) {
+static int RescalePlane(const uint8_t* src,
+ int src_width, int src_height, int src_stride,
+ uint8_t* dst,
+ int dst_width, int dst_height, int dst_stride,
+ rescaler_t* const work,
+ int num_channels) {
WebPRescaler rescaler;
int y = 0;
- WebPRescalerInit(&rescaler, src_width, src_height,
- dst, dst_width, dst_height, dst_stride,
- num_channels, work);
+ if (!WebPRescalerInit(&rescaler, src_width, src_height,
+ dst, dst_width, dst_height, dst_stride,
+ num_channels, work)) {
+ return 0;
+ }
while (y < src_height) {
y += WebPRescalerImport(&rescaler, src_height - y,
src + y * src_stride, src_stride);
WebPRescalerExport(&rescaler);
}
+ return 1;
}
static void AlphaMultiplyARGB(WebPPicture* const pic, int inverse) {
@@ -222,25 +225,28 @@ int WebPPictureRescale(WebPPicture* pic, int width, int height) {
// If present, we need to rescale alpha first (for AlphaMultiplyY).
if (pic->a != NULL) {
WebPInitAlphaProcessing();
- RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
- tmp.a, width, height, tmp.a_stride, work, 1);
+ if (!RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
+ tmp.a, width, height, tmp.a_stride, work, 1)) {
+ return 0;
+ }
}
// We take transparency into account on the luma plane only. That's not
// totally exact blending, but still is a good approximation.
AlphaMultiplyY(pic, 0);
- RescalePlane(pic->y, prev_width, prev_height, pic->y_stride,
- tmp.y, width, height, tmp.y_stride, work, 1);
+ if (!RescalePlane(pic->y, prev_width, prev_height, pic->y_stride,
+ tmp.y, width, height, tmp.y_stride, work, 1) ||
+ !RescalePlane(pic->u,
+ HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
+ tmp.u,
+ HALVE(width), HALVE(height), tmp.uv_stride, work, 1) ||
+ !RescalePlane(pic->v,
+ HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
+ tmp.v,
+ HALVE(width), HALVE(height), tmp.uv_stride, work, 1)) {
+ return 0;
+ }
AlphaMultiplyY(&tmp, 1);
-
- RescalePlane(pic->u,
- HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
- tmp.u,
- HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
- RescalePlane(pic->v,
- HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
- tmp.v,
- HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
} else {
work = (rescaler_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work));
if (work == NULL) {
@@ -252,11 +258,12 @@ int WebPPictureRescale(WebPPicture* pic, int width, int height) {
// the premultiplication afterward (while preserving the alpha channel).
WebPInitAlphaProcessing();
AlphaMultiplyARGB(pic, 0);
- RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height,
- pic->argb_stride * 4,
- (uint8_t*)tmp.argb, width, height,
- tmp.argb_stride * 4,
- work, 4);
+ if (!RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height,
+ pic->argb_stride * 4,
+ (uint8_t*)tmp.argb, width, height,
+ tmp.argb_stride * 4, work, 4)) {
+ return 0;
+ }
AlphaMultiplyARGB(&tmp, 1);
}
WebPPictureFree(pic);
diff --git a/thirdparty/libwebp/src/enc/picture_tools_enc.c b/thirdparty/libwebp/src/enc/picture_tools_enc.c
index d0e8a495da..38cb01534a 100644
--- a/thirdparty/libwebp/src/enc/picture_tools_enc.c
+++ b/thirdparty/libwebp/src/enc/picture_tools_enc.c
@@ -83,6 +83,19 @@ static int SmoothenBlock(const uint8_t* a_ptr, int a_stride, uint8_t* y_ptr,
return (count == 0);
}
+void WebPReplaceTransparentPixels(WebPPicture* const pic, uint32_t color) {
+ if (pic != NULL && pic->use_argb) {
+ int y = pic->height;
+ uint32_t* argb = pic->argb;
+ color &= 0xffffffu; // force alpha=0
+ WebPInitAlphaProcessing();
+ while (y-- > 0) {
+ WebPAlphaReplace(argb, pic->width, color);
+ argb += pic->argb_stride;
+ }
+ }
+}
+
void WebPCleanupTransparentArea(WebPPicture* pic) {
int x, y, w, h;
if (pic == NULL) return;
@@ -165,24 +178,6 @@ void WebPCleanupTransparentArea(WebPPicture* pic) {
#undef SIZE
#undef SIZE2
-void WebPCleanupTransparentAreaLossless(WebPPicture* const pic) {
- int x, y, w, h;
- uint32_t* argb;
- assert(pic != NULL && pic->use_argb);
- w = pic->width;
- h = pic->height;
- argb = pic->argb;
-
- for (y = 0; y < h; ++y) {
- for (x = 0; x < w; ++x) {
- if ((argb[x] & 0xff000000) == 0) {
- argb[x] = 0x00000000;
- }
- }
- argb += pic->argb_stride;
- }
-}
-
//------------------------------------------------------------------------------
// Blend color and remove transparency info
diff --git a/thirdparty/libwebp/src/enc/syntax_enc.c b/thirdparty/libwebp/src/enc/syntax_enc.c
index a9e5a6cf0f..e18cf650ca 100644
--- a/thirdparty/libwebp/src/enc/syntax_enc.c
+++ b/thirdparty/libwebp/src/enc/syntax_enc.c
@@ -349,7 +349,7 @@ int VP8EncWrite(VP8Encoder* const enc) {
(enc->alpha_data_size_ & 1);
riff_size += CHUNK_HEADER_SIZE + padded_alpha_size;
}
- // Sanity check.
+ // RIFF size should fit in 32-bits.
if (riff_size > 0xfffffffeU) {
return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG);
}
diff --git a/thirdparty/libwebp/src/enc/vp8i_enc.h b/thirdparty/libwebp/src/enc/vp8i_enc.h
index fedcaeea27..67e9509367 100644
--- a/thirdparty/libwebp/src/enc/vp8i_enc.h
+++ b/thirdparty/libwebp/src/enc/vp8i_enc.h
@@ -31,8 +31,8 @@ extern "C" {
// version numbers
#define ENC_MAJ_VERSION 1
-#define ENC_MIN_VERSION 1
-#define ENC_REV_VERSION 0
+#define ENC_MIN_VERSION 2
+#define ENC_REV_VERSION 1
enum { MAX_LF_LEVELS = 64, // Maximum loop filter level
MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost
@@ -286,8 +286,7 @@ int VP8IteratorNext(VP8EncIterator* const it);
// save the yuv_out_ boundary values to top_/left_ arrays for next iterations.
void VP8IteratorSaveBoundary(VP8EncIterator* const it);
// Report progression based on macroblock rows. Return 0 for user-abort request.
-int VP8IteratorProgress(const VP8EncIterator* const it,
- int final_delta_percent);
+int VP8IteratorProgress(const VP8EncIterator* const it, int delta);
// Intra4x4 iterations
void VP8IteratorStartI4(VP8EncIterator* const it);
// returns true if not done.
@@ -505,9 +504,9 @@ int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height);
// Returns false in case of error (invalid param, out-of-memory).
int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height);
-// Clean-up the RGB samples under fully transparent area, to help lossless
-// compressibility (no guarantee, though). Assumes that pic->use_argb is true.
-void WebPCleanupTransparentAreaLossless(WebPPicture* const pic);
+// Replace samples that are fully transparent by 'color' to help compressibility
+// (no guarantee, though). Assumes pic->use_argb is true.
+void WebPReplaceTransparentPixels(WebPPicture* const pic, uint32_t color);
//------------------------------------------------------------------------------
diff --git a/thirdparty/libwebp/src/enc/vp8l_enc.c b/thirdparty/libwebp/src/enc/vp8l_enc.c
index 2efd403f77..e330e716f1 100644
--- a/thirdparty/libwebp/src/enc/vp8l_enc.c
+++ b/thirdparty/libwebp/src/enc/vp8l_enc.c
@@ -65,25 +65,22 @@ static WEBP_INLINE void SwapColor(uint32_t* const col1, uint32_t* const col2) {
*col2 = tmp;
}
-static void GreedyMinimizeDeltas(uint32_t palette[], int num_colors) {
- // Find greedily always the closest color of the predicted color to minimize
- // deltas in the palette. This reduces storage needs since the
- // palette is stored with delta encoding.
- uint32_t predict = 0x00000000;
- int i, k;
- for (i = 0; i < num_colors; ++i) {
- int best_ix = i;
- uint32_t best_score = ~0U;
- for (k = i; k < num_colors; ++k) {
- const uint32_t cur_score = PaletteColorDistance(palette[k], predict);
- if (best_score > cur_score) {
- best_score = cur_score;
- best_ix = k;
- }
+static WEBP_INLINE int SearchColorNoIdx(const uint32_t sorted[], uint32_t color,
+ int num_colors) {
+ int low = 0, hi = num_colors;
+ if (sorted[low] == color) return low; // loop invariant: sorted[low] != color
+ while (1) {
+ const int mid = (low + hi) >> 1;
+ if (sorted[mid] == color) {
+ return mid;
+ } else if (sorted[mid] < color) {
+ low = mid;
+ } else {
+ hi = mid;
}
- SwapColor(&palette[best_ix], &palette[i]);
- predict = palette[i];
}
+ assert(0);
+ return 0;
}
// The palette has been sorted by alpha. This function checks if the other
@@ -92,7 +89,8 @@ static void GreedyMinimizeDeltas(uint32_t palette[], int num_colors) {
// no benefit to re-organize them greedily. A monotonic development
// would be spotted in green-only situations (like lossy alpha) or gray-scale
// images.
-static int PaletteHasNonMonotonousDeltas(uint32_t palette[], int num_colors) {
+static int PaletteHasNonMonotonousDeltas(const uint32_t* const palette,
+ int num_colors) {
uint32_t predict = 0x000000;
int i;
uint8_t sign_found = 0x00;
@@ -115,28 +113,215 @@ static int PaletteHasNonMonotonousDeltas(uint32_t palette[], int num_colors) {
return (sign_found & (sign_found << 1)) != 0; // two consequent signs.
}
+static void PaletteSortMinimizeDeltas(const uint32_t* const palette_sorted,
+ int num_colors, uint32_t* const palette) {
+ uint32_t predict = 0x00000000;
+ int i, k;
+ memcpy(palette, palette_sorted, num_colors * sizeof(*palette));
+ if (!PaletteHasNonMonotonousDeltas(palette_sorted, num_colors)) return;
+ // Find greedily always the closest color of the predicted color to minimize
+ // deltas in the palette. This reduces storage needs since the
+ // palette is stored with delta encoding.
+ for (i = 0; i < num_colors; ++i) {
+ int best_ix = i;
+ uint32_t best_score = ~0U;
+ for (k = i; k < num_colors; ++k) {
+ const uint32_t cur_score = PaletteColorDistance(palette[k], predict);
+ if (best_score > cur_score) {
+ best_score = cur_score;
+ best_ix = k;
+ }
+ }
+ SwapColor(&palette[best_ix], &palette[i]);
+ predict = palette[i];
+ }
+}
+
+// Sort palette in increasing order and prepare an inverse mapping array.
+static void PrepareMapToPalette(const uint32_t palette[], uint32_t num_colors,
+ uint32_t sorted[], uint32_t idx_map[]) {
+ uint32_t i;
+ memcpy(sorted, palette, num_colors * sizeof(*sorted));
+ qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort);
+ for (i = 0; i < num_colors; ++i) {
+ idx_map[SearchColorNoIdx(sorted, palette[i], num_colors)] = i;
+ }
+}
+
// -----------------------------------------------------------------------------
-// Palette
+// Modified Zeng method from "A Survey on Palette Reordering
+// Methods for Improving the Compression of Color-Indexed Images" by Armando J.
+// Pinho and Antonio J. R. Neves.
+
+// Finds the biggest cooccurrence in the matrix.
+static void CoOccurrenceFindMax(const uint32_t* const cooccurrence,
+ uint32_t num_colors, uint8_t* const c1,
+ uint8_t* const c2) {
+ // Find the index that is most frequently located adjacent to other
+ // (different) indexes.
+ uint32_t best_sum = 0u;
+ uint32_t i, j, best_cooccurrence;
+ *c1 = 0u;
+ for (i = 0; i < num_colors; ++i) {
+ uint32_t sum = 0;
+ for (j = 0; j < num_colors; ++j) sum += cooccurrence[i * num_colors + j];
+ if (sum > best_sum) {
+ best_sum = sum;
+ *c1 = i;
+ }
+ }
+ // Find the index that is most frequently found adjacent to *c1.
+ *c2 = 0u;
+ best_cooccurrence = 0u;
+ for (i = 0; i < num_colors; ++i) {
+ if (cooccurrence[*c1 * num_colors + i] > best_cooccurrence) {
+ best_cooccurrence = cooccurrence[*c1 * num_colors + i];
+ *c2 = i;
+ }
+ }
+ assert(*c1 != *c2);
+}
-// If number of colors in the image is less than or equal to MAX_PALETTE_SIZE,
-// creates a palette and returns true, else returns false.
-static int AnalyzeAndCreatePalette(const WebPPicture* const pic,
- int low_effort,
- uint32_t palette[MAX_PALETTE_SIZE],
- int* const palette_size) {
- const int num_colors = WebPGetColorPalette(pic, palette);
- if (num_colors > MAX_PALETTE_SIZE) {
- *palette_size = 0;
- return 0;
+// Builds the cooccurrence matrix
+static WebPEncodingError CoOccurrenceBuild(const WebPPicture* const pic,
+ const uint32_t* const palette,
+ uint32_t num_colors,
+ uint32_t* cooccurrence) {
+ uint32_t *lines, *line_top, *line_current, *line_tmp;
+ int x, y;
+ const uint32_t* src = pic->argb;
+ uint32_t prev_pix = ~src[0];
+ uint32_t prev_idx = 0u;
+ uint32_t idx_map[MAX_PALETTE_SIZE] = {0};
+ uint32_t palette_sorted[MAX_PALETTE_SIZE];
+ lines = (uint32_t*)WebPSafeMalloc(2 * pic->width, sizeof(*lines));
+ if (lines == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
+ line_top = &lines[0];
+ line_current = &lines[pic->width];
+ PrepareMapToPalette(palette, num_colors, palette_sorted, idx_map);
+ for (y = 0; y < pic->height; ++y) {
+ for (x = 0; x < pic->width; ++x) {
+ const uint32_t pix = src[x];
+ if (pix != prev_pix) {
+ prev_idx = idx_map[SearchColorNoIdx(palette_sorted, pix, num_colors)];
+ prev_pix = pix;
+ }
+ line_current[x] = prev_idx;
+ // 4-connectivity is what works best as mentioned in "On the relation
+ // between Memon's and the modified Zeng's palette reordering methods".
+ if (x > 0 && prev_idx != line_current[x - 1]) {
+ const uint32_t left_idx = line_current[x - 1];
+ ++cooccurrence[prev_idx * num_colors + left_idx];
+ ++cooccurrence[left_idx * num_colors + prev_idx];
+ }
+ if (y > 0 && prev_idx != line_top[x]) {
+ const uint32_t top_idx = line_top[x];
+ ++cooccurrence[prev_idx * num_colors + top_idx];
+ ++cooccurrence[top_idx * num_colors + prev_idx];
+ }
+ }
+ line_tmp = line_top;
+ line_top = line_current;
+ line_current = line_tmp;
+ src += pic->argb_stride;
+ }
+ WebPSafeFree(lines);
+ return VP8_ENC_OK;
+}
+
+struct Sum {
+ uint8_t index;
+ uint32_t sum;
+};
+
+// Implements the modified Zeng method from "A Survey on Palette Reordering
+// Methods for Improving the Compression of Color-Indexed Images" by Armando J.
+// Pinho and Antonio J. R. Neves.
+static WebPEncodingError PaletteSortModifiedZeng(
+ const WebPPicture* const pic, const uint32_t* const palette_sorted,
+ uint32_t num_colors, uint32_t* const palette) {
+ uint32_t i, j, ind;
+ uint8_t remapping[MAX_PALETTE_SIZE];
+ uint32_t* cooccurrence;
+ struct Sum sums[MAX_PALETTE_SIZE];
+ uint32_t first, last;
+ uint32_t num_sums;
+ // TODO(vrabaud) check whether one color images should use palette or not.
+ if (num_colors <= 1) return VP8_ENC_OK;
+ // Build the co-occurrence matrix.
+ cooccurrence =
+ (uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence));
+ if (cooccurrence == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
+ if (CoOccurrenceBuild(pic, palette_sorted, num_colors, cooccurrence) !=
+ VP8_ENC_OK) {
+ WebPSafeFree(cooccurrence);
+ return VP8_ENC_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Initialize the mapping list with the two best indices.
+ CoOccurrenceFindMax(cooccurrence, num_colors, &remapping[0], &remapping[1]);
+
+ // We need to append and prepend to the list of remapping. To this end, we
+ // actually define the next start/end of the list as indices in a vector (with
+ // a wrap around when the end is reached).
+ first = 0;
+ last = 1;
+ num_sums = num_colors - 2; // -2 because we know the first two values
+ if (num_sums > 0) {
+ // Initialize the sums with the first two remappings and find the best one
+ struct Sum* best_sum = &sums[0];
+ best_sum->index = 0u;
+ best_sum->sum = 0u;
+ for (i = 0, j = 0; i < num_colors; ++i) {
+ if (i == remapping[0] || i == remapping[1]) continue;
+ sums[j].index = i;
+ sums[j].sum = cooccurrence[i * num_colors + remapping[0]] +
+ cooccurrence[i * num_colors + remapping[1]];
+ if (sums[j].sum > best_sum->sum) best_sum = &sums[j];
+ ++j;
+ }
+
+ while (num_sums > 0) {
+ const uint8_t best_index = best_sum->index;
+ // Compute delta to know if we need to prepend or append the best index.
+ int32_t delta = 0;
+ const int32_t n = num_colors - num_sums;
+ for (ind = first, j = 0; (ind + j) % num_colors != last + 1; ++j) {
+ const uint16_t l_j = remapping[(ind + j) % num_colors];
+ delta += (n - 1 - 2 * (int32_t)j) *
+ (int32_t)cooccurrence[best_index * num_colors + l_j];
+ }
+ if (delta > 0) {
+ first = (first == 0) ? num_colors - 1 : first - 1;
+ remapping[first] = best_index;
+ } else {
+ ++last;
+ remapping[last] = best_index;
+ }
+ // Remove best_sum from sums.
+ *best_sum = sums[num_sums - 1];
+ --num_sums;
+ // Update all the sums and find the best one.
+ best_sum = &sums[0];
+ for (i = 0; i < num_sums; ++i) {
+ sums[i].sum += cooccurrence[best_index * num_colors + sums[i].index];
+ if (sums[i].sum > best_sum->sum) best_sum = &sums[i];
+ }
+ }
}
- *palette_size = num_colors;
- qsort(palette, num_colors, sizeof(*palette), PaletteCompareColorsForQsort);
- if (!low_effort && PaletteHasNonMonotonousDeltas(palette, num_colors)) {
- GreedyMinimizeDeltas(palette, num_colors);
+ assert((last + 1) % num_colors == first);
+ WebPSafeFree(cooccurrence);
+
+ // Re-map the palette.
+ for (i = 0; i < num_colors; ++i) {
+ palette[i] = palette_sorted[remapping[(first + i) % num_colors]];
}
- return 1;
+ return VP8_ENC_OK;
}
+// -----------------------------------------------------------------------------
+// Palette
+
// These five modes are evaluated and their respective entropy is computed.
typedef enum {
kDirect = 0,
@@ -144,10 +329,18 @@ typedef enum {
kSubGreen = 2,
kSpatialSubGreen = 3,
kPalette = 4,
- kNumEntropyIx = 5
+ kPaletteAndSpatial = 5,
+ kNumEntropyIx = 6
} EntropyIx;
typedef enum {
+ kSortedDefault = 0,
+ kMinimizeDelta = 1,
+ kModifiedZeng = 2,
+ kUnusedPalette = 3,
+} PaletteSorting;
+
+typedef enum {
kHistoAlpha = 0,
kHistoAlphaPred,
kHistoGreen,
@@ -354,14 +547,21 @@ static int GetTransformBits(int method, int histo_bits) {
}
// Set of parameters to be used in each iteration of the cruncher.
-#define CRUNCH_CONFIGS_LZ77_MAX 2
+#define CRUNCH_SUBCONFIGS_MAX 2
+typedef struct {
+ int lz77_;
+ int do_no_cache_;
+} CrunchSubConfig;
typedef struct {
int entropy_idx_;
- int lz77s_types_to_try_[CRUNCH_CONFIGS_LZ77_MAX];
- int lz77s_types_to_try_size_;
+ PaletteSorting palette_sorting_type_;
+ CrunchSubConfig sub_configs_[CRUNCH_SUBCONFIGS_MAX];
+ int sub_configs_size_;
} CrunchConfig;
-#define CRUNCH_CONFIGS_MAX kNumEntropyIx
+// +2 because we add a palette sorting configuration for kPalette and
+// kPaletteAndSpatial.
+#define CRUNCH_CONFIGS_MAX (kNumEntropyIx + 2)
static int EncoderAnalyze(VP8LEncoder* const enc,
CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX],
@@ -376,11 +576,20 @@ static int EncoderAnalyze(VP8LEncoder* const enc,
int i;
int use_palette;
int n_lz77s;
+ // If set to 0, analyze the cache with the computed cache value. If 1, also
+ // analyze with no-cache.
+ int do_no_cache = 0;
assert(pic != NULL && pic->argb != NULL);
- use_palette =
- AnalyzeAndCreatePalette(pic, low_effort,
- enc->palette_, &enc->palette_size_);
+ // Check whether a palette is possible.
+ enc->palette_size_ = WebPGetColorPalette(pic, enc->palette_sorted_);
+ use_palette = (enc->palette_size_ <= MAX_PALETTE_SIZE);
+ if (!use_palette) {
+ enc->palette_size_ = 0;
+ } else {
+ qsort(enc->palette_sorted_, enc->palette_size_,
+ sizeof(*enc->palette_sorted_), PaletteCompareColorsForQsort);
+ }
// Empirical bit sizes.
enc->histo_bits_ = GetHistoBits(method, use_palette,
@@ -390,6 +599,8 @@ static int EncoderAnalyze(VP8LEncoder* const enc,
if (low_effort) {
// AnalyzeEntropy is somewhat slow.
crunch_configs[0].entropy_idx_ = use_palette ? kPalette : kSpatialSubGreen;
+ crunch_configs[0].palette_sorting_type_ =
+ use_palette ? kSortedDefault : kUnusedPalette;
n_lz77s = 1;
*crunch_configs_size = 1;
} else {
@@ -402,29 +613,59 @@ static int EncoderAnalyze(VP8LEncoder* const enc,
return 0;
}
if (method == 6 && config->quality == 100) {
+ do_no_cache = 1;
// Go brute force on all transforms.
*crunch_configs_size = 0;
for (i = 0; i < kNumEntropyIx; ++i) {
- if (i != kPalette || use_palette) {
+ // We can only apply kPalette or kPaletteAndSpatial if we can indeed use
+ // a palette.
+ if ((i != kPalette && i != kPaletteAndSpatial) || use_palette) {
assert(*crunch_configs_size < CRUNCH_CONFIGS_MAX);
- crunch_configs[(*crunch_configs_size)++].entropy_idx_ = i;
+ crunch_configs[(*crunch_configs_size)].entropy_idx_ = i;
+ if (use_palette && (i == kPalette || i == kPaletteAndSpatial)) {
+ crunch_configs[(*crunch_configs_size)].palette_sorting_type_ =
+ kMinimizeDelta;
+ ++*crunch_configs_size;
+ // Also add modified Zeng's method.
+ crunch_configs[(*crunch_configs_size)].entropy_idx_ = i;
+ crunch_configs[(*crunch_configs_size)].palette_sorting_type_ =
+ kModifiedZeng;
+ } else {
+ crunch_configs[(*crunch_configs_size)].palette_sorting_type_ =
+ kUnusedPalette;
+ }
+ ++*crunch_configs_size;
}
}
} else {
// Only choose the guessed best transform.
*crunch_configs_size = 1;
crunch_configs[0].entropy_idx_ = min_entropy_ix;
+ crunch_configs[0].palette_sorting_type_ =
+ use_palette ? kMinimizeDelta : kUnusedPalette;
+ if (config->quality >= 75 && method == 5) {
+ // Test with and without color cache.
+ do_no_cache = 1;
+ // If we have a palette, also check in combination with spatial.
+ if (min_entropy_ix == kPalette) {
+ *crunch_configs_size = 2;
+ crunch_configs[1].entropy_idx_ = kPaletteAndSpatial;
+ crunch_configs[1].palette_sorting_type_ = kMinimizeDelta;
+ }
+ }
}
}
// Fill in the different LZ77s.
- assert(n_lz77s <= CRUNCH_CONFIGS_LZ77_MAX);
+ assert(n_lz77s <= CRUNCH_SUBCONFIGS_MAX);
for (i = 0; i < *crunch_configs_size; ++i) {
int j;
for (j = 0; j < n_lz77s; ++j) {
- crunch_configs[i].lz77s_types_to_try_[j] =
+ assert(j < CRUNCH_SUBCONFIGS_MAX);
+ crunch_configs[i].sub_configs_[j].lz77_ =
(j == 0) ? kLZ77Standard | kLZ77RLE : kLZ77Box;
+ crunch_configs[i].sub_configs_[j].do_no_cache_ = do_no_cache;
}
- crunch_configs[i].lz77s_types_to_try_size_ = n_lz77s;
+ crunch_configs[i].sub_configs_size_ = n_lz77s;
}
return 1;
}
@@ -440,7 +681,7 @@ static int EncoderInit(VP8LEncoder* const enc) {
int i;
if (!VP8LHashChainInit(&enc->hash_chain_, pix_cnt)) return 0;
- for (i = 0; i < 3; ++i) VP8LBackwardRefsInit(&enc->refs_[i], refs_block_size);
+ for (i = 0; i < 4; ++i) VP8LBackwardRefsInit(&enc->refs_[i], refs_block_size);
return 1;
}
@@ -769,13 +1010,10 @@ static WebPEncodingError StoreImageToBitMask(
}
// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31
-static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
- const uint32_t* const argb,
- VP8LHashChain* const hash_chain,
- VP8LBackwardRefs* const refs_tmp1,
- VP8LBackwardRefs* const refs_tmp2,
- int width, int height,
- int quality, int low_effort) {
+static WebPEncodingError EncodeImageNoHuffman(
+ VP8LBitWriter* const bw, const uint32_t* const argb,
+ VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_array,
+ int width, int height, int quality, int low_effort) {
int i;
int max_tokens = 0;
WebPEncodingError err = VP8_ENC_OK;
@@ -798,13 +1036,11 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
goto Error;
}
- refs = VP8LGetBackwardReferences(width, height, argb, quality, 0,
- kLZ77Standard | kLZ77RLE, &cache_bits,
- hash_chain, refs_tmp1, refs_tmp2);
- if (refs == NULL) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
- }
+ err = VP8LGetBackwardReferences(
+ width, height, argb, quality, /*low_effort=*/0, kLZ77Standard | kLZ77RLE,
+ cache_bits, /*do_no_cache=*/0, hash_chain, refs_array, &cache_bits);
+ if (err != VP8_ENC_OK) goto Error;
+ refs = &refs_array[0];
histogram_image = VP8LAllocateHistogramSet(1, cache_bits);
if (histogram_image == NULL) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
@@ -860,11 +1096,11 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
static WebPEncodingError EncodeImageInternal(
VP8LBitWriter* const bw, const uint32_t* const argb,
- VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[3], int width,
+ VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[4], int width,
int height, int quality, int low_effort, int use_cache,
const CrunchConfig* const config, int* cache_bits, int histogram_bits,
size_t init_byte_position, int* const hdr_size, int* const data_size) {
- WebPEncodingError err = VP8_ENC_OK;
+ WebPEncodingError err = VP8_ENC_ERROR_OUT_OF_MEMORY;
const uint32_t histogram_image_xysize =
VP8LSubSampleSize(width, histogram_bits) *
VP8LSubSampleSize(height, histogram_bits);
@@ -876,103 +1112,103 @@ static WebPEncodingError EncodeImageInternal(
3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
HuffmanTreeToken* tokens = NULL;
HuffmanTreeCode* huffman_codes = NULL;
- VP8LBackwardRefs* refs_best;
- VP8LBackwardRefs* refs_tmp;
uint16_t* const histogram_symbols =
(uint16_t*)WebPSafeMalloc(histogram_image_xysize,
sizeof(*histogram_symbols));
- int lz77s_idx;
+ int sub_configs_idx;
+ int cache_bits_init, write_histogram_image;
VP8LBitWriter bw_init = *bw, bw_best;
int hdr_size_tmp;
+ VP8LHashChain hash_chain_histogram; // histogram image hash chain
+ size_t bw_size_best = ~(size_t)0;
assert(histogram_bits >= MIN_HUFFMAN_BITS);
assert(histogram_bits <= MAX_HUFFMAN_BITS);
assert(hdr_size != NULL);
assert(data_size != NULL);
- if (histogram_symbols == NULL) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ // Make sure we can allocate the different objects.
+ memset(&hash_chain_histogram, 0, sizeof(hash_chain_histogram));
+ if (huff_tree == NULL || histogram_symbols == NULL ||
+ !VP8LHashChainInit(&hash_chain_histogram, histogram_image_xysize) ||
+ !VP8LHashChainFill(hash_chain, quality, argb, width, height,
+ low_effort)) {
goto Error;
}
-
if (use_cache) {
// If the value is different from zero, it has been set during the
// palette analysis.
- if (*cache_bits == 0) *cache_bits = MAX_COLOR_CACHE_BITS;
+ cache_bits_init = (*cache_bits == 0) ? MAX_COLOR_CACHE_BITS : *cache_bits;
} else {
- *cache_bits = 0;
+ cache_bits_init = 0;
}
- // 'best_refs' is the reference to the best backward refs and points to one
- // of refs_array[0] or refs_array[1].
- // Calculate backward references from ARGB image.
- if (huff_tree == NULL ||
- !VP8LHashChainFill(hash_chain, quality, argb, width, height,
- low_effort) ||
- !VP8LBitWriterInit(&bw_best, 0) ||
- (config->lz77s_types_to_try_size_ > 1 &&
+ // If several iterations will happen, clone into bw_best.
+ if (!VP8LBitWriterInit(&bw_best, 0) ||
+ ((config->sub_configs_size_ > 1 ||
+ config->sub_configs_[0].do_no_cache_) &&
!VP8LBitWriterClone(bw, &bw_best))) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
goto Error;
}
- for (lz77s_idx = 0; lz77s_idx < config->lz77s_types_to_try_size_;
- ++lz77s_idx) {
- refs_best = VP8LGetBackwardReferences(
- width, height, argb, quality, low_effort,
- config->lz77s_types_to_try_[lz77s_idx], cache_bits, hash_chain,
- &refs_array[0], &refs_array[1]);
- if (refs_best == NULL) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
- }
- // Keep the best references aside and use the other element from the first
- // two as a temporary for later usage.
- refs_tmp = &refs_array[refs_best == &refs_array[0] ? 1 : 0];
-
- histogram_image =
- VP8LAllocateHistogramSet(histogram_image_xysize, *cache_bits);
- tmp_histo = VP8LAllocateHistogram(*cache_bits);
- if (histogram_image == NULL || tmp_histo == NULL) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
- }
-
- // Build histogram image and symbols from backward references.
- if (!VP8LGetHistoImageSymbols(width, height, refs_best, quality, low_effort,
- histogram_bits, *cache_bits, histogram_image,
- tmp_histo, histogram_symbols)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
- }
- // Create Huffman bit lengths and codes for each histogram image.
- histogram_image_size = histogram_image->size;
- bit_array_size = 5 * histogram_image_size;
- huffman_codes = (HuffmanTreeCode*)WebPSafeCalloc(bit_array_size,
- sizeof(*huffman_codes));
- // Note: some histogram_image entries may point to tmp_histos[], so the
- // latter need to outlive the following call to GetHuffBitLengthsAndCodes().
- if (huffman_codes == NULL ||
- !GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
- }
- // Free combined histograms.
- VP8LFreeHistogramSet(histogram_image);
- histogram_image = NULL;
-
- // Free scratch histograms.
- VP8LFreeHistogram(tmp_histo);
- tmp_histo = NULL;
+ for (sub_configs_idx = 0; sub_configs_idx < config->sub_configs_size_;
+ ++sub_configs_idx) {
+ const CrunchSubConfig* const sub_config =
+ &config->sub_configs_[sub_configs_idx];
+ int cache_bits_best, i_cache;
+ err = VP8LGetBackwardReferences(width, height, argb, quality, low_effort,
+ sub_config->lz77_, cache_bits_init,
+ sub_config->do_no_cache_, hash_chain,
+ &refs_array[0], &cache_bits_best);
+ if (err != VP8_ENC_OK) goto Error;
- // Color Cache parameters.
- if (*cache_bits > 0) {
- VP8LPutBits(bw, 1, 1);
- VP8LPutBits(bw, *cache_bits, 4);
- } else {
- VP8LPutBits(bw, 0, 1);
- }
+ for (i_cache = 0; i_cache < (sub_config->do_no_cache_ ? 2 : 1); ++i_cache) {
+ const int cache_bits_tmp = (i_cache == 0) ? cache_bits_best : 0;
+ // Speed-up: no need to study the no-cache case if it was already studied
+ // in i_cache == 0.
+ if (i_cache == 1 && cache_bits_best == 0) break;
+
+ // Reset the bit writer for this iteration.
+ VP8LBitWriterReset(&bw_init, bw);
+
+ // Build histogram image and symbols from backward references.
+ histogram_image =
+ VP8LAllocateHistogramSet(histogram_image_xysize, cache_bits_tmp);
+ tmp_histo = VP8LAllocateHistogram(cache_bits_tmp);
+ if (histogram_image == NULL || tmp_histo == NULL ||
+ !VP8LGetHistoImageSymbols(width, height, &refs_array[i_cache],
+ quality, low_effort, histogram_bits,
+ cache_bits_tmp, histogram_image, tmp_histo,
+ histogram_symbols)) {
+ goto Error;
+ }
+ // Create Huffman bit lengths and codes for each histogram image.
+ histogram_image_size = histogram_image->size;
+ bit_array_size = 5 * histogram_image_size;
+ huffman_codes = (HuffmanTreeCode*)WebPSafeCalloc(bit_array_size,
+ sizeof(*huffman_codes));
+ // Note: some histogram_image entries may point to tmp_histos[], so the
+ // latter need to outlive the following call to
+ // GetHuffBitLengthsAndCodes().
+ if (huffman_codes == NULL ||
+ !GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
+ goto Error;
+ }
+ // Free combined histograms.
+ VP8LFreeHistogramSet(histogram_image);
+ histogram_image = NULL;
+
+ // Free scratch histograms.
+ VP8LFreeHistogram(tmp_histo);
+ tmp_histo = NULL;
+
+ // Color Cache parameters.
+ if (cache_bits_tmp > 0) {
+ VP8LPutBits(bw, 1, 1);
+ VP8LPutBits(bw, cache_bits_tmp, 4);
+ } else {
+ VP8LPutBits(bw, 0, 1);
+ }
- // Huffman image + meta huffman.
- {
- const int write_histogram_image = (histogram_image_size > 1);
+ // Huffman image + meta huffman.
+ write_histogram_image = (histogram_image_size > 1);
VP8LPutBits(bw, write_histogram_image, 1);
if (write_histogram_image) {
uint32_t* const histogram_argb =
@@ -980,10 +1216,7 @@ static WebPEncodingError EncodeImageInternal(
sizeof(*histogram_argb));
int max_index = 0;
uint32_t i;
- if (histogram_argb == NULL) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
- }
+ if (histogram_argb == NULL) goto Error;
for (i = 0; i < histogram_image_xysize; ++i) {
const int symbol_index = histogram_symbols[i] & 0xffff;
histogram_argb[i] = (symbol_index << 8);
@@ -995,65 +1228,64 @@ static WebPEncodingError EncodeImageInternal(
VP8LPutBits(bw, histogram_bits - 2, 3);
err = EncodeImageNoHuffman(
- bw, histogram_argb, hash_chain, refs_tmp, &refs_array[2],
+ bw, histogram_argb, &hash_chain_histogram, &refs_array[2],
VP8LSubSampleSize(width, histogram_bits),
VP8LSubSampleSize(height, histogram_bits), quality, low_effort);
WebPSafeFree(histogram_argb);
if (err != VP8_ENC_OK) goto Error;
}
- }
- // Store Huffman codes.
- {
- int i;
- int max_tokens = 0;
- // Find maximum number of symbols for the huffman tree-set.
- for (i = 0; i < 5 * histogram_image_size; ++i) {
- HuffmanTreeCode* const codes = &huffman_codes[i];
- if (max_tokens < codes->num_symbols) {
- max_tokens = codes->num_symbols;
+ // Store Huffman codes.
+ {
+ int i;
+ int max_tokens = 0;
+ // Find maximum number of symbols for the huffman tree-set.
+ for (i = 0; i < 5 * histogram_image_size; ++i) {
+ HuffmanTreeCode* const codes = &huffman_codes[i];
+ if (max_tokens < codes->num_symbols) {
+ max_tokens = codes->num_symbols;
+ }
+ }
+ tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens));
+ if (tokens == NULL) goto Error;
+ for (i = 0; i < 5 * histogram_image_size; ++i) {
+ HuffmanTreeCode* const codes = &huffman_codes[i];
+ StoreHuffmanCode(bw, huff_tree, tokens, codes);
+ ClearHuffmanTreeIfOnlyOneSymbol(codes);
}
}
- tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens));
- if (tokens == NULL) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
+ // Store actual literals.
+ hdr_size_tmp = (int)(VP8LBitWriterNumBytes(bw) - init_byte_position);
+ err = StoreImageToBitMask(bw, width, histogram_bits, &refs_array[i_cache],
+ histogram_symbols, huffman_codes);
+ if (err != VP8_ENC_OK) goto Error;
+ // Keep track of the smallest image so far.
+ if (VP8LBitWriterNumBytes(bw) < bw_size_best) {
+ bw_size_best = VP8LBitWriterNumBytes(bw);
+ *cache_bits = cache_bits_tmp;
+ *hdr_size = hdr_size_tmp;
+ *data_size =
+ (int)(VP8LBitWriterNumBytes(bw) - init_byte_position - *hdr_size);
+ VP8LBitWriterSwap(bw, &bw_best);
}
- for (i = 0; i < 5 * histogram_image_size; ++i) {
- HuffmanTreeCode* const codes = &huffman_codes[i];
- StoreHuffmanCode(bw, huff_tree, tokens, codes);
- ClearHuffmanTreeIfOnlyOneSymbol(codes);
+ WebPSafeFree(tokens);
+ tokens = NULL;
+ if (huffman_codes != NULL) {
+ WebPSafeFree(huffman_codes->codes);
+ WebPSafeFree(huffman_codes);
+ huffman_codes = NULL;
}
}
- // Store actual literals.
- hdr_size_tmp = (int)(VP8LBitWriterNumBytes(bw) - init_byte_position);
- err = StoreImageToBitMask(bw, width, histogram_bits, refs_best,
- histogram_symbols, huffman_codes);
- // Keep track of the smallest image so far.
- if (lz77s_idx == 0 ||
- VP8LBitWriterNumBytes(bw) < VP8LBitWriterNumBytes(&bw_best)) {
- *hdr_size = hdr_size_tmp;
- *data_size =
- (int)(VP8LBitWriterNumBytes(bw) - init_byte_position - *hdr_size);
- VP8LBitWriterSwap(bw, &bw_best);
- }
- // Reset the bit writer for the following iteration if any.
- if (config->lz77s_types_to_try_size_ > 1) VP8LBitWriterReset(&bw_init, bw);
- WebPSafeFree(tokens);
- tokens = NULL;
- if (huffman_codes != NULL) {
- WebPSafeFree(huffman_codes->codes);
- WebPSafeFree(huffman_codes);
- huffman_codes = NULL;
- }
}
VP8LBitWriterSwap(bw, &bw_best);
+ err = VP8_ENC_OK;
Error:
WebPSafeFree(tokens);
WebPSafeFree(huff_tree);
VP8LFreeHistogramSet(histogram_image);
VP8LFreeHistogram(tmp_histo);
+ VP8LHashChainClear(&hash_chain_histogram);
if (huffman_codes != NULL) {
WebPSafeFree(huffman_codes->codes);
WebPSafeFree(huffman_codes);
@@ -1095,8 +1327,7 @@ static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc,
VP8LPutBits(bw, pred_bits - 2, 3);
return EncodeImageNoHuffman(
bw, enc->transform_data_, (VP8LHashChain*)&enc->hash_chain_,
- (VP8LBackwardRefs*)&enc->refs_[0], // cast const away
- (VP8LBackwardRefs*)&enc->refs_[1], transform_width, transform_height,
+ (VP8LBackwardRefs*)&enc->refs_[0], transform_width, transform_height,
quality, low_effort);
}
@@ -1116,8 +1347,7 @@ static WebPEncodingError ApplyCrossColorFilter(const VP8LEncoder* const enc,
VP8LPutBits(bw, ccolor_transform_bits - 2, 3);
return EncodeImageNoHuffman(
bw, enc->transform_data_, (VP8LHashChain*)&enc->hash_chain_,
- (VP8LBackwardRefs*)&enc->refs_[0], // cast const away
- (VP8LBackwardRefs*)&enc->refs_[1], transform_width, transform_height,
+ (VP8LBackwardRefs*)&enc->refs_[0], transform_width, transform_height,
quality, low_effort);
}
@@ -1272,22 +1502,6 @@ static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) {
// -----------------------------------------------------------------------------
-static WEBP_INLINE int SearchColorNoIdx(const uint32_t sorted[], uint32_t color,
- int hi) {
- int low = 0;
- if (sorted[low] == color) return low; // loop invariant: sorted[low] != color
- while (1) {
- const int mid = (low + hi) >> 1;
- if (sorted[mid] == color) {
- return mid;
- } else if (sorted[mid] < color) {
- low = mid;
- } else {
- hi = mid;
- }
- }
-}
-
#define APPLY_PALETTE_GREEDY_MAX 4
static WEBP_INLINE uint32_t SearchColorGreedy(const uint32_t palette[],
@@ -1322,17 +1536,6 @@ static WEBP_INLINE uint32_t ApplyPaletteHash2(uint32_t color) {
(32 - PALETTE_INV_SIZE_BITS);
}
-// Sort palette in increasing order and prepare an inverse mapping array.
-static void PrepareMapToPalette(const uint32_t palette[], int num_colors,
- uint32_t sorted[], uint32_t idx_map[]) {
- int i;
- memcpy(sorted, palette, num_colors * sizeof(*sorted));
- qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort);
- for (i = 0; i < num_colors; ++i) {
- idx_map[SearchColorNoIdx(sorted, palette[i], num_colors)] = i;
- }
-}
-
// Use 1 pixel cache for ARGB pixels.
#define APPLY_PALETTE_FOR(COLOR_INDEX) do { \
uint32_t prev_pix = palette[0]; \
@@ -1464,8 +1667,8 @@ static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, int low_effort,
}
tmp_palette[0] = palette[0];
return EncodeImageNoHuffman(bw, tmp_palette, &enc->hash_chain_,
- &enc->refs_[0], &enc->refs_[1], palette_size, 1,
- 20 /* quality */, low_effort);
+ &enc->refs_[0], palette_size, 1, /*quality=*/20,
+ low_effort);
}
// -----------------------------------------------------------------------------
@@ -1491,7 +1694,7 @@ static void VP8LEncoderDelete(VP8LEncoder* enc) {
if (enc != NULL) {
int i;
VP8LHashChainClear(&enc->hash_chain_);
- for (i = 0; i < 3; ++i) VP8LBackwardRefsClear(&enc->refs_[i]);
+ for (i = 0; i < 4; ++i) VP8LBackwardRefsClear(&enc->refs_[i]);
ClearTransformBuffer(enc);
WebPSafeFree(enc);
}
@@ -1541,7 +1744,7 @@ static int EncodeStreamHook(void* input, void* data2) {
int data_size = 0;
int use_delta_palette = 0;
int idx;
- size_t best_size = 0;
+ size_t best_size = ~(size_t)0;
VP8LBitWriter bw_init = *bw, bw_best;
(void)data2;
@@ -1553,12 +1756,15 @@ static int EncodeStreamHook(void* input, void* data2) {
for (idx = 0; idx < num_crunch_configs; ++idx) {
const int entropy_idx = crunch_configs[idx].entropy_idx_;
- enc->use_palette_ = (entropy_idx == kPalette);
+ enc->use_palette_ =
+ (entropy_idx == kPalette) || (entropy_idx == kPaletteAndSpatial);
enc->use_subtract_green_ =
(entropy_idx == kSubGreen) || (entropy_idx == kSpatialSubGreen);
- enc->use_predict_ =
- (entropy_idx == kSpatial) || (entropy_idx == kSpatialSubGreen);
- if (low_effort) {
+ enc->use_predict_ = (entropy_idx == kSpatial) ||
+ (entropy_idx == kSpatialSubGreen) ||
+ (entropy_idx == kPaletteAndSpatial);
+ // When using a palette, R/B==0, hence no need to test for cross-color.
+ if (low_effort || enc->use_palette_) {
enc->use_cross_color_ = 0;
} else {
enc->use_cross_color_ = red_and_blue_always_zero ? 0 : enc->use_predict_;
@@ -1590,6 +1796,19 @@ static int EncodeStreamHook(void* input, void* data2) {
// Encode palette
if (enc->use_palette_) {
+ if (crunch_configs[idx].palette_sorting_type_ == kSortedDefault) {
+ // Nothing to do, we have already sorted the palette.
+ memcpy(enc->palette_, enc->palette_sorted_,
+ enc->palette_size_ * sizeof(*enc->palette_));
+ } else if (crunch_configs[idx].palette_sorting_type_ == kMinimizeDelta) {
+ PaletteSortMinimizeDeltas(enc->palette_sorted_, enc->palette_size_,
+ enc->palette_);
+ } else {
+ assert(crunch_configs[idx].palette_sorting_type_ == kModifiedZeng);
+ err = PaletteSortModifiedZeng(enc->pic_, enc->palette_sorted_,
+ enc->palette_size_, enc->palette_);
+ if (err != VP8_ENC_OK) goto Error;
+ }
err = EncodePalette(bw, low_effort, enc);
if (err != VP8_ENC_OK) goto Error;
err = MapImageFromPalette(enc, use_delta_palette);
@@ -1640,7 +1859,7 @@ static int EncodeStreamHook(void* input, void* data2) {
if (err != VP8_ENC_OK) goto Error;
// If we are better than what we already have.
- if (idx == 0 || VP8LBitWriterNumBytes(bw) < best_size) {
+ if (VP8LBitWriterNumBytes(bw) < best_size) {
best_size = VP8LBitWriterNumBytes(bw);
// Store the BitWriter.
VP8LBitWriterSwap(bw, &bw_best);
@@ -1754,6 +1973,8 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
enc_side->palette_size_ = enc_main->palette_size_;
memcpy(enc_side->palette_, enc_main->palette_,
sizeof(enc_main->palette_));
+ memcpy(enc_side->palette_sorted_, enc_main->palette_sorted_,
+ sizeof(enc_main->palette_sorted_));
param->enc_ = enc_side;
}
// Create the workers.
@@ -1816,7 +2037,7 @@ Error:
}
#undef CRUNCH_CONFIGS_MAX
-#undef CRUNCH_CONFIGS_LZ77_MAX
+#undef CRUNCH_SUBCONFIGS_MAX
int VP8LEncodeImage(const WebPConfig* const config,
const WebPPicture* const picture) {
diff --git a/thirdparty/libwebp/src/enc/vp8li_enc.h b/thirdparty/libwebp/src/enc/vp8li_enc.h
index d2d0fc509c..00de48946c 100644
--- a/thirdparty/libwebp/src/enc/vp8li_enc.h
+++ b/thirdparty/libwebp/src/enc/vp8li_enc.h
@@ -69,9 +69,11 @@ typedef struct {
int use_palette_;
int palette_size_;
uint32_t palette_[MAX_PALETTE_SIZE];
+ // Sorted version of palette_ for cache purposes.
+ uint32_t palette_sorted_[MAX_PALETTE_SIZE];
// Some 'scratch' (potentially large) objects.
- struct VP8LBackwardRefs refs_[3]; // Backward Refs array for temporaries.
+ struct VP8LBackwardRefs refs_[4]; // Backward Refs array for temporaries.
VP8LHashChain hash_chain_; // HashChain data for constructing
// backward references.
} VP8LEncoder;
diff --git a/thirdparty/libwebp/src/enc/webp_enc.c b/thirdparty/libwebp/src/enc/webp_enc.c
index 9f4b10c26c..ce2db2e94b 100644
--- a/thirdparty/libwebp/src/enc/webp_enc.c
+++ b/thirdparty/libwebp/src/enc/webp_enc.c
@@ -400,7 +400,7 @@ int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
}
if (!config->exact) {
- WebPCleanupTransparentAreaLossless(pic);
+ WebPReplaceTransparentPixels(pic, 0x000000);
}
ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem.