diff options
Diffstat (limited to 'drivers/webp/enc')
-rw-r--r-- | drivers/webp/enc/alpha.c | 320 | ||||
-rw-r--r-- | drivers/webp/enc/analysis.c | 347 | ||||
-rw-r--r-- | drivers/webp/enc/backward_references.c | 298 | ||||
-rw-r--r-- | drivers/webp/enc/backward_references.h | 74 | ||||
-rw-r--r-- | drivers/webp/enc/config.c | 34 | ||||
-rw-r--r-- | drivers/webp/enc/cost.c | 219 | ||||
-rw-r--r-- | drivers/webp/enc/cost.h | 15 | ||||
-rw-r--r-- | drivers/webp/enc/filter.c | 132 | ||||
-rw-r--r-- | drivers/webp/enc/frame.c | 767 | ||||
-rw-r--r-- | drivers/webp/enc/histogram.c | 254 | ||||
-rw-r--r-- | drivers/webp/enc/histogram.h | 28 | ||||
-rw-r--r-- | drivers/webp/enc/iterator.c | 208 | ||||
-rw-r--r-- | drivers/webp/enc/layer.c | 15 | ||||
-rw-r--r-- | drivers/webp/enc/picture.c | 453 | ||||
-rw-r--r-- | drivers/webp/enc/quant.c | 458 | ||||
-rw-r--r-- | drivers/webp/enc/syntax.c | 40 | ||||
-rw-r--r-- | drivers/webp/enc/token.c | 273 | ||||
-rw-r--r-- | drivers/webp/enc/tree.c | 22 | ||||
-rw-r--r-- | drivers/webp/enc/vp8enci.h | 203 | ||||
-rw-r--r-- | drivers/webp/enc/vp8l.c | 224 | ||||
-rw-r--r-- | drivers/webp/enc/vp8li.h | 12 | ||||
-rw-r--r-- | drivers/webp/enc/webpenc.c | 167 |
22 files changed, 1615 insertions, 2948 deletions
diff --git a/drivers/webp/enc/alpha.c b/drivers/webp/enc/alpha.c index 21d4b5cbde..0e519b6c66 100644 --- a/drivers/webp/enc/alpha.c +++ b/drivers/webp/enc/alpha.c @@ -1,10 +1,8 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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/ // ----------------------------------------------------------------------------- // // Alpha-plane compression. @@ -19,6 +17,10 @@ #include "../utils/quant_levels.h" #include "../webp/format_constants.h" +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + // ----------------------------------------------------------------------------- // Encodes the given alpha data via specified compression method 'method'. // The pre-processing (quantization) is performed if 'quality' is less than 100. @@ -67,7 +69,7 @@ static int EncodeLossless(const uint8_t* const data, int width, int height, const uint8_t* src = data; for (j = 0; j < picture.height; ++j) { for (i = 0; i < picture.width; ++i) { - dst[i] = src[i] << 8; // we leave A/R/B channels zero'd. + dst[i] = (src[i] << 8) | 0xff000000u; } src += width; dst += picture.argb_stride; @@ -77,19 +79,18 @@ static int EncodeLossless(const uint8_t* const data, int width, int height, WebPConfigInit(&config); config.lossless = 1; config.method = effort_level; // impact is very small - // Set a low default quality for encoding alpha. Ensure that Alpha quality at - // lower methods (3 and below) is less than the threshold for triggering - // costly 'BackwardReferencesTraceBackwards'. - config.quality = 8.f * effort_level; - assert(config.quality >= 0 && config.quality <= 100.f); + // Set moderate default quality setting for alpha. Higher qualities (80 and + // above) could be very slow. + config.quality = 10.f + 15.f * effort_level; + if (config.quality > 100.f) config.quality = 100.f; ok = VP8LBitWriterInit(&tmp_bw, (width * height) >> 3); ok = ok && (VP8LEncodeStream(&config, &picture, &tmp_bw) == VP8_ENC_OK); WebPPictureFree(&picture); if (ok) { - const uint8_t* const buffer = VP8LBitWriterFinish(&tmp_bw); - const size_t buffer_size = VP8LBitWriterNumBytes(&tmp_bw); - VP8BitWriterAppend(bw, buffer, buffer_size); + const uint8_t* const data = VP8LBitWriterFinish(&tmp_bw); + const size_t data_size = VP8LBitWriterNumBytes(&tmp_bw); + VP8BitWriterAppend(bw, data, data_size); } VP8LBitWriterDestroy(&tmp_bw); return ok && !bw->error_; @@ -97,19 +98,12 @@ static int EncodeLossless(const uint8_t* const data, int width, int height, // ----------------------------------------------------------------------------- -// Small struct to hold the result of a filter mode compression attempt. -typedef struct { - size_t score; - VP8BitWriter bw; - WebPAuxStats stats; -} FilterTrial; - -// This function always returns an initialized 'bw' object, even upon error. static int EncodeAlphaInternal(const uint8_t* const data, int width, int height, int method, int filter, int reduce_levels, int effort_level, // in [0..6] range uint8_t* const tmp_alpha, - FilterTrial* result) { + VP8BitWriter* const bw, + WebPAuxStats* const stats) { int ok = 0; const uint8_t* alpha_src; WebPFilterFunc filter_func; @@ -130,26 +124,24 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height, header = method | (filter << 2); if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4; - VP8BitWriterInit(&result->bw, expected_size); - VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN); + VP8BitWriterInit(bw, expected_size); + VP8BitWriterAppend(bw, &header, ALPHA_HEADER_LEN); filter_func = WebPFilters[filter]; - if (filter_func != NULL) { - filter_func(data, width, height, width, tmp_alpha); + if (filter_func) { + filter_func(data, width, height, 1, width, tmp_alpha); alpha_src = tmp_alpha; } else { alpha_src = data; } if (method == ALPHA_NO_COMPRESSION) { - ok = VP8BitWriterAppend(&result->bw, alpha_src, width * height); - ok = ok && !result->bw.error_; + ok = VP8BitWriterAppend(bw, alpha_src, width * height); + ok = ok && !bw->error_; } else { - ok = EncodeLossless(alpha_src, width, height, effort_level, - &result->bw, &result->stats); - VP8BitWriterFinish(&result->bw); + ok = EncodeLossless(alpha_src, width, height, effort_level, bw, stats); + VP8BitWriterFinish(bw); } - result->score = VP8BitWriterSize(&result->bw); return ok; } @@ -165,104 +157,6 @@ static void CopyPlane(const uint8_t* src, int src_stride, } } -static int GetNumColors(const uint8_t* data, int width, int height, - int stride) { - int j; - int colors = 0; - uint8_t color[256] = { 0 }; - - for (j = 0; j < height; ++j) { - int i; - const uint8_t* const p = data + j * stride; - for (i = 0; i < width; ++i) { - color[p[i]] = 1; - } - } - for (j = 0; j < 256; ++j) { - if (color[j] > 0) ++colors; - } - return colors; -} - -#define FILTER_TRY_NONE (1 << WEBP_FILTER_NONE) -#define FILTER_TRY_ALL ((1 << WEBP_FILTER_LAST) - 1) - -// Given the input 'filter' option, return an OR'd bit-set of filters to try. -static uint32_t GetFilterMap(const uint8_t* alpha, int width, int height, - int filter, int effort_level) { - uint32_t bit_map = 0U; - if (filter == WEBP_FILTER_FAST) { - // Quick estimate of the best candidate. - int try_filter_none = (effort_level > 3); - const int kMinColorsForFilterNone = 16; - const int kMaxColorsForFilterNone = 192; - const int num_colors = GetNumColors(alpha, width, height, width); - // For low number of colors, NONE yields better compression. - filter = (num_colors <= kMinColorsForFilterNone) ? WEBP_FILTER_NONE : - EstimateBestFilter(alpha, width, height, width); - bit_map |= 1 << filter; - // For large number of colors, try FILTER_NONE in addition to the best - // filter as well. - if (try_filter_none || num_colors > kMaxColorsForFilterNone) { - bit_map |= FILTER_TRY_NONE; - } - } else if (filter == WEBP_FILTER_NONE) { - bit_map = FILTER_TRY_NONE; - } else { // WEBP_FILTER_BEST -> try all - bit_map = FILTER_TRY_ALL; - } - return bit_map; -} - -static void InitFilterTrial(FilterTrial* const score) { - score->score = (size_t)~0U; - VP8BitWriterInit(&score->bw, 0); -} - -static int ApplyFiltersAndEncode(const uint8_t* alpha, int width, int height, - size_t data_size, int method, int filter, - int reduce_levels, int effort_level, - uint8_t** const output, - size_t* const output_size, - WebPAuxStats* const stats) { - int ok = 1; - FilterTrial best; - uint32_t try_map = - GetFilterMap(alpha, width, height, filter, effort_level); - InitFilterTrial(&best); - if (try_map != FILTER_TRY_NONE) { - uint8_t* filtered_alpha = (uint8_t*)malloc(data_size); - if (filtered_alpha == NULL) return 0; - - for (filter = WEBP_FILTER_NONE; ok && try_map; ++filter, try_map >>= 1) { - if (try_map & 1) { - FilterTrial trial; - ok = EncodeAlphaInternal(alpha, width, height, method, filter, - reduce_levels, effort_level, filtered_alpha, - &trial); - if (ok && trial.score < best.score) { - VP8BitWriterWipeOut(&best.bw); - best = trial; - } else { - VP8BitWriterWipeOut(&trial.bw); - } - } - } - free(filtered_alpha); - } else { - ok = EncodeAlphaInternal(alpha, width, height, method, WEBP_FILTER_NONE, - reduce_levels, effort_level, NULL, &best); - } - if (ok) { - if (stats != NULL) *stats = best.stats; - *output_size = VP8BitWriterSize(&best.bw); - *output = VP8BitWriterBuf(&best.bw); - } else { - VP8BitWriterWipeOut(&best.bw); - } - return ok; -} - static int EncodeAlpha(VP8Encoder* const enc, int quality, int method, int filter, int effort_level, @@ -293,11 +187,6 @@ static int EncodeAlpha(VP8Encoder* const enc, return 0; } - if (method == ALPHA_NO_COMPRESSION) { - // Don't filter, as filtering will make no impact on compressed size. - filter = WEBP_FILTER_NONE; - } - quant_alpha = (uint8_t*)malloc(data_size); if (quant_alpha == NULL) { return 0; @@ -316,95 +205,126 @@ static int EncodeAlpha(VP8Encoder* const enc, } if (ok) { - ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method, - filter, reduce_levels, effort_level, output, - output_size, pic->stats); - if (pic->stats != NULL) { // need stats? - pic->stats->coded_size += (int)(*output_size); - enc->sse_[3] = sse; + VP8BitWriter bw; + int test_filter; + uint8_t* filtered_alpha = NULL; + + // We always test WEBP_FILTER_NONE first. + ok = EncodeAlphaInternal(quant_alpha, width, height, + method, WEBP_FILTER_NONE, reduce_levels, + effort_level, NULL, &bw, pic->stats); + if (!ok) { + VP8BitWriterWipeOut(&bw); + goto End; } - } + if (filter == WEBP_FILTER_FAST) { // Quick estimate of a second candidate? + filter = EstimateBestFilter(quant_alpha, width, height, width); + } + // Stop? + if (filter == WEBP_FILTER_NONE) { + goto Ok; + } + + filtered_alpha = (uint8_t*)malloc(data_size); + ok = (filtered_alpha != NULL); + if (!ok) { + goto End; + } + + // Try the other mode(s). + { + WebPAuxStats best_stats; + size_t best_score = VP8BitWriterSize(&bw); + + memset(&best_stats, 0, sizeof(best_stats)); // prevent spurious warning + if (pic->stats != NULL) best_stats = *pic->stats; + for (test_filter = WEBP_FILTER_HORIZONTAL; + ok && (test_filter <= WEBP_FILTER_GRADIENT); + ++test_filter) { + VP8BitWriter tmp_bw; + if (filter != WEBP_FILTER_BEST && test_filter != filter) { + continue; + } + ok = EncodeAlphaInternal(quant_alpha, width, height, + method, test_filter, reduce_levels, + effort_level, filtered_alpha, &tmp_bw, + pic->stats); + if (ok) { + const size_t score = VP8BitWriterSize(&tmp_bw); + if (score < best_score) { + // swap bitwriter objects. + VP8BitWriter tmp = tmp_bw; + tmp_bw = bw; + bw = tmp; + best_score = score; + if (pic->stats != NULL) best_stats = *pic->stats; + } + } else { + VP8BitWriterWipeOut(&bw); + } + VP8BitWriterWipeOut(&tmp_bw); + } + if (pic->stats != NULL) *pic->stats = best_stats; + } + Ok: + if (ok) { + *output_size = VP8BitWriterSize(&bw); + *output = VP8BitWriterBuf(&bw); + if (pic->stats != NULL) { // need stats? + pic->stats->coded_size += (int)(*output_size); + enc->sse_[3] = sse; + } + } + free(filtered_alpha); + } + End: free(quant_alpha); return ok; } + //------------------------------------------------------------------------------ // Main calls -static int CompressAlphaJob(VP8Encoder* const enc, void* dummy) { - const WebPConfig* config = enc->config_; - uint8_t* alpha_data = NULL; - size_t alpha_size = 0; - const int effort_level = config->method; // maps to [0..6] - const WEBP_FILTER_TYPE filter = - (config->alpha_filtering == 0) ? WEBP_FILTER_NONE : - (config->alpha_filtering == 1) ? WEBP_FILTER_FAST : - WEBP_FILTER_BEST; - if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression, - filter, effort_level, &alpha_data, &alpha_size)) { - return 0; - } - if (alpha_size != (uint32_t)alpha_size) { // Sanity check. - free(alpha_data); - return 0; - } - enc->alpha_data_size_ = (uint32_t)alpha_size; - enc->alpha_data_ = alpha_data; - (void)dummy; - return 1; -} - void VP8EncInitAlpha(VP8Encoder* const enc) { enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_); enc->alpha_data_ = NULL; enc->alpha_data_size_ = 0; - if (enc->thread_level_ > 0) { - WebPWorker* const worker = &enc->alpha_worker_; - WebPWorkerInit(worker); - worker->data1 = enc; - worker->data2 = NULL; - worker->hook = (WebPWorkerHook)CompressAlphaJob; - } -} - -int VP8EncStartAlpha(VP8Encoder* const enc) { - if (enc->has_alpha_) { - if (enc->thread_level_ > 0) { - WebPWorker* const worker = &enc->alpha_worker_; - if (!WebPWorkerReset(worker)) { // Makes sure worker is good to go. - return 0; - } - WebPWorkerLaunch(worker); - return 1; - } else { - return CompressAlphaJob(enc, NULL); // just do the job right away - } - } - return 1; } int VP8EncFinishAlpha(VP8Encoder* const enc) { if (enc->has_alpha_) { - if (enc->thread_level_ > 0) { - WebPWorker* const worker = &enc->alpha_worker_; - if (!WebPWorkerSync(worker)) return 0; // error + const WebPConfig* config = enc->config_; + uint8_t* tmp_data = NULL; + size_t tmp_size = 0; + const int effort_level = config->method; // maps to [0..6] + const WEBP_FILTER_TYPE filter = + (config->alpha_filtering == 0) ? WEBP_FILTER_NONE : + (config->alpha_filtering == 1) ? WEBP_FILTER_FAST : + WEBP_FILTER_BEST; + + if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression, + filter, effort_level, &tmp_data, &tmp_size)) { + return 0; + } + if (tmp_size != (uint32_t)tmp_size) { // Sanity check. + free(tmp_data); + return 0; } + enc->alpha_data_size_ = (uint32_t)tmp_size; + enc->alpha_data_ = tmp_data; } return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_); } -int VP8EncDeleteAlpha(VP8Encoder* const enc) { - int ok = 1; - if (enc->thread_level_ > 0) { - WebPWorker* const worker = &enc->alpha_worker_; - ok = WebPWorkerSync(worker); // finish anything left in flight - WebPWorkerEnd(worker); // still need to end the worker, even if !ok - } +void VP8EncDeleteAlpha(VP8Encoder* const enc) { free(enc->alpha_data_); enc->alpha_data_ = NULL; enc->alpha_data_size_ = 0; enc->has_alpha_ = 0; - return ok; } +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/drivers/webp/enc/analysis.c b/drivers/webp/enc/analysis.c index 7d4cfdc190..22cfb492e7 100644 --- a/drivers/webp/enc/analysis.c +++ b/drivers/webp/enc/analysis.c @@ -1,10 +1,8 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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 @@ -19,8 +17,16 @@ #include "./cost.h" #include "../utils/utils.h" +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + #define MAX_ITERS_K_MEANS 6 +static int ClipAlpha(int alpha) { + return alpha < 0 ? 0 : alpha > 255 ? 255 : alpha; +} + //------------------------------------------------------------------------------ // Smooth the segment map by replacing isolated block by the majority of its // neighbours. @@ -51,7 +57,6 @@ static void SmoothSegmentMap(VP8Encoder* const enc) { for (n = 0; n < NUM_MB_SEGMENTS; ++n) { if (cnt[n] >= majority_cnt_3_x_3_grid) { majority_seg = n; - break; } } tmp[x + y * w] = majority_seg; @@ -67,10 +72,50 @@ static void SmoothSegmentMap(VP8Encoder* const enc) { } //------------------------------------------------------------------------------ -// set segment susceptibility alpha_ / beta_ +// 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 WEBP_INLINE int clip(int v, int m, int M) { - return (v < m) ? m : (v > M) ? M : v; + return v < m ? m : v > M ? M : v; } static void SetSegmentAlphas(VP8Encoder* const enc, @@ -97,72 +142,28 @@ static void SetSegmentAlphas(VP8Encoder* const enc, } //------------------------------------------------------------------------------ -// Compute susceptibility based on DCT-coeff histograms: -// the higher, the "easier" the macroblock is to compress. - -#define MAX_ALPHA 255 // 8b of precision for susceptibilities. -#define ALPHA_SCALE (2 * MAX_ALPHA) // scaling factor for alpha. -#define DEFAULT_ALPHA (-1) -#define IS_BETTER_ALPHA(alpha, best_alpha) ((alpha) > (best_alpha)) - -static int FinalAlphaValue(int alpha) { - alpha = MAX_ALPHA - alpha; - return clip(alpha, 0, MAX_ALPHA); -} - -static int GetAlpha(const VP8Histogram* const histo) { - int max_value = 0, last_non_zero = 1; - int k; - int alpha; - for (k = 0; k <= MAX_COEFF_THRESH; ++k) { - const int value = histo->distribution[k]; - if (value > 0) { - if (value > max_value) max_value = value; - last_non_zero = k; - } - } - // 'alpha' will later be clipped to [0..MAX_ALPHA] range, clamping outer - // values which happen to be mostly noise. This leaves the maximum precision - // for handling the useful small values which contribute most. - alpha = (max_value > 1) ? ALPHA_SCALE * last_non_zero / max_value : 0; - return alpha; -} - -static void MergeHistograms(const VP8Histogram* const in, - VP8Histogram* const out) { - int i; - for (i = 0; i <= MAX_COEFF_THRESH; ++i) { - out->distribution[i] += in->distribution[i]; - } -} - -//------------------------------------------------------------------------------ // Simplified k-Means, to assign Nb segments based on alpha-histogram -static void AssignSegments(VP8Encoder* const enc, - const int alphas[MAX_ALPHA + 1]) { +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 = 0; - int map[MAX_ALPHA + 1]; + int map[256]; int a, n, k; - int min_a = 0, max_a = MAX_ALPHA, range_a; + 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]; - assert(nb >= 1); - // bracket the input - for (n = 0; n <= MAX_ALPHA && alphas[n] == 0; ++n) {} + for (n = 0; n < 256 && alphas[n] == 0; ++n) {} min_a = n; - for (n = MAX_ALPHA; n > min_a && alphas[n] == 0; --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 (k = 0, n = 1; k < nb; ++k, n += 2) { - assert(n < 2 * nb); - centers[k] = min_a + (n * range_a) / (2 * nb); + 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 @@ -177,7 +178,7 @@ static void AssignSegments(VP8Encoder* const enc, n = 0; // track the nearest center for current 'a' for (a = min_a; a <= max_a; ++a) { if (alphas[a]) { - while (n + 1 < nb && abs(a - centers[n + 1]) < abs(a - centers[n])) { + while (n < nb - 1 && abs(a - centers[n + 1]) < abs(a - centers[n])) { n++; } map[a] = n; @@ -209,7 +210,7 @@ static void AssignSegments(VP8Encoder* const enc, VP8MBInfo* const mb = &enc->mb_info_[n]; const int alpha = mb->alpha_; mb->segment_ = map[alpha]; - mb->alpha_ = centers[map[alpha]]; // for the record. + mb->alpha_ = centers[map[alpha]]; // just for the record. } if (nb > 1) { @@ -217,6 +218,7 @@ static void AssignSegments(VP8Encoder* const enc, if (smooth) SmoothSegmentMap(enc); } + SetSegmentProbas(enc); // Assign final proba SetSegmentAlphas(enc, centers, weighted_average); // pick some alphas. } @@ -225,32 +227,24 @@ static void AssignSegments(VP8Encoder* const enc, // susceptibility and set best modes for this macroblock. // Segment assignment is done later. -// Number of modes to inspect for alpha_ evaluation. For high-quality settings -// (method >= FAST_ANALYSIS_METHOD) we don't need to test all the possible modes -// during the analysis phase. -#define FAST_ANALYSIS_METHOD 4 // method above which we do partial analysis +// 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_ >= FAST_ANALYSIS_METHOD) ? MAX_INTRA16_MODE - : NUM_PRED_MODES; + const int max_mode = (it->enc_->method_ >= 3) ? MAX_INTRA16_MODE : 4; int mode; - int best_alpha = DEFAULT_ALPHA; + int best_alpha = -1; int best_mode = 0; VP8MakeLuma16Preds(it); for (mode = 0; mode < max_mode; ++mode) { - VP8Histogram histo = { { 0 } }; - int alpha; - - VP8CollectHistogram(it->yuv_in_ + Y_OFF, - it->yuv_p_ + VP8I16ModeOffsets[mode], - 0, 16, &histo); - alpha = GetAlpha(&histo); - if (IS_BETTER_ALPHA(alpha, best_alpha)) { + const int alpha = VP8CollectHistogram(it->yuv_in_ + Y_OFF, + it->yuv_p_ + VP8I16ModeOffsets[mode], + 0, 16); + if (alpha > best_alpha) { best_alpha = alpha; best_mode = mode; } @@ -262,63 +256,46 @@ static int MBAnalyzeBestIntra16Mode(VP8EncIterator* const it) { static int MBAnalyzeBestIntra4Mode(VP8EncIterator* const it, int best_alpha) { uint8_t modes[16]; - const int max_mode = - (it->enc_->method_ >= FAST_ANALYSIS_METHOD) ? MAX_INTRA4_MODE - : NUM_BMODES; - int i4_alpha; - VP8Histogram total_histo = { { 0 } }; - int cur_histo = 0; - + 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 = DEFAULT_ALPHA; - VP8Histogram histos[2]; + 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) { - int alpha; - - memset(&histos[cur_histo], 0, sizeof(histos[cur_histo])); - VP8CollectHistogram(src, it->yuv_p_ + VP8I4ModeOffsets[mode], - 0, 1, &histos[cur_histo]); - alpha = GetAlpha(&histos[cur_histo]); - if (IS_BETTER_ALPHA(alpha, best_mode_alpha)) { + const int alpha = VP8CollectHistogram(src, + it->yuv_p_ + VP8I4ModeOffsets[mode], + 0, 1); + if (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); + i4_alpha += best_mode_alpha; // Note: we reuse the original samples for predictors } while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF)); - i4_alpha = GetAlpha(&total_histo); - if (IS_BETTER_ALPHA(i4_alpha, best_alpha)) { + if (i4_alpha > best_alpha) { VP8SetIntra4Mode(it, modes); - best_alpha = i4_alpha; + best_alpha = ClipAlpha(i4_alpha); } return best_alpha; } static int MBAnalyzeBestUVMode(VP8EncIterator* const it) { - int best_alpha = DEFAULT_ALPHA; + int best_alpha = -1; int best_mode = 0; - const int max_mode = - (it->enc_->method_ >= FAST_ANALYSIS_METHOD) ? MAX_UV_MODE - : NUM_PRED_MODES; + const int max_mode = (it->enc_->method_ >= 3) ? MAX_UV_MODE : 4; int mode; VP8MakeChroma8Preds(it); for (mode = 0; mode < max_mode; ++mode) { - VP8Histogram histo = { { 0 } }; - int alpha; - VP8CollectHistogram(it->yuv_in_ + U_OFF, - it->yuv_p_ + VP8UVModeOffsets[mode], - 16, 16 + 4 + 4, &histo); - alpha = GetAlpha(&histo); - if (IS_BETTER_ALPHA(alpha, best_alpha)) { + const int alpha = VP8CollectHistogram(it->yuv_in_ + U_OFF, + it->yuv_p_ + VP8UVModeOffsets[mode], + 16, 16 + 4 + 4); + if (alpha > best_alpha) { best_alpha = alpha; best_mode = mode; } @@ -328,8 +305,7 @@ static int MBAnalyzeBestUVMode(VP8EncIterator* const it) { } static void MBAnalyze(VP8EncIterator* const it, - int alphas[MAX_ALPHA + 1], - int* const alpha, int* const uv_alpha) { + int alphas[256], int* const uv_alpha) { const VP8Encoder* const enc = it->enc_; int best_alpha, best_uv_alpha; @@ -338,7 +314,7 @@ static void MBAnalyze(VP8EncIterator* const it, VP8SetSegment(it, 0); // default segment, spec-wise. best_alpha = MBAnalyzeBestIntra16Mode(it); - if (enc->method_ >= 5) { + 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. @@ -348,22 +324,10 @@ static void MBAnalyze(VP8EncIterator* const it, best_uv_alpha = MBAnalyzeBestUVMode(it); // Final susceptibility mix - best_alpha = (3 * best_alpha + best_uv_alpha + 2) >> 2; - best_alpha = FinalAlphaValue(best_alpha); + best_alpha = (best_alpha + best_uv_alpha + 1) / 2; alphas[best_alpha]++; - it->mb_->alpha_ = best_alpha; // for later remapping. - - // Accumulate for later complexity analysis. - *alpha += best_alpha; // mixed susceptibility (not just luma) *uv_alpha += best_uv_alpha; -} - -static void DefaultMBInfo(VP8MBInfo* const mb) { - mb->type_ = 1; // I16x16 - mb->uv_mode_ = 0; - mb->skip_ = 0; // not skipped - mb->segment_ = 0; // default segment - mb->alpha_ = 0; + it->mb_->alpha_ = best_alpha; // Informative only. } //------------------------------------------------------------------------------ @@ -376,122 +340,25 @@ static void DefaultMBInfo(VP8MBInfo* const mb) { // and decide intra4/intra16, but that's usually almost always a bad choice at // this stage. -static void ResetAllMBInfo(VP8Encoder* const enc) { - int n; - for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { - DefaultMBInfo(&enc->mb_info_[n]); - } - // Default susceptibilities. - enc->dqm_[0].alpha_ = 0; - enc->dqm_[0].beta_ = 0; - // Note: we can't compute this alpha_ / uv_alpha_ -> set to default value. - enc->alpha_ = 0; - enc->uv_alpha_ = 0; - WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_); -} - -// struct used to collect job result -typedef struct { - WebPWorker worker; - int alphas[MAX_ALPHA + 1]; - int alpha, uv_alpha; - VP8EncIterator it; - int delta_progress; -} SegmentJob; - -// main work call -static int DoSegmentsJob(SegmentJob* const job, VP8EncIterator* const it) { +int VP8EncAnalyze(VP8Encoder* const enc) { int ok = 1; - if (!VP8IteratorIsDone(it)) { - uint8_t tmp[32 + ALIGN_CST]; - uint8_t* const scratch = (uint8_t*)DO_ALIGN(tmp); - do { - // Let's pretend we have perfect lossless reconstruction. - VP8IteratorImport(it, scratch); - MBAnalyze(it, job->alphas, &job->alpha, &job->uv_alpha); - ok = VP8IteratorProgress(it, job->delta_progress); - } while (ok && VP8IteratorNext(it)); - } - return ok; -} - -static void MergeJobs(const SegmentJob* const src, SegmentJob* const dst) { - int i; - for (i = 0; i <= MAX_ALPHA; ++i) dst->alphas[i] += src->alphas[i]; - dst->alpha += src->alpha; - dst->uv_alpha += src->uv_alpha; -} + int alphas[256] = { 0 }; + VP8EncIterator it; -// initialize the job struct with some TODOs -static void InitSegmentJob(VP8Encoder* const enc, SegmentJob* const job, - int start_row, int end_row) { - WebPWorkerInit(&job->worker); - job->worker.data1 = job; - job->worker.data2 = &job->it; - job->worker.hook = (WebPWorkerHook)DoSegmentsJob; - VP8IteratorInit(enc, &job->it); - VP8IteratorSetRow(&job->it, start_row); - VP8IteratorSetCountDown(&job->it, (end_row - start_row) * enc->mb_w_); - memset(job->alphas, 0, sizeof(job->alphas)); - job->alpha = 0; - job->uv_alpha = 0; - // only one of both jobs can record the progress, since we don't - // expect the user's hook to be multi-thread safe - job->delta_progress = (start_row == 0) ? 20 : 0; -} + VP8IteratorInit(enc, &it); + enc->uv_alpha_ = 0; + do { + VP8IteratorImport(&it); + MBAnalyze(&it, alphas, &enc->uv_alpha_); + ok = VP8IteratorProgress(&it, 20); + // Let's pretend we have perfect lossless reconstruction. + } while (ok && VP8IteratorNext(&it, it.yuv_in_)); + enc->uv_alpha_ /= enc->mb_w_ * enc->mb_h_; + if (ok) AssignSegments(enc, alphas); -// main entry point -int VP8EncAnalyze(VP8Encoder* const enc) { - int ok = 1; - const int do_segments = - enc->config_->emulate_jpeg_size || // We need the complexity evaluation. - (enc->segment_hdr_.num_segments_ > 1) || - (enc->method_ == 0); // for method 0, we need preds_[] to be filled. - if (do_segments) { - const int last_row = enc->mb_h_; - // We give a little more than a half work to the main thread. - const int split_row = (9 * last_row + 15) >> 4; - const int total_mb = last_row * enc->mb_w_; -#ifdef WEBP_USE_THREAD - const int kMinSplitRow = 2; // minimal rows needed for mt to be worth it - const int do_mt = (enc->thread_level_ > 0) && (split_row >= kMinSplitRow); -#else - const int do_mt = 0; -#endif - SegmentJob main_job; - if (do_mt) { - SegmentJob side_job; - // Note the use of '&' instead of '&&' because we must call the functions - // no matter what. - InitSegmentJob(enc, &main_job, 0, split_row); - InitSegmentJob(enc, &side_job, split_row, last_row); - // we don't need to call Reset() on main_job.worker, since we're calling - // WebPWorkerExecute() on it - ok &= WebPWorkerReset(&side_job.worker); - // launch the two jobs in parallel - if (ok) { - WebPWorkerLaunch(&side_job.worker); - WebPWorkerExecute(&main_job.worker); - ok &= WebPWorkerSync(&side_job.worker); - ok &= WebPWorkerSync(&main_job.worker); - } - WebPWorkerEnd(&side_job.worker); - if (ok) MergeJobs(&side_job, &main_job); // merge results together - } else { - // Even for single-thread case, we use the generic Worker tools. - InitSegmentJob(enc, &main_job, 0, last_row); - WebPWorkerExecute(&main_job.worker); - ok &= WebPWorkerSync(&main_job.worker); - } - WebPWorkerEnd(&main_job.worker); - if (ok) { - enc->alpha_ = main_job.alpha / total_mb; - enc->uv_alpha_ = main_job.uv_alpha / total_mb; - AssignSegments(enc, main_job.alphas); - } - } else { // Use only one default segment. - ResetAllMBInfo(enc); - } return ok; } +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/drivers/webp/enc/backward_references.c b/drivers/webp/enc/backward_references.c index 77b4be7432..b8c8ece806 100644 --- a/drivers/webp/enc/backward_references.c +++ b/drivers/webp/enc/backward_references.c @@ -1,10 +1,8 @@ // Copyright 2012 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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/ // ----------------------------------------------------------------------------- // // Author: Jyrki Alakuijala (jyrki@google.com) @@ -143,95 +141,74 @@ static void HashChainInsert(HashChain* const p, p->hash_to_first_index_[hash_code] = pos; } -static void GetParamsForHashChainFindCopy(int quality, int xsize, - int cache_bits, int* window_size, - int* iter_pos, int* iter_limit) { - const int iter_mult = (quality < 27) ? 1 : 1 + ((quality - 27) >> 4); - const int iter_neg = -iter_mult * (quality >> 1); - // Limit the backward-ref window size for lower qualities. - const int max_window_size = (quality > 50) ? WINDOW_SIZE - : (quality > 25) ? (xsize << 8) - : (xsize << 4); - assert(xsize > 0); - *window_size = (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE - : max_window_size; - *iter_pos = 8 + (quality >> 3); - // For lower entropy images, the rigorous search loop in HashChainFindCopy - // can be relaxed. - *iter_limit = (cache_bits > 0) ? iter_neg : iter_neg / 2; -} - static int HashChainFindCopy(const HashChain* const p, - int base_position, int xsize_signed, - const uint32_t* const argb, int max_len, - int window_size, int iter_pos, int iter_limit, + int quality, int index, int xsize, + const uint32_t* const argb, int maxlen, int* const distance_ptr, int* const length_ptr) { - const uint32_t* const argb_start = argb + base_position; - uint64_t best_val = 0; - uint32_t best_length = 1; - uint32_t best_distance = 0; - const uint32_t xsize = (uint32_t)xsize_signed; - const int min_pos = - (base_position > window_size) ? base_position - window_size : 0; + const uint64_t hash_code = GetPixPairHash64(&argb[index]); + int prev_length = 0; + int64_t best_val = 0; + int best_length = 0; + int best_distance = 0; + const uint32_t* const argb_start = argb + index; + const int iter_min_mult = (quality < 50) ? 2 : (quality < 75) ? 4 : 8; + const int iter_min = -quality * iter_min_mult; + int iter_cnt = 10 + (quality >> 1); + const int min_pos = (index > WINDOW_SIZE) ? index - WINDOW_SIZE : 0; int pos; + assert(xsize > 0); - if (max_len > MAX_LENGTH) { - max_len = MAX_LENGTH; - } - for (pos = p->hash_to_first_index_[GetPixPairHash64(argb_start)]; + for (pos = p->hash_to_first_index_[hash_code]; pos >= min_pos; pos = p->chain_[pos]) { - uint64_t val; - uint32_t curr_length; - uint32_t distance; - const uint64_t* const ptr1 = - (const uint64_t*)(argb + pos + best_length - 1); - const uint64_t* const ptr2 = - (const uint64_t*)(argb_start + best_length - 1); - - if (iter_pos < 0) { - if (iter_pos < iter_limit || best_val >= 0xff0000) { + int64_t val; + int curr_length; + if (iter_cnt < 0) { + if (iter_cnt < iter_min || best_val >= 0xff0000) { break; } } - --iter_pos; - - // Before 'expensive' linear match, check if the two arrays match at the - // current best length index and also for the succeeding elements. - if (*ptr1 != *ptr2) continue; - - curr_length = FindMatchLength(argb + pos, argb_start, max_len); - if (curr_length < best_length) continue; - - distance = (uint32_t)(base_position - pos); - val = curr_length << 16; + --iter_cnt; + if (best_length != 0 && + argb[pos + best_length - 1] != argb_start[best_length - 1]) { + continue; + } + curr_length = FindMatchLength(argb + pos, argb_start, maxlen); + if (curr_length < prev_length) { + continue; + } + val = 65536 * curr_length; // Favoring 2d locality here gives savings for certain images. - if (distance < 9 * xsize) { - const uint32_t y = distance / xsize; - uint32_t x = distance % xsize; - if (x > (xsize >> 1)) { + if (index - pos < 9 * xsize) { + const int y = (index - pos) / xsize; + int x = (index - pos) % xsize; + if (x > xsize / 2) { x = xsize - x; } - if (x <= 7) { - val += 9 * 9 + 9 * 9; + if (x <= 7 && x >= -8) { val -= y * y + x * x; + } else { + val -= 9 * 9 + 9 * 9; } + } else { + val -= 9 * 9 + 9 * 9; } if (best_val < val) { + prev_length = curr_length; best_val = val; best_length = curr_length; - best_distance = distance; - if (curr_length >= (uint32_t)max_len) { + best_distance = index - pos; + if (curr_length >= MAX_LENGTH) { break; } - if ((best_distance == 1 || distance == xsize) && + if ((best_distance == 1 || best_distance == xsize) && best_length >= 128) { break; } } } - *distance_ptr = (int)best_distance; + *distance_ptr = best_distance; *length_ptr = best_length; return (best_length >= MIN_LENGTH); } @@ -280,9 +257,6 @@ static int BackwardReferencesHashChain(int xsize, int ysize, const int pix_count = xsize * ysize; HashChain* const hash_chain = (HashChain*)malloc(sizeof(*hash_chain)); VP8LColorCache hashers; - int window_size = WINDOW_SIZE; - int iter_pos = 1; - int iter_limit = -1; if (hash_chain == NULL) return 0; if (use_color_cache) { @@ -293,16 +267,16 @@ static int BackwardReferencesHashChain(int xsize, int ysize, if (!HashChainInit(hash_chain, pix_count)) goto Error; refs->size = 0; - GetParamsForHashChainFindCopy(quality, xsize, cache_bits, - &window_size, &iter_pos, &iter_limit); for (i = 0; i < pix_count; ) { // Alternative#1: Code the pixels starting at 'i' using backward reference. int offset = 0; int len = 0; if (i < pix_count - 1) { // FindCopy(i,..) reads pixels at [i] and [i + 1]. - int max_len = pix_count - i; - HashChainFindCopy(hash_chain, i, xsize, argb, max_len, - window_size, iter_pos, iter_limit, + int maxlen = pix_count - i; + if (maxlen > MAX_LENGTH) { + maxlen = MAX_LENGTH; + } + HashChainFindCopy(hash_chain, quality, i, xsize, argb, maxlen, &offset, &len); } if (len >= MIN_LENGTH) { @@ -313,10 +287,12 @@ static int BackwardReferencesHashChain(int xsize, int ysize, int k; HashChainInsert(hash_chain, &argb[i], i); if (i < pix_count - 2) { // FindCopy(i+1,..) reads [i + 1] and [i + 2]. - int max_len = pix_count - (i + 1); - HashChainFindCopy(hash_chain, i + 1, xsize, argb, max_len, - window_size, iter_pos, iter_limit, - &offset2, &len2); + int maxlen = pix_count - (i + 1); + if (maxlen > MAX_LENGTH) { + maxlen = MAX_LENGTH; + } + HashChainFindCopy(hash_chain, quality, + i + 1, xsize, argb, maxlen, &offset2, &len2); if (len2 > len + 1) { const uint32_t pixel = argb[i]; // Alternative#2 is a better match. So push pixel at 'i' as literal. @@ -324,10 +300,10 @@ static int BackwardReferencesHashChain(int xsize, int ysize, const int ix = VP8LColorCacheGetIndex(&hashers, pixel); refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix); } else { - if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel); refs->refs[refs->size] = PixOrCopyCreateLiteral(pixel); } ++refs->size; + if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel); i++; // Backward reference to be done for next pixel. len = len2; offset = offset2; @@ -357,10 +333,10 @@ static int BackwardReferencesHashChain(int xsize, int ysize, const int ix = VP8LColorCacheGetIndex(&hashers, pixel); refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix); } else { - if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel); refs->refs[refs->size] = PixOrCopyCreateLiteral(pixel); } ++refs->size; + if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel); if (i + 1 < pix_count) { HashChainInsert(hash_chain, &argb[i], i); } @@ -386,8 +362,7 @@ typedef struct { static int BackwardReferencesTraceBackwards( int xsize, int ysize, int recursive_cost_model, - const uint32_t* const argb, int quality, int cache_bits, - VP8LBackwardRefs* const refs); + const uint32_t* const argb, int cache_bits, VP8LBackwardRefs* const refs); static void ConvertPopulationCountTableToBitEstimates( int num_symbols, const int population_counts[], double output[]) { @@ -412,16 +387,17 @@ static void ConvertPopulationCountTableToBitEstimates( static int CostModelBuild(CostModel* const m, int xsize, int ysize, int recursion_level, const uint32_t* const argb, - int quality, int cache_bits) { + int cache_bits) { int ok = 0; VP8LHistogram histo; VP8LBackwardRefs refs; + const int quality = 100; if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize)) goto Error; if (recursion_level > 0) { if (!BackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1, - argb, quality, cache_bits, &refs)) { + argb, cache_bits, &refs)) { goto Error; } } else { @@ -462,37 +438,34 @@ static WEBP_INLINE double GetCacheCost(const CostModel* const m, uint32_t idx) { static WEBP_INLINE double GetLengthCost(const CostModel* const m, uint32_t length) { - int code, extra_bits; - VP8LPrefixEncodeBits(length, &code, &extra_bits); - return m->literal_[VALUES_IN_BYTE + code] + extra_bits; + int code, extra_bits_count, extra_bits_value; + PrefixEncode(length, &code, &extra_bits_count, &extra_bits_value); + return m->literal_[VALUES_IN_BYTE + code] + extra_bits_count; } static WEBP_INLINE double GetDistanceCost(const CostModel* const m, uint32_t distance) { - int code, extra_bits; - VP8LPrefixEncodeBits(distance, &code, &extra_bits); - return m->distance_[code] + extra_bits; + int code, extra_bits_count, extra_bits_value; + PrefixEncode(distance, &code, &extra_bits_count, &extra_bits_value); + return m->distance_[code] + extra_bits_count; } static int BackwardReferencesHashChainDistanceOnly( int xsize, int ysize, int recursive_cost_model, const uint32_t* const argb, - int quality, int cache_bits, uint32_t* const dist_array) { + int cache_bits, uint32_t* const dist_array) { int i; int ok = 0; int cc_init = 0; + const int quality = 100; const int pix_count = xsize * ysize; const int use_color_cache = (cache_bits > 0); - float* const cost = - (float*)WebPSafeMalloc((uint64_t)pix_count, sizeof(*cost)); + double* const cost = + (double*)WebPSafeMalloc((uint64_t)pix_count, sizeof(*cost)); CostModel* cost_model = (CostModel*)malloc(sizeof(*cost_model)); HashChain* hash_chain = (HashChain*)malloc(sizeof(*hash_chain)); VP8LColorCache hashers; const double mul0 = (recursive_cost_model != 0) ? 1.0 : 0.68; const double mul1 = (recursive_cost_model != 0) ? 1.0 : 0.82; - const int min_distance_code = 2; // TODO(vikasa): tune as function of quality - int window_size = WINDOW_SIZE; - int iter_pos = 1; - int iter_limit = -1; if (cost == NULL || cost_model == NULL || hash_chain == NULL) goto Error; @@ -504,17 +477,15 @@ static int BackwardReferencesHashChainDistanceOnly( } if (!CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, argb, - quality, cache_bits)) { + cache_bits)) { goto Error; } - for (i = 0; i < pix_count; ++i) cost[i] = 1e38f; + for (i = 0; i < pix_count; ++i) cost[i] = 1e100; // We loop one pixel at a time, but store all currently best points to // non-processed locations from this point. dist_array[0] = 0; - GetParamsForHashChainFindCopy(quality, xsize, cache_bits, - &window_size, &iter_pos, &iter_limit); for (i = 0; i < pix_count; ++i) { double prev_cost = 0.0; int shortmax; @@ -525,9 +496,11 @@ static int BackwardReferencesHashChainDistanceOnly( int offset = 0; int len = 0; if (i < pix_count - 1) { // FindCopy reads pixels at [i] and [i + 1]. - int max_len = shortmax ? 2 : pix_count - i; - HashChainFindCopy(hash_chain, i, xsize, argb, max_len, - window_size, iter_pos, iter_limit, + int maxlen = shortmax ? 2 : MAX_LENGTH; + if (maxlen > pix_count - i) { + maxlen = pix_count - i; + } + HashChainFindCopy(hash_chain, quality, i, xsize, argb, maxlen, &offset, &len); } if (len >= MIN_LENGTH) { @@ -536,15 +509,16 @@ static int BackwardReferencesHashChainDistanceOnly( prev_cost + GetDistanceCost(cost_model, code); int k; for (k = 1; k < len; ++k) { - const double cost_val = distance_cost + GetLengthCost(cost_model, k); + const double cost_val = + distance_cost + GetLengthCost(cost_model, k); if (cost[i + k] > cost_val) { - cost[i + k] = (float)cost_val; + cost[i + k] = cost_val; dist_array[i + k] = k + 1; } } // This if is for speedup only. It roughly doubles the speed, and // makes compression worse by .1 %. - if (len >= 128 && code <= min_distance_code) { + if (len >= 128 && code < 2) { // Long copy for short distances, let's skip the middle // lookups for better copies. // 1) insert the hashes. @@ -555,10 +529,10 @@ static int BackwardReferencesHashChainDistanceOnly( } // 2) Add to the hash_chain (but cannot add the last pixel) { - const int last = (len + i < pix_count - 1) ? len + i - : pix_count - 1; - for (k = i; k < last; ++k) { - HashChainInsert(hash_chain, &argb[k], k); + const int last = (len < pix_count - 1 - i) ? len + : pix_count - 1 - i; + for (k = 0; k < last; ++k) { + HashChainInsert(hash_chain, &argb[i + k], i + k); } } // 3) jump. @@ -577,13 +551,13 @@ static int BackwardReferencesHashChainDistanceOnly( const int ix = VP8LColorCacheGetIndex(&hashers, argb[i]); cost_val += GetCacheCost(cost_model, ix) * mul0; } else { - if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]); cost_val += GetLiteralCost(cost_model, argb[i]) * mul1; } if (cost[i] > cost_val) { - cost[i] = (float)cost_val; + cost[i] = cost_val; dist_array[i] = 1; // only one is inserted. } + if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]); } next_symbol: ; } @@ -598,30 +572,40 @@ Error: return ok; } -// We pack the path at the end of *dist_array and return -// a pointer to this part of the array. Example: -// dist_array = [1x2xx3x2] => packed [1x2x1232], chosen_path = [1232] -static void TraceBackwards(uint32_t* const dist_array, - int dist_array_size, - uint32_t** const chosen_path, - int* const chosen_path_size) { - uint32_t* path = dist_array + dist_array_size; - uint32_t* cur = dist_array + dist_array_size - 1; - while (cur >= dist_array) { - const int k = *cur; - --path; - *path = k; - cur -= k; - } - *chosen_path = path; - *chosen_path_size = (int)(dist_array + dist_array_size - path); +static int TraceBackwards(const uint32_t* const dist_array, + int dist_array_size, + uint32_t** const chosen_path, + int* const chosen_path_size) { + int i; + // Count how many. + int count = 0; + for (i = dist_array_size - 1; i >= 0; ) { + int k = dist_array[i]; + assert(k >= 1); + ++count; + i -= k; + } + // Allocate. + *chosen_path_size = count; + *chosen_path = + (uint32_t*)WebPSafeMalloc((uint64_t)count, sizeof(**chosen_path)); + if (*chosen_path == NULL) return 0; + + // Write in reverse order. + for (i = dist_array_size - 1; i >= 0; ) { + int k = dist_array[i]; + assert(k >= 1); + (*chosen_path)[--count] = k; + i -= k; + } + return 1; } static int BackwardReferencesHashChainFollowChosenPath( - int xsize, int ysize, const uint32_t* const argb, - int quality, int cache_bits, + int xsize, int ysize, const uint32_t* const argb, int cache_bits, const uint32_t* const chosen_path, int chosen_path_size, VP8LBackwardRefs* const refs) { + const int quality = 100; const int pix_count = xsize * ysize; const int use_color_cache = (cache_bits > 0); int size = 0; @@ -630,9 +614,6 @@ static int BackwardReferencesHashChainFollowChosenPath( int ix; int ok = 0; int cc_init = 0; - int window_size = WINDOW_SIZE; - int iter_pos = 1; - int iter_limit = -1; HashChain* hash_chain = (HashChain*)malloc(sizeof(*hash_chain)); VP8LColorCache hashers; @@ -645,17 +626,14 @@ static int BackwardReferencesHashChainFollowChosenPath( } refs->size = 0; - GetParamsForHashChainFindCopy(quality, xsize, cache_bits, - &window_size, &iter_pos, &iter_limit); for (ix = 0; ix < chosen_path_size; ++ix, ++size) { int offset = 0; int len = 0; - int max_len = chosen_path[ix]; - if (max_len != 1) { - HashChainFindCopy(hash_chain, i, xsize, argb, max_len, - window_size, iter_pos, iter_limit, - &offset, &len); - assert(len == max_len); + int maxlen = chosen_path[ix]; + if (maxlen != 1) { + HashChainFindCopy(hash_chain, quality, + i, xsize, argb, maxlen, &offset, &len); + assert(len == maxlen); refs->refs[size] = PixOrCopyCreateCopy(offset, len); if (use_color_cache) { for (k = 0; k < len; ++k) { @@ -675,9 +653,9 @@ static int BackwardReferencesHashChainFollowChosenPath( const int idx = VP8LColorCacheGetIndex(&hashers, argb[i]); refs->refs[size] = PixOrCopyCreateCacheIdx(idx); } else { - if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]); refs->refs[size] = PixOrCopyCreateLiteral(argb[i]); } + if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]); if (i + 1 < pix_count) { HashChainInsert(hash_chain, &argb[i], i); } @@ -697,7 +675,7 @@ Error: static int BackwardReferencesTraceBackwards(int xsize, int ysize, int recursive_cost_model, const uint32_t* const argb, - int quality, int cache_bits, + int cache_bits, VP8LBackwardRefs* const refs) { int ok = 0; const int dist_array_size = xsize * ysize; @@ -709,18 +687,22 @@ static int BackwardReferencesTraceBackwards(int xsize, int ysize, if (dist_array == NULL) goto Error; if (!BackwardReferencesHashChainDistanceOnly( - xsize, ysize, recursive_cost_model, argb, quality, cache_bits, - dist_array)) { + xsize, ysize, recursive_cost_model, argb, cache_bits, dist_array)) { + goto Error; + } + if (!TraceBackwards(dist_array, dist_array_size, + &chosen_path, &chosen_path_size)) { goto Error; } - TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size); + free(dist_array); // no need to retain this memory any longer + dist_array = NULL; if (!BackwardReferencesHashChainFollowChosenPath( - xsize, ysize, argb, quality, cache_bits, chosen_path, chosen_path_size, - refs)) { + xsize, ysize, argb, cache_bits, chosen_path, chosen_path_size, refs)) { goto Error; } ok = 1; Error: + free(chosen_path); free(dist_array); return ok; } @@ -780,20 +762,18 @@ int VP8LGetBackwardReferences(int width, int height, // Choose appropriate backward reference. if (lz77_is_useful) { - // TraceBackwards is costly. Don't execute it at lower quality. - const int try_lz77_trace_backwards = (quality >= 25); + // TraceBackwards is costly. Run it for higher qualities. + const int try_lz77_trace_backwards = (quality >= 75); *best = refs_lz77; // default guess: lz77 is better VP8LClearBackwardRefs(&refs_rle); if (try_lz77_trace_backwards) { - // Set recursion level for large images using a color cache. - const int recursion_level = - (num_pix < 320 * 200) && (cache_bits > 0) ? 1 : 0; + const int recursion_level = (num_pix < 320 * 200) ? 1 : 0; VP8LBackwardRefs refs_trace; if (!VP8LBackwardRefsAlloc(&refs_trace, num_pix)) { goto End; } - if (BackwardReferencesTraceBackwards(width, height, recursion_level, argb, - quality, cache_bits, &refs_trace)) { + if (BackwardReferencesTraceBackwards( + width, height, recursion_level, argb, cache_bits, &refs_trace)) { VP8LClearBackwardRefs(&refs_lz77); *best = refs_trace; } diff --git a/drivers/webp/enc/backward_references.h b/drivers/webp/enc/backward_references.h index e1c75f04f9..91c03361ed 100644 --- a/drivers/webp/enc/backward_references.h +++ b/drivers/webp/enc/backward_references.h @@ -1,10 +1,8 @@ // Copyright 2012 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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/ // ----------------------------------------------------------------------------- // // Author: Jyrki Alakuijala (jyrki@google.com) @@ -18,7 +16,7 @@ #include "../webp/types.h" #include "../webp/format_constants.h" -#ifdef __cplusplus +#if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif @@ -31,6 +29,68 @@ extern "C" { (NUM_LITERAL_CODES + NUM_LENGTH_CODES + (1 << MAX_COLOR_CACHE_BITS)) // ----------------------------------------------------------------------------- +// PrefixEncode() + +// use GNU builtins where available. +#if defined(__GNUC__) && \ + ((__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || __GNUC__ >= 4) +static WEBP_INLINE int BitsLog2Floor(uint32_t n) { + return n == 0 ? -1 : 31 ^ __builtin_clz(n); +} +#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) +#include <intrin.h> +#pragma intrinsic(_BitScanReverse) + +static WEBP_INLINE int BitsLog2Floor(uint32_t n) { + unsigned long first_set_bit; + return _BitScanReverse(&first_set_bit, n) ? first_set_bit : -1; +} +#else +static WEBP_INLINE int BitsLog2Floor(uint32_t n) { + int log = 0; + uint32_t value = n; + int i; + + if (value == 0) return -1; + for (i = 4; i >= 0; --i) { + const int shift = (1 << i); + const uint32_t x = value >> shift; + if (x != 0) { + value = x; + log += shift; + } + } + return log; +} +#endif + +static WEBP_INLINE int VP8LBitsLog2Ceiling(uint32_t n) { + const int floor = BitsLog2Floor(n); + if (n == (n & ~(n - 1))) // zero or a power of two. + return floor; + else + return floor + 1; +} + +// Splitting of distance and length codes into prefixes and +// extra bits. The prefixes are encoded with an entropy code +// while the extra bits are stored just as normal bits. +static WEBP_INLINE void PrefixEncode(int distance, int* const code, + int* const extra_bits_count, + int* const extra_bits_value) { + // Collect the two most significant bits where the highest bit is 1. + const int highest_bit = BitsLog2Floor(--distance); + // & 0x3f is to make behavior well defined when highest_bit + // does not exist or is the least significant bit. + const int second_highest_bit = + (distance >> ((highest_bit - 1) & 0x3f)) & 1; + *extra_bits_count = (highest_bit > 0) ? (highest_bit - 1) : 0; + *extra_bits_value = distance & ((1 << *extra_bits_count) - 1); + *code = (highest_bit > 0) ? (2 * highest_bit + second_highest_bit) + : (highest_bit == 0) ? 1 : 0; +} + +// ----------------------------------------------------------------------------- // PixOrCopy enum Mode { @@ -145,7 +205,7 @@ int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb, int xsize, int ysize, int* const best_cache_bits); -#ifdef __cplusplus +#if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/drivers/webp/enc/config.c b/drivers/webp/enc/config.c index af7f0b09e8..1a26113554 100644 --- a/drivers/webp/enc/config.c +++ b/drivers/webp/enc/config.c @@ -1,10 +1,8 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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 @@ -13,6 +11,10 @@ #include "../webp/encode.h" +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + //------------------------------------------------------------------------------ // WebPConfig //------------------------------------------------------------------------------ @@ -29,9 +31,9 @@ int WebPConfigInitInternal(WebPConfig* config, config->target_PSNR = 0.; config->method = 4; config->sns_strength = 50; - config->filter_strength = 60; // mid-filtering + config->filter_strength = 20; // default: light filtering config->filter_sharpness = 0; - config->filter_type = 1; // default: strong (so U/V is filtered too) + config->filter_type = 0; // default: simple config->partitions = 0; config->segments = 4; config->pass = 1; @@ -44,9 +46,6 @@ int WebPConfigInitInternal(WebPConfig* config, config->alpha_quality = 100; config->lossless = 0; config->image_hint = WEBP_HINT_DEFAULT; - config->emulate_jpeg_size = 0; - config->thread_level = 0; - config->low_memory = 0; // TODO(skal): tune. switch (preset) { @@ -54,13 +53,11 @@ int WebPConfigInitInternal(WebPConfig* config, config->sns_strength = 80; config->filter_sharpness = 4; config->filter_strength = 35; - config->preprocessing &= ~2; // no dithering break; case WEBP_PRESET_PHOTO: config->sns_strength = 80; config->filter_sharpness = 3; config->filter_strength = 30; - config->preprocessing |= 2; break; case WEBP_PRESET_DRAWING: config->sns_strength = 25; @@ -70,12 +67,10 @@ int WebPConfigInitInternal(WebPConfig* config, case WEBP_PRESET_ICON: config->sns_strength = 0; config->filter_strength = 0; // disable filtering to retain sharpness - config->preprocessing &= ~2; // no dithering break; case WEBP_PRESET_TEXT: config->sns_strength = 0; config->filter_strength = 0; // disable filtering to retain sharpness - config->preprocessing &= ~2; // no dithering config->segments = 2; break; case WEBP_PRESET_DEFAULT: @@ -111,7 +106,7 @@ int WebPValidateConfig(const WebPConfig* config) { return 0; if (config->show_compressed < 0 || config->show_compressed > 1) return 0; - if (config->preprocessing < 0 || config->preprocessing > 3) + if (config->preprocessing < 0 || config->preprocessing > 1) return 0; if (config->partitions < 0 || config->partitions > 3) return 0; @@ -127,14 +122,11 @@ int WebPValidateConfig(const WebPConfig* config) { return 0; if (config->image_hint >= WEBP_HINT_LAST) return 0; - if (config->emulate_jpeg_size < 0 || config->emulate_jpeg_size > 1) - return 0; - if (config->thread_level < 0 || config->thread_level > 1) - return 0; - if (config->low_memory < 0 || config->low_memory > 1) - return 0; return 1; } //------------------------------------------------------------------------------ +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/drivers/webp/enc/cost.c b/drivers/webp/enc/cost.c index 09699f8044..92e0cc713c 100644 --- a/drivers/webp/enc/cost.c +++ b/drivers/webp/enc/cost.c @@ -1,10 +1,8 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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 @@ -13,6 +11,10 @@ #include "./cost.h" +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + //------------------------------------------------------------------------------ // Boolean-cost cost table @@ -73,7 +75,7 @@ const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2] = { // fixed costs for coding levels, deduce from the coding tree. // This is only the part that doesn't depend on the probability state. -const uint16_t VP8LevelFixedCosts[MAX_LEVEL + 1] = { +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, @@ -357,7 +359,7 @@ void VP8CalculateLevelCosts(VP8Proba* const proba) { for (ctype = 0; ctype < NUM_TYPES; ++ctype) { for (band = 0; band < NUM_BANDS; ++band) { - for (ctx = 0; ctx < NUM_CTX; ++ctx) { + 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]); @@ -383,107 +385,110 @@ const uint16_t VP8FixedCostsUV[4] = { 302, 984, 439, 642 }; // note: these values include the fixed VP8BitCost(1, 145) mode selection cost. const uint16_t VP8FixedCostsI16[4] = { 663, 919, 872, 919 }; const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES] = { - { { 40, 1151, 1723, 1874, 2103, 2019, 1628, 1777, 2226, 2137 }, - { 192, 469, 1296, 1308, 1849, 1794, 1781, 1703, 1713, 1522 }, - { 142, 910, 762, 1684, 1849, 1576, 1460, 1305, 1801, 1657 }, - { 559, 641, 1370, 421, 1182, 1569, 1612, 1725, 863, 1007 }, - { 299, 1059, 1256, 1108, 636, 1068, 1581, 1883, 869, 1142 }, - { 277, 1111, 707, 1362, 1089, 672, 1603, 1541, 1545, 1291 }, - { 214, 781, 1609, 1303, 1632, 2229, 726, 1560, 1713, 918 }, - { 152, 1037, 1046, 1759, 1983, 2174, 1358, 742, 1740, 1390 }, - { 512, 1046, 1420, 753, 752, 1297, 1486, 1613, 460, 1207 }, - { 424, 827, 1362, 719, 1462, 1202, 1199, 1476, 1199, 538 } }, - { { 240, 402, 1134, 1491, 1659, 1505, 1517, 1555, 1979, 2099 }, - { 467, 242, 960, 1232, 1714, 1620, 1834, 1570, 1676, 1391 }, - { 500, 455, 463, 1507, 1699, 1282, 1564, 982, 2114, 2114 }, - { 672, 643, 1372, 331, 1589, 1667, 1453, 1938, 996, 876 }, - { 458, 783, 1037, 911, 738, 968, 1165, 1518, 859, 1033 }, - { 504, 815, 504, 1139, 1219, 719, 1506, 1085, 1268, 1268 }, - { 333, 630, 1445, 1239, 1883, 3672, 799, 1548, 1865, 598 }, - { 399, 644, 746, 1342, 1856, 1350, 1493, 613, 1855, 1015 }, - { 622, 749, 1205, 608, 1066, 1408, 1290, 1406, 546, 971 }, - { 500, 753, 1041, 668, 1230, 1617, 1297, 1425, 1383, 523 } }, - { { 394, 553, 523, 1502, 1536, 981, 1608, 1142, 1666, 2181 }, - { 655, 430, 375, 1411, 1861, 1220, 1677, 1135, 1978, 1553 }, - { 690, 640, 245, 1954, 2070, 1194, 1528, 982, 1972, 2232 }, - { 559, 834, 741, 867, 1131, 980, 1225, 852, 1092, 784 }, - { 690, 875, 516, 959, 673, 894, 1056, 1190, 1528, 1126 }, - { 740, 951, 384, 1277, 1177, 492, 1579, 1155, 1846, 1513 }, - { 323, 775, 1062, 1776, 3062, 1274, 813, 1188, 1372, 655 }, - { 488, 971, 484, 1767, 1515, 1775, 1115, 503, 1539, 1461 }, - { 740, 1006, 998, 709, 851, 1230, 1337, 788, 741, 721 }, - { 522, 1073, 573, 1045, 1346, 887, 1046, 1146, 1203, 697 } }, - { { 105, 864, 1442, 1009, 1934, 1840, 1519, 1920, 1673, 1579 }, - { 534, 305, 1193, 683, 1388, 2164, 1802, 1894, 1264, 1170 }, - { 305, 518, 877, 1108, 1426, 3215, 1425, 1064, 1320, 1242 }, - { 683, 732, 1927, 257, 1493, 2048, 1858, 1552, 1055, 947 }, - { 394, 814, 1024, 660, 959, 1556, 1282, 1289, 893, 1047 }, - { 528, 615, 996, 940, 1201, 635, 1094, 2515, 803, 1358 }, - { 347, 614, 1609, 1187, 3133, 1345, 1007, 1339, 1017, 667 }, - { 218, 740, 878, 1605, 3650, 3650, 1345, 758, 1357, 1617 }, - { 672, 750, 1541, 558, 1257, 1599, 1870, 2135, 402, 1087 }, - { 592, 684, 1161, 430, 1092, 1497, 1475, 1489, 1095, 822 } }, - { { 228, 1056, 1059, 1368, 752, 982, 1512, 1518, 987, 1782 }, - { 494, 514, 818, 942, 965, 892, 1610, 1356, 1048, 1363 }, - { 512, 648, 591, 1042, 761, 991, 1196, 1454, 1309, 1463 }, - { 683, 749, 1043, 676, 841, 1396, 1133, 1138, 654, 939 }, - { 622, 1101, 1126, 994, 361, 1077, 1203, 1318, 877, 1219 }, - { 631, 1068, 857, 1650, 651, 477, 1650, 1419, 828, 1170 }, - { 555, 727, 1068, 1335, 3127, 1339, 820, 1331, 1077, 429 }, - { 504, 879, 624, 1398, 889, 889, 1392, 808, 891, 1406 }, - { 683, 1602, 1289, 977, 578, 983, 1280, 1708, 406, 1122 }, - { 399, 865, 1433, 1070, 1072, 764, 968, 1477, 1223, 678 } }, - { { 333, 760, 935, 1638, 1010, 529, 1646, 1410, 1472, 2219 }, - { 512, 494, 750, 1160, 1215, 610, 1870, 1868, 1628, 1169 }, - { 572, 646, 492, 1934, 1208, 603, 1580, 1099, 1398, 1995 }, - { 786, 789, 942, 581, 1018, 951, 1599, 1207, 731, 768 }, - { 690, 1015, 672, 1078, 582, 504, 1693, 1438, 1108, 2897 }, - { 768, 1267, 571, 2005, 1243, 244, 2881, 1380, 1786, 1453 }, - { 452, 899, 1293, 903, 1311, 3100, 465, 1311, 1319, 813 }, - { 394, 927, 942, 1103, 1358, 1104, 946, 593, 1363, 1109 }, - { 559, 1005, 1007, 1016, 658, 1173, 1021, 1164, 623, 1028 }, - { 564, 796, 632, 1005, 1014, 863, 2316, 1268, 938, 764 } }, - { { 266, 606, 1098, 1228, 1497, 1243, 948, 1030, 1734, 1461 }, - { 366, 585, 901, 1060, 1407, 1247, 876, 1134, 1620, 1054 }, - { 452, 565, 542, 1729, 1479, 1479, 1016, 886, 2938, 1150 }, - { 555, 1088, 1533, 950, 1354, 895, 834, 1019, 1021, 496 }, - { 704, 815, 1193, 971, 973, 640, 1217, 2214, 832, 578 }, - { 672, 1245, 579, 871, 875, 774, 872, 1273, 1027, 949 }, - { 296, 1134, 2050, 1784, 1636, 3425, 442, 1550, 2076, 722 }, - { 342, 982, 1259, 1846, 1848, 1848, 622, 568, 1847, 1052 }, - { 555, 1064, 1304, 828, 746, 1343, 1075, 1329, 1078, 494 }, - { 288, 1167, 1285, 1174, 1639, 1639, 833, 2254, 1304, 509 } }, - { { 342, 719, 767, 1866, 1757, 1270, 1246, 550, 1746, 2151 }, - { 483, 653, 694, 1509, 1459, 1410, 1218, 507, 1914, 1266 }, - { 488, 757, 447, 2979, 1813, 1268, 1654, 539, 1849, 2109 }, - { 522, 1097, 1085, 851, 1365, 1111, 851, 901, 961, 605 }, - { 709, 716, 841, 728, 736, 945, 941, 862, 2845, 1057 }, - { 512, 1323, 500, 1336, 1083, 681, 1342, 717, 1604, 1350 }, - { 452, 1155, 1372, 1900, 1501, 3290, 311, 944, 1919, 922 }, - { 403, 1520, 977, 2132, 1733, 3522, 1076, 276, 3335, 1547 }, - { 559, 1374, 1101, 615, 673, 2462, 974, 795, 984, 984 }, - { 547, 1122, 1062, 812, 1410, 951, 1140, 622, 1268, 651 } }, - { { 165, 982, 1235, 938, 1334, 1366, 1659, 1578, 964, 1612 }, - { 592, 422, 925, 847, 1139, 1112, 1387, 2036, 861, 1041 }, - { 403, 837, 732, 770, 941, 1658, 1250, 809, 1407, 1407 }, - { 896, 874, 1071, 381, 1568, 1722, 1437, 2192, 480, 1035 }, - { 640, 1098, 1012, 1032, 684, 1382, 1581, 2106, 416, 865 }, - { 559, 1005, 819, 914, 710, 770, 1418, 920, 838, 1435 }, - { 415, 1258, 1245, 870, 1278, 3067, 770, 1021, 1287, 522 }, - { 406, 990, 601, 1009, 1265, 1265, 1267, 759, 1017, 1277 }, - { 968, 1182, 1329, 788, 1032, 1292, 1705, 1714, 203, 1403 }, - { 732, 877, 1279, 471, 901, 1161, 1545, 1294, 755, 755 } }, - { { 111, 931, 1378, 1185, 1933, 1648, 1148, 1714, 1873, 1307 }, - { 406, 414, 1030, 1023, 1910, 1404, 1313, 1647, 1509, 793 }, - { 342, 640, 575, 1088, 1241, 1349, 1161, 1350, 1756, 1502 }, - { 559, 766, 1185, 357, 1682, 1428, 1329, 1897, 1219, 802 }, - { 473, 909, 1164, 771, 719, 2508, 1427, 1432, 722, 782 }, - { 342, 892, 785, 1145, 1150, 794, 1296, 1550, 973, 1057 }, - { 208, 1036, 1326, 1343, 1606, 3395, 815, 1455, 1618, 712 }, - { 228, 928, 890, 1046, 3499, 1711, 994, 829, 1720, 1318 }, - { 768, 724, 1058, 636, 991, 1075, 1319, 1324, 616, 825 }, - { 305, 1167, 1358, 899, 1587, 1587, 987, 1988, 1332, 501 } } + { { 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/drivers/webp/enc/cost.h b/drivers/webp/enc/cost.h index 3cbad1ae4c..09b75b699d 100644 --- a/drivers/webp/enc/cost.h +++ b/drivers/webp/enc/cost.h @@ -1,10 +1,8 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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. @@ -16,12 +14,11 @@ #include "./vp8enci.h" -#ifdef __cplusplus +#if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif -// approximate cost per level: -extern const uint16_t VP8LevelFixedCosts[MAX_LEVEL + 1]; +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'. @@ -44,7 +41,7 @@ extern const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES]; //------------------------------------------------------------------------------ -#ifdef __cplusplus +#if defined(__cplusplus) || defined(c_plusplus) } // extern "C" #endif diff --git a/drivers/webp/enc/filter.c b/drivers/webp/enc/filter.c index dd27804b55..7fb78a3949 100644 --- a/drivers/webp/enc/filter.c +++ b/drivers/webp/enc/filter.c @@ -1,67 +1,20 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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 <assert.h> #include "./vp8enci.h" -// This table gives, for a given sharpness, the filtering strength to be -// used (at least) in order to filter a given edge step delta. -// This is constructed by brute force inspection: for all delta, we iterate -// over all possible filtering strength / thresh until needs_filter() returns -// true. -#define MAX_DELTA_SIZE 64 -static const uint8_t kLevelsFromDelta[8][MAX_DELTA_SIZE] = { - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 }, - { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 17, 18, - 20, 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, - 44, 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, - { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 19, - 20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, - 44, 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, - { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19, - 21, 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43, - 45, 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, - { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20, - 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, - 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, - { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 17, 19, 20, - 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, - 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, - { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19, 21, - 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43, 45, - 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, - { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20, 21, - 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, - 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 } -}; - -int VP8FilterStrengthFromDelta(int sharpness, int delta) { - const int pos = (delta < MAX_DELTA_SIZE) ? delta : MAX_DELTA_SIZE - 1; - assert(sharpness >= 0 && sharpness <= 7); - return kLevelsFromDelta[sharpness][pos]; -} +#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 @@ -385,29 +338,28 @@ static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) { // loop filter strength void VP8InitFilter(VP8EncIterator* const it) { - if (it->lf_stats_ != NULL) { - int s, i; - InitTables(); - for (s = 0; s < NUM_MB_SEGMENTS; s++) { - for (i = 0; i < MAX_LF_LEVELS; i++) { - (*it->lf_stats_)[s][i] = 0; - } + 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; - VP8Encoder* const enc = it->enc_; const int s = it->mb_->segment_; - const int level0 = enc->dqm_[s].fstrength_; // TODO: ref_lf_delta[] + const int level0 = it->enc_->dqm_[s].fstrength_; // TODO: ref_lf_delta[] // explore +/-quant range of values around level0 - const int delta_min = -enc->dqm_[s].quant_; - const int delta_max = enc->dqm_[s].quant_; + const int 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_ == NULL) return; + if (!it->lf_stats_) return; // NOTE: Currently we are applying filter only across the sublock edges // There are two reasons for that. @@ -431,41 +383,27 @@ void VP8StoreFilterStats(VP8EncIterator* const it) { } void VP8AdjustFilterStrength(VP8EncIterator* const it) { + int s; VP8Encoder* const enc = it->enc_; - if (it->lf_stats_ != NULL) { - int s; - for (s = 0; s < NUM_MB_SEGMENTS; s++) { - int i, best_level = 0; - // Improvement over filter level 0 should be at least 1e-5 (relatively) - double best_v = 1.00001 * (*it->lf_stats_)[s][0]; - for (i = 1; i < MAX_LF_LEVELS; i++) { - const double v = (*it->lf_stats_)[s][i]; - if (v > best_v) { - best_v = v; - best_level = i; - } - } - enc->dqm_[s].fstrength_ = best_level; - } - } else if (enc->config_->filter_strength > 0) { - int max_level = 0; - int s; - for (s = 0; s < NUM_MB_SEGMENTS; s++) { - VP8SegmentInfo* const dqm = &enc->dqm_[s]; - // this '>> 3' accounts for some inverse WHT scaling - const int delta = (dqm->max_edge_ * dqm->y2_.q_[1]) >> 3; - const int level = - VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, delta); - if (level > dqm->fstrength_) { - dqm->fstrength_ = level; - } - if (max_level < dqm->fstrength_) { - max_level = dqm->fstrength_; + + 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->filter_hdr_.level_ = max_level; + enc->dqm_[s].fstrength_ = best_level; } } -// ----------------------------------------------------------------------------- - +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/drivers/webp/enc/frame.c b/drivers/webp/enc/frame.c index 2582244c6c..bdd360069b 100644 --- a/drivers/webp/enc/frame.c +++ b/drivers/webp/enc/frame.c @@ -1,10 +1,8 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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 @@ -18,7 +16,10 @@ #include "./vp8enci.h" #include "./cost.h" -#include "../webp/format_constants.h" // RIFF constants + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif #define SEGMENT_VISU 0 #define DEBUG_SEARCH 0 // useful to track search convergence @@ -37,63 +38,6 @@ typedef struct { } VP8Residual; //------------------------------------------------------------------------------ -// multi-pass convergence - -#define HEADER_SIZE_ESTIMATE (RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + \ - VP8_FRAME_HEADER_SIZE) -#define DQ_LIMIT 0.4 // convergence is considered reached if dq < DQ_LIMIT -// we allow 2k of extra head-room in PARTITION0 limit. -#define PARTITION0_SIZE_LIMIT ((VP8_MAX_PARTITION0_SIZE - 2048ULL) << 11) - -typedef struct { // struct for organizing convergence in either size or PSNR - int is_first; - float dq; - float q, last_q; - double value, last_value; // PSNR or size - double target; - int do_size_search; -} PassStats; - -static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) { - const uint64_t target_size = (uint64_t)enc->config_->target_size; - const int do_size_search = (target_size != 0); - const float target_PSNR = enc->config_->target_PSNR; - - s->is_first = 1; - s->dq = 10.f; - s->q = s->last_q = enc->config_->quality; - s->target = do_size_search ? (double)target_size - : (target_PSNR > 0.) ? target_PSNR - : 40.; // default, just in case - s->value = s->last_value = 0.; - s->do_size_search = do_size_search; - return do_size_search; -} - -static float Clamp(float v, float min, float max) { - return (v < min) ? min : (v > max) ? max : v; -} - -static float ComputeNextQ(PassStats* const s) { - float dq; - if (s->is_first) { - dq = (s->value > s->target) ? -s->dq : s->dq; - s->is_first = 0; - } else if (s->value != s->last_value) { - const double slope = (s->target - s->value) / (s->last_value - s->value); - dq = (float)(slope * (s->last_q - s->q)); - } else { - dq = 0.; // we're done?! - } - // Limit variable to avoid large swings. - s->dq = Clamp(dq, -30.f, 30.f); - s->last_q = s->q; - s->last_value = s->value; - s->q = Clamp(s->q + s->dq, 0.f, 100.f); - return s->q; -} - -//------------------------------------------------------------------------------ // Tables for level coding const uint8_t VP8EncBands[16 + 1] = { @@ -101,10 +45,10 @@ const uint8_t VP8EncBands[16 + 1] = { 0 // sentinel }; -const uint8_t VP8Cat3[] = { 173, 148, 140 }; -const uint8_t VP8Cat4[] = { 176, 155, 140, 135 }; -const uint8_t VP8Cat5[] = { 180, 157, 141, 134, 130 }; -const uint8_t VP8Cat6[] = +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 }; //------------------------------------------------------------------------------ @@ -169,15 +113,14 @@ static int Record(int bit, proba_t* const stats) { // Note: no need to record the fixed probas. static int RecordCoeffs(int ctx, const VP8Residual* const res) { int n = res->first; - // should be stats[VP8EncBands[n]], but it's equivalent for n=0 or 1 - proba_t* s = res->stats[n][ctx]; + proba_t* s = res->stats[VP8EncBands[n]][ctx]; if (res->last < 0) { Record(0, s + 0); return 0; } while (n <= res->last) { int v; - Record(1, s + 0); // order of record doesn't matter + Record(1, s + 0); while ((v = res->coeffs[n++]) == 0) { Record(0, s + 1); s = res->stats[VP8EncBands[n]][0]; @@ -231,7 +174,8 @@ static int BranchCost(int nb, int total, int proba) { return nb * VP8BitCost(1, proba) + (total - nb) * VP8BitCost(0, proba); } -static int FinalizeTokenProbas(VP8Proba* const proba) { +static int FinalizeTokenProbas(VP8Encoder* const enc) { + VP8Proba* const proba = &enc->proba_; int has_changed = 0; int size = 0; int t, b, c, p; @@ -268,47 +212,6 @@ static int FinalizeTokenProbas(VP8Proba* const proba) { } //------------------------------------------------------------------------------ -// Finalize Segment probability based on the coding tree - -static int GetProba(int a, int b) { - const int total = a + b; - return (total == 0) ? 255 // that's the default probability. - : (255 * a + total / 2) / total; // rounded proba -} - -static void SetSegmentProbas(VP8Encoder* const enc) { - int p[NUM_MB_SEGMENTS] = { 0 }; - int n; - - for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { - const VP8MBInfo* const mb = &enc->mb_info_[n]; - p[mb->segment_]++; - } - if (enc->pic_->stats != NULL) { - for (n = 0; n < NUM_MB_SEGMENTS; ++n) { - enc->pic_->stats->segment_size[n] = p[n]; - } - } - if (enc->segment_hdr_.num_segments_ > 1) { - uint8_t* const probas = enc->proba_.segments_; - probas[0] = GetProba(p[0] + p[1], p[2] + p[3]); - probas[1] = GetProba(p[0], p[1]); - probas[2] = GetProba(p[2], p[3]); - - enc->segment_hdr_.update_map_ = - (probas[0] != 255) || (probas[1] != 255) || (probas[2] != 255); - enc->segment_hdr_.size_ = - p[0] * (VP8BitCost(0, probas[0]) + VP8BitCost(0, probas[1])) + - p[1] * (VP8BitCost(0, probas[0]) + VP8BitCost(1, probas[1])) + - p[2] * (VP8BitCost(1, probas[0]) + VP8BitCost(0, probas[2])) + - p[3] * (VP8BitCost(1, probas[0]) + VP8BitCost(1, probas[2])); - } else { - enc->segment_hdr_.update_map_ = 0; - enc->segment_hdr_.size_ = 0; - } -} - -//------------------------------------------------------------------------------ // helper functions for residuals struct VP8Residual. static void InitResidual(int first, int coeff_type, @@ -336,38 +239,39 @@ static void SetResidualCoeffs(const int16_t* const coeffs, //------------------------------------------------------------------------------ // Mode costs -static int GetResidualCost(int ctx0, const VP8Residual* const res) { +static int GetResidualCost(int ctx, const VP8Residual* const res) { int n = res->first; - // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1 - int p0 = res->prob[n][ctx0][0]; - const uint16_t* t = res->cost[n][ctx0]; + int p0 = res->prob[VP8EncBands[n]][ctx][0]; + const uint16_t* t = res->cost[VP8EncBands[n]][ctx]; int cost; if (res->last < 0) { return VP8BitCost(0, p0); } - cost = VP8BitCost(1, p0); - for (; n < res->last; ++n) { - const int v = abs(res->coeffs[n]); + cost = 0; + while (n <= res->last) { + const int v = res->coeffs[n]; const int b = VP8EncBands[n + 1]; - const int ctx = (v >= 2) ? 2 : v; - cost += VP8LevelCost(t, v); - t = res->cost[b][ctx]; - // the masking trick is faster than "if (v) cost += ..." with clang - cost += (v ? ~0U : 0) & VP8BitCost(1, res->prob[b][ctx][0]); - } - // Last coefficient is always non-zero - { - const int v = abs(res->coeffs[n]); - assert(v != 0); - cost += VP8LevelCost(t, v); - if (n < 15) { - const int b = VP8EncBands[n + 1]; - const int ctx = (v == 1) ? 1 : 2; - const int last_p0 = res->prob[b][ctx][0]; - cost += VP8BitCost(0, last_p0); + ++n; + if (v == 0) { + // short-case for VP8LevelCost(t, 0) (note: VP8LevelFixedCosts[0] == 0): + cost += t[0]; + t = res->cost[b][0]; + continue; + } + cost += VP8BitCost(1, p0); + if (2u >= (unsigned int)(v + 1)) { // v = -1 or 1 + // short-case for "VP8LevelCost(t, 1)" (256 is VP8LevelFixedCosts[1]): + cost += 256 + t[1]; + p0 = res->prob[b][1][0]; + t = res->cost[b][1]; + } else { + cost += VP8LevelCost(t, abs(v)); + p0 = res->prob[b][2][0]; + t = res->cost[b][2]; } } + if (n < 16) cost += VP8BitCost(0, p0); return cost; } @@ -438,8 +342,7 @@ int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd) { static int PutCoeffs(VP8BitWriter* const bw, int ctx, const VP8Residual* res) { int n = res->first; - // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1 - const uint8_t* p = res->prob[n][ctx]; + const uint8_t* p = res->prob[VP8EncBands[n]][ctx]; if (!VP8PutBit(bw, res->last >= 0, p[0])) { return 0; } @@ -468,30 +371,30 @@ static int PutCoeffs(VP8BitWriter* const bw, int ctx, const VP8Residual* res) { } else { int mask; const uint8_t* tab; - if (v < 3 + (8 << 1)) { // VP8Cat3 (3b) + 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 = VP8Cat3; - } else if (v < 3 + (8 << 2)) { // VP8Cat4 (4b) + 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 = VP8Cat4; - } else if (v < 3 + (8 << 3)) { // VP8Cat5 (5b) + 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 = VP8Cat5; - } else { // VP8Cat6 (11b) + tab = kCat5; + } else { // kCat6 (11b) VP8PutBit(bw, 1, p[8]); VP8PutBit(bw, 1, p[10]); v -= 3 + (8 << 3); mask = 1 << 10; - tab = VP8Cat6; + tab = kCat6; } while (mask) { VP8PutBit(bw, !!(v & mask), *tab++); @@ -508,7 +411,8 @@ static int PutCoeffs(VP8BitWriter* const bw, int ctx, const VP8Residual* res) { return 1; } -static void CodeResiduals(VP8BitWriter* const bw, VP8EncIterator* const it, +static void CodeResiduals(VP8BitWriter* const bw, + VP8EncIterator* const it, const VP8ModeScore* const rd) { int x, y, ch; VP8Residual res; @@ -608,23 +512,146 @@ static void RecordResiduals(VP8EncIterator* const it, //------------------------------------------------------------------------------ // Token buffer -#if !defined(DISABLE_TOKEN_BUFFER) +#ifdef USE_TOKEN_BUFFER + +void VP8TBufferInit(VP8TBuffer* const b) { + b->rows_ = NULL; + b->tokens_ = NULL; + b->last_ = &b->rows_; + b->left_ = 0; + b->error_ = 0; +} + +int VP8TBufferNewPage(VP8TBuffer* const b) { + VP8Tokens* const page = b->error_ ? NULL : (VP8Tokens*)malloc(sizeof(*page)); + if (page == NULL) { + b->error_ = 1; + return 0; + } + *b->last_ = page; + b->last_ = &page->next_; + b->left_ = MAX_NUM_TOKEN; + b->tokens_ = page->tokens_; + return 1; +} + +void VP8TBufferClear(VP8TBuffer* const b) { + if (b != NULL) { + const VP8Tokens* p = b->rows_; + while (p != NULL) { + const VP8Tokens* const next = p->next_; + free((void*)p); + p = next; + } + VP8TBufferInit(b); + } +} + +int VP8EmitTokens(const VP8TBuffer* const b, VP8BitWriter* const bw, + const uint8_t* const probas) { + VP8Tokens* p = b->rows_; + if (b->error_) return 0; + while (p != NULL) { + const int N = (p->next_ == NULL) ? b->left_ : 0; + int n = MAX_NUM_TOKEN; + while (n-- > N) { + VP8PutBit(bw, (p->tokens_[n] >> 15) & 1, probas[p->tokens_[n] & 0x7fff]); + } + p = p->next_; + } + return 1; +} + +#define TOKEN_ID(b, ctx, p) ((p) + NUM_PROBAS * ((ctx) + (b) * NUM_CTX)) + +static int RecordCoeffTokens(int ctx, const VP8Residual* const res, + VP8TBuffer* tokens) { + int n = res->first; + int b = VP8EncBands[n]; + if (!VP8AddToken(tokens, res->last >= 0, TOKEN_ID(b, ctx, 0))) { + return 0; + } + + while (n < 16) { + const int c = res->coeffs[n++]; + const int sign = c < 0; + int v = sign ? -c : c; + const int base_id = TOKEN_ID(b, ctx, 0); + if (!VP8AddToken(tokens, v != 0, base_id + 1)) { + b = VP8EncBands[n]; + ctx = 0; + continue; + } + if (!VP8AddToken(tokens, v > 1, base_id + 2)) { + b = VP8EncBands[n]; + ctx = 1; + } else { + if (!VP8AddToken(tokens, v > 4, base_id + 3)) { + if (VP8AddToken(tokens, v != 2, base_id + 4)) + VP8AddToken(tokens, v == 4, base_id + 5); + } else if (!VP8AddToken(tokens, v > 10, base_id + 6)) { + if (!VP8AddToken(tokens, v > 6, base_id + 7)) { +// VP8AddToken(tokens, v == 6, 159); + } else { +// VP8AddToken(tokens, v >= 9, 165); +// VP8AddToken(tokens, !(v & 1), 145); + } + } else { + int mask; + const uint8_t* tab; + if (v < 3 + (8 << 1)) { // kCat3 (3b) + VP8AddToken(tokens, 0, base_id + 8); + VP8AddToken(tokens, 0, base_id + 9); + v -= 3 + (8 << 0); + mask = 1 << 2; + tab = kCat3; + } else if (v < 3 + (8 << 2)) { // kCat4 (4b) + VP8AddToken(tokens, 0, base_id + 8); + VP8AddToken(tokens, 1, base_id + 9); + v -= 3 + (8 << 1); + mask = 1 << 3; + tab = kCat4; + } else if (v < 3 + (8 << 3)) { // kCat5 (5b) + VP8AddToken(tokens, 1, base_id + 8); + VP8AddToken(tokens, 0, base_id + 10); + v -= 3 + (8 << 2); + mask = 1 << 4; + tab = kCat5; + } else { // kCat6 (11b) + VP8AddToken(tokens, 1, base_id + 8); + VP8AddToken(tokens, 1, base_id + 10); + v -= 3 + (8 << 3); + mask = 1 << 10; + tab = kCat6; + } + while (mask) { + // VP8AddToken(tokens, !!(v & mask), *tab++); + mask >>= 1; + } + } + ctx = 2; + } + b = VP8EncBands[n]; + // VP8PutBitUniform(bw, sign); + if (n == 16 || !VP8AddToken(tokens, n <= res->last, TOKEN_ID(b, ctx, 0))) { + return 1; // EOB + } + } + return 1; +} -static void RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd, - VP8TBuffer* const tokens) { +static void RecordTokens(VP8EncIterator* const it, + const VP8ModeScore* const rd, VP8TBuffer tokens[2]) { int x, y, ch; VP8Residual res; VP8Encoder* const enc = it->enc_; VP8IteratorNzToBytes(it); if (it->mb_->type_ == 1) { // i16x16 - const int ctx = it->top_nz_[8] + it->left_nz_[8]; InitResidual(0, 1, enc, &res); SetResidualCoeffs(rd->y_dc_levels, &res); - it->top_nz_[8] = it->left_nz_[8] = - VP8RecordCoeffTokens(ctx, 1, - res.first, res.last, res.coeffs, tokens); - RecordCoeffs(ctx, &res); +// TODO(skal): FIX -> it->top_nz_[8] = it->left_nz_[8] = + RecordCoeffTokens(it->top_nz_[8] + it->left_nz_[8], &res, &tokens[0]); InitResidual(1, 0, enc, &res); } else { InitResidual(0, 3, enc, &res); @@ -636,9 +663,7 @@ static void RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd, const int ctx = it->top_nz_[x] + it->left_nz_[y]; SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res); it->top_nz_[x] = it->left_nz_[y] = - VP8RecordCoeffTokens(ctx, res.coeff_type, - res.first, res.last, res.coeffs, tokens); - RecordCoeffs(ctx, &res); + RecordCoeffTokens(ctx, &res, &tokens[0]); } } @@ -650,16 +675,13 @@ static void RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd, const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y]; SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res); it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = - VP8RecordCoeffTokens(ctx, 2, - res.first, res.last, res.coeffs, tokens); - RecordCoeffs(ctx, &res); + RecordCoeffTokens(ctx, &res, &tokens[1]); } } } - VP8IteratorBytesToNz(it); } -#endif // !DISABLE_TOKEN_BUFFER +#endif // USE_TOKEN_BUFFER //------------------------------------------------------------------------------ // ExtraInfo map / Debug function @@ -675,10 +697,7 @@ static void SetBlock(uint8_t* p, int value, int size) { #endif static void ResetSSE(VP8Encoder* const enc) { - enc->sse_[0] = 0; - enc->sse_[1] = 0; - enc->sse_[2] = 0; - // Note: enc->sse_[3] is managed by alpha.c + memset(enc->sse_, 0, sizeof(enc->sse_)); enc->sse_count_ = 0; } @@ -717,7 +736,6 @@ static void StoreSideInfo(const VP8EncIterator* const it) { const int b = (int)((it->luma_bits_ + it->uv_bits_ + 7) >> 3); *info = (b > 255) ? 255 : b; break; } - case 7: *info = mb->alpha_; break; default: *info = 0; break; }; } @@ -728,149 +746,62 @@ static void StoreSideInfo(const VP8EncIterator* const it) { #endif } -static double GetPSNR(uint64_t mse, uint64_t size) { - return (mse > 0 && size > 0) ? 10. * log10(255. * 255. * size / mse) : 99; -} - //------------------------------------------------------------------------------ -// StatLoop(): only collect statistics (number of skips, token usage, ...). -// This is used for deciding optimal probabilities. It also modifies the -// quantizer value if some target (size, PSNR) was specified. +// Main loops +// +// VP8EncLoop(): does the final bitstream coding. -static void SetLoopParams(VP8Encoder* const enc, float q) { - // Make sure the quality parameter is inside valid bounds - q = Clamp(q, 0.f, 100.f); +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 + } +} - VP8SetSegmentParams(enc, q); // setup segment quantizations and filters - SetSegmentProbas(enc); // compute segment probabilities +int VP8EncLoop(VP8Encoder* const enc) { + int i, s, p; + int ok = 1; + 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); ResetSSE(enc); -} - -static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt, - int nb_mbs, int percent_delta, - PassStats* const s) { - VP8EncIterator it; - uint64_t size = 0; - uint64_t size_p0 = 0; - uint64_t distortion = 0; - const uint64_t pixel_count = nb_mbs * 384; VP8IteratorInit(enc, &it); - SetLoopParams(enc, s->q); + VP8InitFilter(&it); do { - VP8ModeScore info; - VP8IteratorImport(&it, NULL); - if (VP8Decimate(&it, &info, rd_opt)) { - // Just record the number of skips and act like skip_proba is not used. - enc->proba_.nb_skip_++; + 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); } - RecordResiduals(&it, &info); - size += info.R + info.H; - size_p0 += info.H; - distortion += info.D; - if (percent_delta && !VP8IteratorProgress(&it, percent_delta)) - return 0; - VP8IteratorSaveBoundary(&it); - } while (VP8IteratorNext(&it) && --nb_mbs > 0); - - size_p0 += enc->segment_hdr_.size_; - if (s->do_size_search) { - size += FinalizeSkipProba(enc); - size += FinalizeTokenProbas(&enc->proba_); - size = ((size + size_p0 + 1024) >> 11) + HEADER_SIZE_ESTIMATE; - s->value = (double)size; - } else { - s->value = GetPSNR(distortion, pixel_count); - } - return size_p0; -} - -static int StatLoop(VP8Encoder* const enc) { - const int method = enc->method_; - const int do_search = enc->do_search_; - const int fast_probe = ((method == 0 || method == 3) && !do_search); - int num_pass_left = enc->config_->pass; - const int task_percent = 20; - const int percent_per_pass = - (task_percent + num_pass_left / 2) / num_pass_left; - const int final_percent = enc->percent_ + task_percent; - const VP8RDLevel rd_opt = - (method >= 3 || do_search) ? RD_OPT_BASIC : RD_OPT_NONE; - int nb_mbs = enc->mb_w_ * enc->mb_h_; - PassStats stats; - - InitPassStats(enc, &stats); - ResetTokenStats(enc); - - // Fast mode: quick analysis pass over few mbs. Better than nothing. - if (fast_probe) { - if (method == 3) { // we need more stats for method 3 to be reliable. - nb_mbs = (nb_mbs > 200) ? nb_mbs >> 1 : 100; - } else { - nb_mbs = (nb_mbs > 200) ? nb_mbs >> 2 : 50; +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (enc->use_layer_) { + VP8EncCodeLayerBlock(&it); } - } - - while (num_pass_left-- > 0) { - const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) || - (num_pass_left == 0) || - (enc->max_i4_header_bits_ == 0); - const uint64_t size_p0 = - OneStatPass(enc, rd_opt, nb_mbs, percent_per_pass, &stats); - if (size_p0 == 0) return 0; -#if (DEBUG_SEARCH > 0) - printf("#%d value:%.1lf -> %.1lf q:%.2f -> %.2f\n", - num_pass_left, stats.last_value, stats.value, stats.last_q, stats.q); #endif - if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) { - ++num_pass_left; - enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation... - continue; // ...and start over - } - if (is_last_pass) { - break; - } - // If no target size: just do several pass without changing 'q' - if (do_search) { - ComputeNextQ(&stats); - if (fabs(stats.dq) <= DQ_LIMIT) break; - } - } - if (!do_search || !stats.do_size_search) { - // Need to finalize probas now, since it wasn't done during the search. - FinalizeSkipProba(enc); - FinalizeTokenProbas(&enc->proba_); - } - VP8CalculateLevelCosts(&enc->proba_); // finalize costs - return WebPReportProgress(enc->pic_, final_percent, &enc->percent_); -} - -//------------------------------------------------------------------------------ -// Main loops -// - -static const int kAverageBytesPerMB[8] = { 50, 24, 16, 9, 7, 5, 3, 2 }; - -static int PreLoopInitialize(VP8Encoder* const enc) { - int p; - int ok = 1; - const int average_bytes_per_MB = kAverageBytesPerMB[enc->base_quant_ >> 4]; - const int bytes_per_parts = - enc->mb_w_ * enc->mb_h_ * average_bytes_per_MB / enc->num_parts_; - // Initialize the bit-writers - for (p = 0; ok && p < enc->num_parts_; ++p) { - ok = VP8BitWriterInit(enc->parts_ + p, bytes_per_parts); - } - if (!ok) VP8EncFreeBitWriters(enc); // malloc error occurred - return ok; -} + StoreSideInfo(&it); + VP8StoreFilterStats(&it); + VP8IteratorExport(&it); + ok = VP8IteratorProgress(&it, 20); + } while (ok && VP8IteratorNext(&it, it.yuv_out_)); -static int PostLoopFinalize(VP8EncIterator* const it, int ok) { - VP8Encoder* const enc = it->enc_; if (ok) { // Finalize the partitions, check for extra errors. - int p; for (p = 0; p < enc->num_parts_; ++p) { VP8BitWriterFinish(enc->parts_ + p); ok &= !enc->parts_[p].error_; @@ -878,191 +809,131 @@ static int PostLoopFinalize(VP8EncIterator* const it, int ok) { } if (ok) { // All good. Finish up. - if (enc->pic_->stats != NULL) { // finalize byte counters... - int i, s; + if (enc->pic_->stats) { // finalize byte counters... 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); + enc->residual_bytes_[i][s] = (int)((it.bit_count_[s][i] + 7) >> 3); } } } - VP8AdjustFilterStrength(it); // ...and store filter stats. + VP8AdjustFilterStrength(&it); // ...and store filter stats. } else { // Something bad happened -> need to do some memory cleanup. VP8EncFreeBitWriters(enc); } + return ok; } //------------------------------------------------------------------------------ -// VP8EncLoop(): does the final bitstream coding. +// 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. -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 - } -} +#define kHeaderSizeEstimate (15 + 20 + 10) // TODO: fix better -int VP8EncLoop(VP8Encoder* const enc) { +static int OneStatPass(VP8Encoder* const enc, float q, int rd_opt, int nb_mbs, + float* const PSNR, int percent_delta) { VP8EncIterator it; - int ok = PreLoopInitialize(enc); - if (!ok) return 0; + uint64_t size = 0; + uint64_t distortion = 0; + const uint64_t pixel_count = nb_mbs * 384; - StatLoop(enc); // stats-collection loop + // 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); + ResetTokenStats(enc); VP8IteratorInit(enc, &it); - VP8InitFilter(&it); do { VP8ModeScore info; - const int dont_use_skip = !enc->proba_.use_skip_proba_; - const VP8RDLevel rd_opt = enc->rd_opt_level_; - - VP8IteratorImport(&it, NULL); - // Warning! order is important: first call VP8Decimate() and - // *then* decide how to code the skip decision if there's one. - if (!VP8Decimate(&it, &info, rd_opt) || dont_use_skip) { - CodeResiduals(it.bw_, &it, &info); - } else { // reset predictors after a skip - ResetAfterSkip(&it); - } -#ifdef WEBP_EXPERIMENTAL_FEATURES - if (enc->use_layer_) { - VP8EncCodeLayerBlock(&it); + 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_++; } -#endif - StoreSideInfo(&it); - VP8StoreFilterStats(&it); - VP8IteratorExport(&it); - ok = VP8IteratorProgress(&it, 20); - VP8IteratorSaveBoundary(&it); - } while (ok && VP8IteratorNext(&it)); + RecordResiduals(&it, &info); + size += info.R; + distortion += info.D; + if (percent_delta && !VP8IteratorProgress(&it, percent_delta)) + return 0; + } 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; - return PostLoopFinalize(&it, ok); + if (PSNR) { + *PSNR = (float)(10.* log10(255. * 255. * pixel_count / distortion)); + } + return (int)size; } -//------------------------------------------------------------------------------ -// Single pass using Token Buffer. - -#if !defined(DISABLE_TOKEN_BUFFER) +// successive refinement increments. +static const int dqs[] = { 20, 15, 10, 8, 6, 4, 2, 1, 0 }; -#define MIN_COUNT 96 // minimum number of macroblocks before updating stats +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; + const int max_passes = enc->config_->pass; + const int task_percent = 20; + const int percent_per_pass = (task_percent + max_passes / 2) / max_passes; + const int final_percent = enc->percent_ + task_percent; + int pass; + int nb_mbs; -int VP8EncTokenLoop(VP8Encoder* const enc) { - // Roughly refresh the proba eight times per pass - int max_count = (enc->mb_w_ * enc->mb_h_) >> 3; - int num_pass_left = enc->config_->pass; - const int do_search = enc->do_search_; - VP8EncIterator it; - VP8Proba* const proba = &enc->proba_; - const VP8RDLevel rd_opt = enc->rd_opt_level_; - const uint64_t pixel_count = enc->mb_w_ * enc->mb_h_ * 384; - PassStats stats; - int ok; - - InitPassStats(enc, &stats); - ok = PreLoopInitialize(enc); - if (!ok) return 0; - - if (max_count < MIN_COUNT) max_count = MIN_COUNT; - - assert(enc->num_parts_ == 1); - assert(enc->use_tokens_); - assert(proba->use_skip_proba_ == 0); - assert(rd_opt >= RD_OPT_BASIC); // otherwise, token-buffer won't be useful - assert(num_pass_left > 0); - - while (ok && num_pass_left-- > 0) { - const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) || - (num_pass_left == 0) || - (enc->max_i4_header_bits_ == 0); - uint64_t size_p0 = 0; - uint64_t distortion = 0; - int cnt = max_count; - VP8IteratorInit(enc, &it); - SetLoopParams(enc, stats.q); - if (is_last_pass) { - ResetTokenStats(enc); - VP8InitFilter(&it); // don't collect stats until last pass (too costly) - } - VP8TBufferClear(&enc->tokens_); - do { - VP8ModeScore info; - VP8IteratorImport(&it, NULL); - if (--cnt < 0) { - FinalizeTokenProbas(proba); - VP8CalculateLevelCosts(proba); // refresh cost tables for rd-opt - cnt = max_count; - } - VP8Decimate(&it, &info, rd_opt); - RecordTokens(&it, &info, &enc->tokens_); - size_p0 += info.H; - distortion += info.D; -#ifdef WEBP_EXPERIMENTAL_FEATURES - if (enc->use_layer_) { - VP8EncCodeLayerBlock(&it); - } -#endif - if (is_last_pass) { - StoreSideInfo(&it); - VP8StoreFilterStats(&it); - VP8IteratorExport(&it); - ok = VP8IteratorProgress(&it, 20); + // 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 < max_passes; ++pass) { + const int rd_opt = (enc->method_ > 2); + if (!OneStatPass(enc, q, rd_opt, nb_mbs, NULL, percent_per_pass)) { + return 0; } - VP8IteratorSaveBoundary(&it); - } while (ok && VP8IteratorNext(&it)); - if (!ok) break; - - size_p0 += enc->segment_hdr_.size_; - if (stats.do_size_search) { - uint64_t size = FinalizeTokenProbas(&enc->proba_); - size += VP8EstimateTokenSize(&enc->tokens_, - (const uint8_t*)proba->coeffs_); - size = (size + size_p0 + 1024) >> 11; // -> size in bytes - size += HEADER_SIZE_ESTIMATE; - stats.value = (double)size; - } else { // compute and store PSNR - stats.value = GetPSNR(distortion, pixel_count); } - -#if (DEBUG_SEARCH > 0) - printf("#%2d metric:%.1lf -> %.1lf last_q=%.2lf q=%.2lf dq=%.2lf\n", - num_pass_left, stats.last_value, stats.value, - stats.last_q, stats.q, stats.dq); + } else { + // binary search for a size close to target + for (pass = 0; pass < max_passes && (dqs[pass] > 0); ++pass) { + const int rd_opt = 1; + float PSNR; + int criterion; + const int size = OneStatPass(enc, q, rd_opt, nb_mbs, &PSNR, + percent_per_pass); +#if DEBUG_SEARCH + printf("#%d size=%d PSNR=%.2f q=%.2f\n", pass, size, PSNR, q); #endif - if (size_p0 > PARTITION0_SIZE_LIMIT) { - ++num_pass_left; - enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation... - continue; // ...and start over - } - if (is_last_pass) { - break; // done - } - if (do_search) { - ComputeNextQ(&stats); // Adjust q - } - } - if (ok) { - if (!stats.do_size_search) { - FinalizeTokenProbas(&enc->proba_); + if (!size) return 0; + 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]; + } } - ok = VP8EmitTokens(&enc->tokens_, enc->parts_ + 0, - (const uint8_t*)proba->coeffs_, 1); } - ok = ok && WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_); - return PostLoopFinalize(&it, ok); -} - -#else - -int VP8EncTokenLoop(VP8Encoder* const enc) { - (void)enc; - return 0; // we shouldn't be here. + return WebPReportProgress(enc->pic_, final_percent, &enc->percent_); } -#endif // DISABLE_TOKEN_BUFFER - //------------------------------------------------------------------------------ +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/drivers/webp/enc/histogram.c b/drivers/webp/enc/histogram.c index abd253bd7c..ca838e064d 100644 --- a/drivers/webp/enc/histogram.c +++ b/drivers/webp/enc/histogram.c @@ -1,10 +1,8 @@ // Copyright 2012 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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/ // ----------------------------------------------------------------------------- // // Author: Jyrki Alakuijala (jyrki@google.com) @@ -57,9 +55,9 @@ VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) { int i; VP8LHistogramSet* set; VP8LHistogram* bulk; - const uint64_t total_size = sizeof(*set) - + (uint64_t)size * sizeof(*set->histograms) - + (uint64_t)size * sizeof(**set->histograms); + const uint64_t total_size = (uint64_t)sizeof(*set) + + size * sizeof(*set->histograms) + + size * sizeof(**set->histograms); uint8_t* memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory)); if (memory == NULL) return NULL; @@ -90,14 +88,18 @@ void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo, int literal_ix = 256 + NUM_LENGTH_CODES + PixOrCopyCacheIdx(v); ++histo->literal_[literal_ix]; } else { - int code, extra_bits; - VP8LPrefixEncodeBits(PixOrCopyLength(v), &code, &extra_bits); + int code, extra_bits_count, extra_bits_value; + PrefixEncode(PixOrCopyLength(v), + &code, &extra_bits_count, &extra_bits_value); ++histo->literal_[256 + code]; - VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code, &extra_bits); + PrefixEncode(PixOrCopyDistance(v), + &code, &extra_bits_count, &extra_bits_value); ++histo->distance_[code]; } } + + static double BitsEntropy(const int* const array, int n) { double retval = 0.; int sum = 0; @@ -147,6 +149,25 @@ static double BitsEntropy(const int* const array, int n) { } } +double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) { + double retval = BitsEntropy(&p->literal_[0], VP8LHistogramNumCodes(p)) + + BitsEntropy(&p->red_[0], 256) + + BitsEntropy(&p->blue_[0], 256) + + BitsEntropy(&p->alpha_[0], 256) + + BitsEntropy(&p->distance_[0], NUM_DISTANCE_CODES); + // Compute the extra bits cost. + int i; + for (i = 2; i < NUM_LENGTH_CODES - 2; ++i) { + retval += + (i >> 1) * p->literal_[256 + i + 2]; + } + for (i = 2; i < NUM_DISTANCE_CODES - 2; ++i) { + retval += (i >> 1) * p->distance_[i + 2]; + } + return retval; +} + + // Returns the cost encode the rle-encoded entropy code. // The constants in this function are experimental. static double HuffmanCost(const int* const population, int length) { @@ -186,150 +207,19 @@ static double HuffmanCost(const int* const population, int length) { return retval; } -static double PopulationCost(const int* const population, int length) { - return BitsEntropy(population, length) + HuffmanCost(population, length); -} - -static double ExtraCost(const int* const population, int length) { - int i; - double cost = 0.; - for (i = 2; i < length - 2; ++i) cost += (i >> 1) * population[i + 2]; - return cost; +// Estimates the Huffman dictionary + other block overhead size. +static double HistogramEstimateBitsHeader(const VP8LHistogram* const p) { + return HuffmanCost(&p->alpha_[0], 256) + + HuffmanCost(&p->red_[0], 256) + + HuffmanCost(&p->literal_[0], VP8LHistogramNumCodes(p)) + + HuffmanCost(&p->blue_[0], 256) + + HuffmanCost(&p->distance_[0], NUM_DISTANCE_CODES); } -// Estimates the Entropy + Huffman + other block overhead size cost. double VP8LHistogramEstimateBits(const VP8LHistogram* const p) { - return PopulationCost(p->literal_, VP8LHistogramNumCodes(p)) - + PopulationCost(p->red_, 256) - + PopulationCost(p->blue_, 256) - + PopulationCost(p->alpha_, 256) - + PopulationCost(p->distance_, NUM_DISTANCE_CODES) - + ExtraCost(p->literal_ + 256, NUM_LENGTH_CODES) - + ExtraCost(p->distance_, NUM_DISTANCE_CODES); -} - -double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) { - return BitsEntropy(p->literal_, VP8LHistogramNumCodes(p)) - + BitsEntropy(p->red_, 256) - + BitsEntropy(p->blue_, 256) - + BitsEntropy(p->alpha_, 256) - + BitsEntropy(p->distance_, NUM_DISTANCE_CODES) - + ExtraCost(p->literal_ + 256, NUM_LENGTH_CODES) - + ExtraCost(p->distance_, NUM_DISTANCE_CODES); -} - -// ----------------------------------------------------------------------------- -// Various histogram combine/cost-eval functions - -// Adds 'in' histogram to 'out' -static void HistogramAdd(const VP8LHistogram* const in, - VP8LHistogram* const out) { - int i; - for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) { - out->literal_[i] += in->literal_[i]; - } - for (i = 0; i < NUM_DISTANCE_CODES; ++i) { - out->distance_[i] += in->distance_[i]; - } - for (i = 0; i < 256; ++i) { - out->red_[i] += in->red_[i]; - out->blue_[i] += in->blue_[i]; - out->alpha_[i] += in->alpha_[i]; - } -} - -// Performs out = a + b, computing the cost C(a+b) - C(a) - C(b) while comparing -// to the threshold value 'cost_threshold'. The score returned is -// Score = C(a+b) - C(a) - C(b), where C(a) + C(b) is known and fixed. -// Since the previous score passed is 'cost_threshold', we only need to compare -// the partial cost against 'cost_threshold + C(a) + C(b)' to possibly bail-out -// early. -static double HistogramAddEval(const VP8LHistogram* const a, - const VP8LHistogram* const b, - VP8LHistogram* const out, - double cost_threshold) { - double cost = 0; - const double sum_cost = a->bit_cost_ + b->bit_cost_; - int i; - - cost_threshold += sum_cost; - - // palette_code_bits_ is part of the cost evaluation for literal_. - // TODO(skal): remove/simplify this palette_code_bits_? - out->palette_code_bits_ = - (a->palette_code_bits_ > b->palette_code_bits_) ? a->palette_code_bits_ : - b->palette_code_bits_; - for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) { - out->literal_[i] = a->literal_[i] + b->literal_[i]; - } - cost += PopulationCost(out->literal_, VP8LHistogramNumCodes(out)); - cost += ExtraCost(out->literal_ + 256, NUM_LENGTH_CODES); - if (cost > cost_threshold) return cost; - - for (i = 0; i < 256; ++i) out->red_[i] = a->red_[i] + b->red_[i]; - cost += PopulationCost(out->red_, 256); - if (cost > cost_threshold) return cost; - - for (i = 0; i < 256; ++i) out->blue_[i] = a->blue_[i] + b->blue_[i]; - cost += PopulationCost(out->blue_, 256); - if (cost > cost_threshold) return cost; - - for (i = 0; i < NUM_DISTANCE_CODES; ++i) { - out->distance_[i] = a->distance_[i] + b->distance_[i]; - } - cost += PopulationCost(out->distance_, NUM_DISTANCE_CODES); - cost += ExtraCost(out->distance_, NUM_DISTANCE_CODES); - if (cost > cost_threshold) return cost; - - for (i = 0; i < 256; ++i) out->alpha_[i] = a->alpha_[i] + b->alpha_[i]; - cost += PopulationCost(out->alpha_, 256); - - out->bit_cost_ = cost; - return cost - sum_cost; + return HistogramEstimateBitsHeader(p) + VP8LHistogramEstimateBitsBulk(p); } -// Same as HistogramAddEval(), except that the resulting histogram -// is not stored. Only the cost C(a+b) - C(a) is evaluated. We omit -// the term C(b) which is constant over all the evaluations. -static double HistogramAddThresh(const VP8LHistogram* const a, - const VP8LHistogram* const b, - double cost_threshold) { - int tmp[PIX_OR_COPY_CODES_MAX]; // <= max storage we'll need - int i; - double cost = -a->bit_cost_; - - for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) { - tmp[i] = a->literal_[i] + b->literal_[i]; - } - // note that the tests are ordered so that the usually largest - // cost shares come first. - cost += PopulationCost(tmp, VP8LHistogramNumCodes(a)); - cost += ExtraCost(tmp + 256, NUM_LENGTH_CODES); - if (cost > cost_threshold) return cost; - - for (i = 0; i < 256; ++i) tmp[i] = a->red_[i] + b->red_[i]; - cost += PopulationCost(tmp, 256); - if (cost > cost_threshold) return cost; - - for (i = 0; i < 256; ++i) tmp[i] = a->blue_[i] + b->blue_[i]; - cost += PopulationCost(tmp, 256); - if (cost > cost_threshold) return cost; - - for (i = 0; i < NUM_DISTANCE_CODES; ++i) { - tmp[i] = a->distance_[i] + b->distance_[i]; - } - cost += PopulationCost(tmp, NUM_DISTANCE_CODES); - cost += ExtraCost(tmp, NUM_DISTANCE_CODES); - if (cost > cost_threshold) return cost; - - for (i = 0; i < 256; ++i) tmp[i] = a->alpha_[i] + b->alpha_[i]; - cost += PopulationCost(tmp, 256); - - return cost; -} - -// ----------------------------------------------------------------------------- - static void HistogramBuildImage(int xsize, int histo_bits, const VP8LBackwardRefs* const backward_refs, VP8LHistogramSet* const image) { @@ -359,15 +249,14 @@ static uint32_t MyRand(uint32_t *seed) { } static int HistogramCombine(const VP8LHistogramSet* const in, - VP8LHistogramSet* const out, int iter_mult, - int num_pairs, int num_tries_no_success) { + VP8LHistogramSet* const out, int num_pairs) { int ok = 0; int i, iter; uint32_t seed = 0; int tries_with_no_success = 0; - int out_size = in->size; - const int outer_iters = in->size * iter_mult; const int min_cluster_size = 2; + int out_size = in->size; + const int outer_iters = in->size * 3; VP8LHistogram* const histos = (VP8LHistogram*)malloc(2 * sizeof(*histos)); VP8LHistogram* cur_combo = histos + 0; // trial merged histogram VP8LHistogram* best_combo = histos + 1; // best merged histogram so far @@ -382,26 +271,29 @@ static int HistogramCombine(const VP8LHistogramSet* const in, // Collapse similar histograms in 'out'. for (iter = 0; iter < outer_iters && out_size >= min_cluster_size; ++iter) { + // We pick the best pair to be combined out of 'inner_iters' pairs. double best_cost_diff = 0.; - int best_idx1 = -1, best_idx2 = 1; + int best_idx1 = 0, best_idx2 = 1; int j; - const int num_tries = (num_pairs < out_size) ? num_pairs : out_size; seed += iter; - for (j = 0; j < num_tries; ++j) { + for (j = 0; j < num_pairs; ++j) { double curr_cost_diff; // Choose two histograms at random and try to combine them. const uint32_t idx1 = MyRand(&seed) % out_size; - const uint32_t tmp = (j & 7) + 1; + const uint32_t tmp = ((j & 7) + 1) % (out_size - 1); const uint32_t diff = (tmp < 3) ? tmp : MyRand(&seed) % (out_size - 1); const uint32_t idx2 = (idx1 + diff + 1) % out_size; if (idx1 == idx2) { continue; } + *cur_combo = *out->histograms[idx1]; + VP8LHistogramAdd(cur_combo, out->histograms[idx2]); + cur_combo->bit_cost_ = VP8LHistogramEstimateBits(cur_combo); // Calculate cost reduction on combining. - curr_cost_diff = HistogramAddEval(out->histograms[idx1], - out->histograms[idx2], - cur_combo, best_cost_diff); - if (curr_cost_diff < best_cost_diff) { // found a better pair? + curr_cost_diff = cur_combo->bit_cost_ + - out->histograms[idx1]->bit_cost_ + - out->histograms[idx2]->bit_cost_; + if (best_cost_diff > curr_cost_diff) { // found a better pair? { // swap cur/best combo histograms VP8LHistogram* const tmp_histo = cur_combo; cur_combo = best_combo; @@ -413,7 +305,7 @@ static int HistogramCombine(const VP8LHistogramSet* const in, } } - if (best_idx1 >= 0) { + if (best_cost_diff < 0.0) { *out->histograms[best_idx1] = *best_combo; // swap best_idx2 slot with last one (which is now unused) --out_size; @@ -423,7 +315,7 @@ static int HistogramCombine(const VP8LHistogramSet* const in, } tries_with_no_success = 0; } - if (++tries_with_no_success >= num_tries_no_success) { + if (++tries_with_no_success >= 50) { break; } } @@ -438,11 +330,20 @@ static int HistogramCombine(const VP8LHistogramSet* const in, // ----------------------------------------------------------------------------- // Histogram refinement -// What is the bit cost of moving square_histogram from cur_symbol to candidate. +// What is the bit cost of moving square_histogram from +// cur_symbol to candidate_symbol. +// TODO(skal): we don't really need to copy the histogram and Add(). Instead +// we just need VP8LDualHistogramEstimateBits(A, B) estimation function. static double HistogramDistance(const VP8LHistogram* const square_histogram, - const VP8LHistogram* const candidate, - double cost_threshold) { - return HistogramAddThresh(candidate, square_histogram, cost_threshold); + const VP8LHistogram* const candidate) { + const double previous_bit_cost = candidate->bit_cost_; + double new_bit_cost; + VP8LHistogram modified_histo; + modified_histo = *candidate; + VP8LHistogramAdd(&modified_histo, square_histogram); + new_bit_cost = VP8LHistogramEstimateBits(&modified_histo); + + return new_bit_cost - previous_bit_cost; } // Find the best 'out' histogram for each of the 'in' histograms. @@ -453,12 +354,11 @@ static void HistogramRemap(const VP8LHistogramSet* const in, int i; for (i = 0; i < in->size; ++i) { int best_out = 0; - double best_bits = - HistogramDistance(in->histograms[i], out->histograms[0], 1.e38); + double best_bits = HistogramDistance(in->histograms[i], out->histograms[0]); int k; for (k = 1; k < out->size; ++k) { const double cur_bits = - HistogramDistance(in->histograms[i], out->histograms[k], best_bits); + HistogramDistance(in->histograms[i], out->histograms[k]); if (cur_bits < best_bits) { best_bits = cur_bits; best_out = k; @@ -472,7 +372,7 @@ static void HistogramRemap(const VP8LHistogramSet* const in, HistogramClear(out->histograms[i]); } for (i = 0; i < in->size; ++i) { - HistogramAdd(in->histograms[i], out->histograms[symbols[i]]); + VP8LHistogramAdd(out->histograms[symbols[i]], in->histograms[i]); } } @@ -484,13 +384,8 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize, 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 num_histo_pairs = 10 + quality / 2; // For HistogramCombine(). const int histo_image_raw_size = histo_xsize * histo_ysize; - - // Heuristic params for HistogramCombine(). - const int num_tries_no_success = 8 + (quality >> 1); - const int iter_mult = (quality < 27) ? 1 : 1 + ((quality - 27) >> 4); - const int num_pairs = (quality < 25) ? 10 : (5 * quality) >> 3; - VP8LHistogramSet* const image_out = VP8LAllocateHistogramSet(histo_image_raw_size, cache_bits); if (image_out == NULL) return 0; @@ -498,8 +393,7 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize, // Build histogram image. HistogramBuildImage(xsize, histo_bits, refs, image_out); // Collapse similar histograms. - if (!HistogramCombine(image_out, image_in, iter_mult, num_pairs, - num_tries_no_success)) { + if (!HistogramCombine(image_out, image_in, num_histo_pairs)) { goto Error; } // Find the optimal map from original histograms to the final ones. diff --git a/drivers/webp/enc/histogram.h b/drivers/webp/enc/histogram.h index 4d346a857b..ec573c5c85 100644 --- a/drivers/webp/enc/histogram.h +++ b/drivers/webp/enc/histogram.h @@ -1,10 +1,8 @@ // Copyright 2012 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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/ // ----------------------------------------------------------------------------- // // Author: Jyrki Alakuijala (jyrki@google.com) @@ -24,7 +22,7 @@ #include "../webp/format_constants.h" #include "../webp/types.h" -#ifdef __cplusplus +#if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif @@ -82,6 +80,22 @@ double VP8LHistogramEstimateBits(const VP8LHistogram* const p); // represent the entropy code itself. double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p); +static WEBP_INLINE void VP8LHistogramAdd(VP8LHistogram* const p, + const VP8LHistogram* const a) { + int i; + for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) { + p->literal_[i] += a->literal_[i]; + } + for (i = 0; i < NUM_DISTANCE_CODES; ++i) { + p->distance_[i] += a->distance_[i]; + } + for (i = 0; i < 256; ++i) { + p->red_[i] += a->red_[i]; + p->blue_[i] += a->blue_[i]; + p->alpha_[i] += a->alpha_[i]; + } +} + static WEBP_INLINE int VP8LHistogramNumCodes(const VP8LHistogram* const p) { return 256 + NUM_LENGTH_CODES + ((p->palette_code_bits_ > 0) ? (1 << p->palette_code_bits_) : 0); @@ -94,7 +108,7 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize, VP8LHistogramSet* const image_in, uint16_t* const histogram_symbols); -#ifdef __cplusplus +#if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/drivers/webp/enc/iterator.c b/drivers/webp/enc/iterator.c index e42ad001ac..86e473bcf0 100644 --- a/drivers/webp/enc/iterator.c +++ b/drivers/webp/enc/iterator.c @@ -1,10 +1,8 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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 @@ -15,16 +13,21 @@ #include "./vp8enci.h" +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + //------------------------------------------------------------------------------ // VP8Iterator //------------------------------------------------------------------------------ static void InitLeft(VP8EncIterator* const it) { - it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] = + const VP8Encoder* const enc = it->enc_; + enc->y_left_[-1] = enc->u_left_[-1] = enc->v_left_[-1] = (it->y_ > 0) ? 129 : 127; - memset(it->y_left_, 129, 16); - memset(it->u_left_, 129, 8); - memset(it->v_left_, 129, 8); + memset(enc->y_left_, 129, 16); + memset(enc->u_left_, 129, 8); + memset(enc->v_left_, 129, 8); it->left_nz_[8] = 0; } @@ -35,60 +38,43 @@ static void InitTop(VP8EncIterator* const it) { memset(enc->nz_, 0, enc->mb_w_ * sizeof(*enc->nz_)); } -void VP8IteratorSetRow(VP8EncIterator* const it, int y) { +void VP8IteratorReset(VP8EncIterator* const it) { VP8Encoder* const enc = it->enc_; it->x_ = 0; - it->y_ = y; - it->bw_ = &enc->parts_[y & (enc->num_parts_ - 1)]; - it->preds_ = enc->preds_ + y * 4 * enc->preds_w_; + it->y_ = 0; + it->y_offset_ = 0; + it->uv_offset_ = 0; + it->mb_ = enc->mb_info_; + it->preds_ = enc->preds_; it->nz_ = enc->nz_; - it->mb_ = enc->mb_info_ + y * enc->mb_w_; - it->y_top_ = enc->y_top_; - it->uv_top_ = enc->uv_top_; - InitLeft(it); -} - -void VP8IteratorReset(VP8EncIterator* const it) { - VP8Encoder* const enc = it->enc_; - VP8IteratorSetRow(it, 0); - VP8IteratorSetCountDown(it, enc->mb_w_ * enc->mb_h_); // default + 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 VP8IteratorSetCountDown(VP8EncIterator* const it, int count_down) { - it->count_down_ = it->count_down0_ = count_down; -} - -int VP8IteratorIsDone(const VP8EncIterator* const it) { - return (it->count_down_ <= 0); -} - void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it) { it->enc_ = enc; it->y_stride_ = enc->pic_->y_stride; it->uv_stride_ = enc->pic_->uv_stride; - it->yuv_in_ = (uint8_t*)DO_ALIGN(it->yuv_mem_); - it->yuv_out_ = it->yuv_in_ + YUV_SIZE; - it->yuv_out2_ = it->yuv_out_ + YUV_SIZE; - it->yuv_p_ = it->yuv_out2_ + YUV_SIZE; + // 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_; it->percent0_ = enc->percent_; - it->y_left_ = (uint8_t*)DO_ALIGN(it->yuv_left_mem_ + 1); - it->u_left_ = it->y_left_ + 16 + 16; - it->v_left_ = it->u_left_ + 16; VP8IteratorReset(it); } int VP8IteratorProgress(const VP8EncIterator* const it, int delta) { VP8Encoder* const enc = it->enc_; - if (delta && enc->pic_->progress_hook != NULL) { - const int done = it->count_down0_ - it->count_down_; - const int percent = (it->count_down0_ <= 0) + if (delta && enc->pic_->progress_hook) { + const int percent = (enc->mb_h_ <= 1) ? it->percent0_ - : it->percent0_ + delta * done / it->count_down0_; + : it->percent0_ + delta * it->y_ / (enc->mb_h_ - 1); return WebPReportProgress(enc->pic_, percent, &enc->percent_); } return 1; @@ -98,8 +84,6 @@ int VP8IteratorProgress(const VP8EncIterator* const it, int delta) { // Import the source samples into the cache. Takes care of replicating // boundary pixels if necessary. -static WEBP_INLINE int MinSize(int a, int b) { return (a < b) ? a : b; } - static void ImportBlock(const uint8_t* src, int src_stride, uint8_t* dst, int w, int h, int size) { int i; @@ -117,55 +101,30 @@ static void ImportBlock(const uint8_t* src, int src_stride, } } -static void ImportLine(const uint8_t* src, int src_stride, - uint8_t* dst, int len, int total_len) { - int i; - for (i = 0; i < len; ++i, src += src_stride) dst[i] = *src; - for (; i < total_len; ++i) dst[i] = dst[len - 1]; -} - -void VP8IteratorImport(VP8EncIterator* const it, uint8_t* tmp_32) { +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* const ysrc = pic->y + (y * pic->y_stride + x) * 16; + const uint8_t* const ysrc = pic->y + (y * pic->y_stride + x) * 16; const uint8_t* const usrc = pic->u + (y * pic->uv_stride + x) * 8; const uint8_t* const vsrc = pic->v + (y * pic->uv_stride + x) * 8; - const int w = MinSize(pic->width - x * 16, 16); - const int h = MinSize(pic->height - y * 16, 16); - const int uv_w = (w + 1) >> 1; - const int uv_h = (h + 1) >> 1; - - ImportBlock(ysrc, pic->y_stride, it->yuv_in_ + Y_OFF, w, h, 16); - ImportBlock(usrc, pic->uv_stride, it->yuv_in_ + U_OFF, uv_w, uv_h, 8); - ImportBlock(vsrc, pic->uv_stride, it->yuv_in_ + V_OFF, uv_w, uv_h, 8); - - if (tmp_32 == NULL) return; - - // Import source (uncompressed) samples into boundary. - if (x == 0) { - InitLeft(it); - } else { - if (y == 0) { - it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] = 127; - } else { - it->y_left_[-1] = ysrc[- 1 - pic->y_stride]; - it->u_left_[-1] = usrc[- 1 - pic->uv_stride]; - it->v_left_[-1] = vsrc[- 1 - pic->uv_stride]; - } - ImportLine(ysrc - 1, pic->y_stride, it->y_left_, h, 16); - ImportLine(usrc - 1, pic->uv_stride, it->u_left_, uv_h, 8); - ImportLine(vsrc - 1, pic->uv_stride, it->v_left_, uv_h, 8); - } - - it->y_top_ = tmp_32 + 0; - it->uv_top_ = tmp_32 + 16; - if (y == 0) { - memset(tmp_32, 127, 32 * sizeof(*tmp_32)); - } else { - ImportLine(ysrc - pic->y_stride, 1, tmp_32, w, 16); - ImportLine(usrc - pic->uv_stride, 1, tmp_32 + 16, uv_w, 8); - ImportLine(vsrc - pic->uv_stride, 1, tmp_32 + 16 + 8, uv_w, 8); + uint8_t* const ydst = it->yuv_in_ + Y_OFF; + uint8_t* const udst = it->yuv_in_ + U_OFF; + uint8_t* const vdst = it->yuv_in_ + V_OFF; + int w = (pic->width - x * 16); + int h = (pic->height - y * 16); + + if (w > 16) w = 16; + if (h > 16) h = 16; + + // Luma plane + ImportBlock(ysrc, pic->y_stride, ydst, w, h, 16); + + { // U/V planes + const int uv_w = (w + 1) >> 1; + const int uv_h = (h + 1) >> 1; + ImportBlock(usrc, pic->uv_stride, udst, uv_w, uv_h, 8); + ImportBlock(vsrc, pic->uv_stride, vdst, uv_w, uv_h, 8); } } @@ -281,44 +240,48 @@ void VP8IteratorBytesToNz(VP8EncIterator* const it) { #undef BIT //------------------------------------------------------------------------------ -// Advance to the next position, doing the bookkeeping. +// Advance to the next position, doing the bookeeping. -void VP8IteratorSaveBoundary(VP8EncIterator* const it) { +int VP8IteratorNext(VP8EncIterator* const it, + const uint8_t* const block_to_save) { VP8Encoder* const enc = it->enc_; - const int x = it->x_, y = it->y_; - const uint8_t* const ysrc = it->yuv_out_ + Y_OFF; - const uint8_t* const uvsrc = it->yuv_out_ + U_OFF; - if (x < enc->mb_w_ - 1) { // left - int i; - for (i = 0; i < 16; ++i) { - it->y_left_[i] = ysrc[15 + i * BPS]; + 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]; } - for (i = 0; i < 8; ++i) { - it->u_left_[i] = uvsrc[7 + i * BPS]; - it->v_left_[i] = uvsrc[15 + i * BPS]; + 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); } - // top-left (before 'top'!) - it->y_left_[-1] = it->y_top_[15]; - it->u_left_[-1] = it->uv_top_[0 + 7]; - it->v_left_[-1] = it->uv_top_[8 + 7]; } - if (y < enc->mb_h_ - 1) { // top - memcpy(it->y_top_, ysrc + 15 * BPS, 16); - memcpy(it->uv_top_, uvsrc + 7 * BPS, 8 + 8); - } -} -int VP8IteratorNext(VP8EncIterator* const it) { + it->mb_++; it->preds_ += 4; - it->mb_ += 1; - it->nz_ += 1; - it->y_top_ += 16; - it->uv_top_ += 16; - it->x_ += 1; - if (it->x_ == it->enc_->mb_w_) { - VP8IteratorSetRow(it, ++it->y_); + 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->count_down_); + return (0 < --it->done_); } //------------------------------------------------------------------------------ @@ -405,15 +368,15 @@ void VP8IteratorStartI4(VP8EncIterator* const it) { // Import the boundary samples for (i = 0; i < 17; ++i) { // left - it->i4_boundary_[i] = it->y_left_[15 - i]; + it->i4_boundary_[i] = enc->y_left_[15 - i]; } for (i = 0; i < 16; ++i) { // top - it->i4_boundary_[17 + i] = it->y_top_[i]; + 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] = it->y_top_[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) { @@ -454,3 +417,6 @@ int VP8IteratorRotateI4(VP8EncIterator* const it, //------------------------------------------------------------------------------ +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/drivers/webp/enc/layer.c b/drivers/webp/enc/layer.c index 2402362359..423127df63 100644 --- a/drivers/webp/enc/layer.c +++ b/drivers/webp/enc/layer.c @@ -1,10 +1,8 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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/ // ----------------------------------------------------------------------------- // // Enhancement layer (for YUV444/422) @@ -15,6 +13,10 @@ #include "./vp8enci.h" +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + //------------------------------------------------------------------------------ void VP8EncInitLayer(VP8Encoder* const enc) { @@ -42,3 +44,6 @@ void VP8EncDeleteLayer(VP8Encoder* enc) { free(enc->layer_data_); } +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/drivers/webp/enc/picture.c b/drivers/webp/enc/picture.c index 011690d065..44eed06083 100644 --- a/drivers/webp/enc/picture.c +++ b/drivers/webp/enc/picture.c @@ -1,10 +1,8 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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, ... @@ -16,15 +14,14 @@ #include <math.h> #include "./vp8enci.h" -#include "../utils/alpha_processing.h" -#include "../utils/random.h" #include "../utils/rescaler.h" #include "../utils/utils.h" #include "../dsp/dsp.h" #include "../dsp/yuv.h" -// Uncomment to disable gamma-compression during RGB->U/V averaging -#define USE_GAMMA_COMPRESSION +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif #define HALVE(x) (((x) + 1) >> 1) #define IS_YUV_CSP(csp, YUV_CSP) (((csp) & WEBP_CSP_UV_MASK) == (YUV_CSP)) @@ -35,10 +32,6 @@ static const union { } test_endian = { 0xff000000u }; #define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff) -static WEBP_INLINE uint32_t MakeARGB32(int r, int g, int b) { - return (0xff000000u | (r << 16) | (g << 8) | b); -} - //------------------------------------------------------------------------------ // WebPPicture //------------------------------------------------------------------------------ @@ -123,7 +116,6 @@ int WebPPictureAlloc(WebPPicture* picture) { picture->v0 = mem; mem += uv0_size; } - (void)mem; // makes the static analyzer happy } else { void* memory; const uint64_t argb_size = (uint64_t)width * height; @@ -298,11 +290,8 @@ int WebPPictureView(const WebPPicture* src, dst->y = src->y + top * src->y_stride + left; dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1); dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1); - dst->y_stride = src->y_stride; - dst->uv_stride = src->uv_stride; if (src->a != NULL) { dst->a = src->a + top * src->a_stride + left; - dst->a_stride = src->a_stride; } #ifdef WEBP_EXPERIMENTAL_FEATURES if (src->u0 != NULL) { @@ -310,12 +299,10 @@ int WebPPictureView(const WebPPicture* src, IS_YUV_CSP(dst->colorspace, WEBP_YUV422) ? (left >> 1) : left; dst->u0 = src->u0 + top * src->uv0_stride + left_pos; dst->v0 = src->v0 + top * src->uv0_stride + left_pos; - dst->uv0_stride = src->uv0_stride; } #endif } else { dst->argb = src->argb + top * src->argb_stride + left; - dst->argb_stride = src->argb_stride; } return 1; } @@ -401,28 +388,6 @@ static void RescalePlane(const uint8_t* src, } } -static void AlphaMultiplyARGB(WebPPicture* const pic, int inverse) { - uint32_t* ptr = pic->argb; - int y; - for (y = 0; y < pic->height; ++y) { - WebPMultARGBRow(ptr, pic->width, inverse); - ptr += pic->argb_stride; - } -} - -static void AlphaMultiplyY(WebPPicture* const pic, int inverse) { - const uint8_t* ptr_a = pic->a; - if (ptr_a != NULL) { - uint8_t* ptr_y = pic->y; - int y; - for (y = 0; y < pic->height; ++y) { - WebPMultRow(ptr_y, ptr_a, pic->width, inverse); - ptr_y += pic->y_stride; - ptr_a += pic->a_stride; - } - } -} - int WebPPictureRescale(WebPPicture* pic, int width, int height) { WebPPicture tmp; int prev_width, prev_height; @@ -453,19 +418,9 @@ int WebPPictureRescale(WebPPicture* pic, int width, int height) { WebPPictureFree(&tmp); return 0; } - // If present, we need to rescale alpha first (for AlphaMultiplyY). - if (pic->a != NULL) { - RescalePlane(pic->a, prev_width, prev_height, pic->a_stride, - tmp.a, width, height, tmp.a_stride, work, 1); - } - // We take transparency into account on the luma plane only. That's not - // totally exact blending, but still is a good approximation. - AlphaMultiplyY(pic, 0); RescalePlane(pic->y, prev_width, prev_height, pic->y_stride, tmp.y, width, height, tmp.y_stride, work, 1); - AlphaMultiplyY(&tmp, 1); - RescalePlane(pic->u, HALVE(prev_width), HALVE(prev_height), pic->uv_stride, tmp.u, @@ -475,6 +430,10 @@ int WebPPictureRescale(WebPPicture* pic, int width, int height) { tmp.v, HALVE(width), HALVE(height), tmp.uv_stride, work, 1); + if (tmp.a != NULL) { + RescalePlane(pic->a, prev_width, prev_height, pic->a_stride, + tmp.a, width, height, tmp.a_stride, work, 1); + } #ifdef WEBP_EXPERIMENTAL_FEATURES if (tmp.u0 != NULL) { const int s = IS_YUV_CSP(tmp.colorspace, WEBP_YUV422) ? 2 : 1; @@ -492,16 +451,13 @@ int WebPPictureRescale(WebPPicture* pic, int width, int height) { WebPPictureFree(&tmp); return 0; } - // In order to correctly interpolate colors, we need to apply the alpha - // weighting first (black-matting), scale the RGB values, and remove - // the premultiplication afterward (while preserving the alpha channel). - AlphaMultiplyARGB(pic, 0); + RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height, pic->argb_stride * 4, (uint8_t*)tmp.argb, width, height, tmp.argb_stride * 4, work, 4); - AlphaMultiplyARGB(&tmp, 1); + } WebPPictureFree(pic); free(work); @@ -590,101 +546,20 @@ int WebPPictureHasTransparency(const WebPPicture* picture) { //------------------------------------------------------------------------------ // RGB -> YUV conversion -static int RGBToY(int r, int g, int b, VP8Random* const rg) { - return VP8RGBToY(r, g, b, VP8RandomBits(rg, YUV_FIX)); -} - -static int RGBToU(int r, int g, int b, VP8Random* const rg) { - return VP8RGBToU(r, g, b, VP8RandomBits(rg, YUV_FIX + 2)); -} - -static int RGBToV(int r, int g, int b, VP8Random* const rg) { - return VP8RGBToV(r, g, b, VP8RandomBits(rg, YUV_FIX + 2)); -} - -//------------------------------------------------------------------------------ - -#if defined(USE_GAMMA_COMPRESSION) - -// gamma-compensates loss of resolution during chroma subsampling -#define kGamma 0.80 -#define kGammaFix 12 // fixed-point precision for linear values -#define kGammaScale ((1 << kGammaFix) - 1) -#define kGammaTabFix 7 // fixed-point fractional bits precision -#define kGammaTabScale (1 << kGammaTabFix) -#define kGammaTabRounder (kGammaTabScale >> 1) -#define kGammaTabSize (1 << (kGammaFix - kGammaTabFix)) - -static int kLinearToGammaTab[kGammaTabSize + 1]; -static uint16_t kGammaToLinearTab[256]; -static int kGammaTablesOk = 0; - -static void InitGammaTables(void) { - if (!kGammaTablesOk) { - int v; - const double scale = 1. / kGammaScale; - for (v = 0; v <= 255; ++v) { - kGammaToLinearTab[v] = - (uint16_t)(pow(v / 255., kGamma) * kGammaScale + .5); - } - for (v = 0; v <= kGammaTabSize; ++v) { - const double x = scale * (v << kGammaTabFix); - kLinearToGammaTab[v] = (int)(pow(x, 1. / kGamma) * 255. + .5); - } - kGammaTablesOk = 1; - } -} - -static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { - return kGammaToLinearTab[v]; -} - -// Convert a linear value 'v' to YUV_FIX+2 fixed-point precision -// U/V value, suitable for RGBToU/V calls. -static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) { - const int v = base_value << shift; // final uplifted value - const int tab_pos = v >> (kGammaTabFix + 2); // integer part - const int x = v & ((kGammaTabScale << 2) - 1); // fractional part - const int v0 = kLinearToGammaTab[tab_pos]; - const int v1 = kLinearToGammaTab[tab_pos + 1]; - const int y = v1 * x + v0 * ((kGammaTabScale << 2) - x); // interpolate - return (y + kGammaTabRounder) >> kGammaTabFix; // descale -} - -#else - -static void InitGammaTables(void) {} -static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { return v; } -static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) { - (void)shift; - return v; -} - -#endif // USE_GAMMA_COMPRESSION - -//------------------------------------------------------------------------------ - -#define SUM4(ptr) LinearToGamma( \ - GammaToLinear((ptr)[0]) + \ - GammaToLinear((ptr)[step]) + \ - GammaToLinear((ptr)[rgb_stride]) + \ - GammaToLinear((ptr)[rgb_stride + step]), 0) \ - -#define SUM2H(ptr) \ - LinearToGamma(GammaToLinear((ptr)[0]) + GammaToLinear((ptr)[step]), 1) -#define SUM2V(ptr) \ - LinearToGamma(GammaToLinear((ptr)[0]) + GammaToLinear((ptr)[rgb_stride]), 1) -#define SUM1(ptr) \ - LinearToGamma(GammaToLinear((ptr)[0]), 2) - +// 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] = RGBToU(r, g, b, &rg); \ - picture->v[dst] = RGBToV(r, g, b, &rg); \ + picture->u[dst] = VP8RGBToU(r, g, b); \ + picture->v[dst] = VP8RGBToV(r, g, b); \ } #define RGB_TO_UV0(x_in, x_out, y, SUM) { \ @@ -693,8 +568,8 @@ static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) { const int r = SUM(r_ptr + src); \ const int g = SUM(g_ptr + src); \ const int b = SUM(b_ptr + src); \ - picture->u0[dst] = RGBToU(r, g, b, &rg); \ - picture->v0[dst] = RGBToV(r, g, b, &rg); \ + picture->u0[dst] = VP8RGBToU(r, g, b); \ + picture->v0[dst] = VP8RGBToV(r, g, b); \ } static void MakeGray(WebPPicture* const picture) { @@ -713,14 +588,12 @@ static int ImportYUVAFromRGBA(const uint8_t* const r_ptr, const uint8_t* const a_ptr, int step, // bytes per pixel int rgb_stride, // bytes per scanline - float dithering, WebPPicture* const picture) { const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK; int x, y; const int width = picture->width; const int height = picture->height; const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride); - VP8Random rg; picture->colorspace = uv_csp; picture->use_argb = 0; @@ -729,15 +602,12 @@ static int ImportYUVAFromRGBA(const uint8_t* const r_ptr, } if (!WebPPictureAlloc(picture)) return 0; - VP8InitRandom(&rg, dithering); - InitGammaTables(); - // Import luma plane for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { const int offset = step * x + y * rgb_stride; picture->y[x + y * picture->y_stride] = - RGBToY(r_ptr[offset], g_ptr[offset], b_ptr[offset], &rg); + VP8RGBToY(r_ptr[offset], g_ptr[offset], b_ptr[offset]); } } @@ -785,7 +655,6 @@ static int ImportYUVAFromRGBA(const uint8_t* const r_ptr, if (has_alpha) { assert(step >= 4); - assert(picture->a != NULL); for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { picture->a[x + y * picture->a_stride] = @@ -808,7 +677,7 @@ static int Import(WebPPicture* const picture, if (!picture->use_argb) { return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride, - 0.f /* no dithering */, picture); + picture); } if (import_alpha) { picture->colorspace |= WEBP_CSP_ALPHA_BIT; @@ -823,7 +692,10 @@ static int Import(WebPPicture* const picture, for (x = 0; x < width; ++x) { const int offset = step * x + y * rgb_stride; const uint32_t argb = - MakeARGB32(r_ptr[offset], g_ptr[offset], b_ptr[offset]); + 0xff000000u | + (r_ptr[offset] << 16) | + (g_ptr[offset] << 8) | + (b_ptr[offset]); picture->argb[x + y * picture->argb_stride] = argb; } } @@ -833,7 +705,7 @@ static int Import(WebPPicture* const picture, for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { const int offset = step * x + y * rgb_stride; - const uint32_t argb = ((uint32_t)a_ptr[offset] << 24) | + const uint32_t argb = (a_ptr[offset] << 24) | (r_ptr[offset] << 16) | (g_ptr[offset] << 8) | (b_ptr[offset]); @@ -884,7 +756,8 @@ int WebPPictureImportBGRX(WebPPicture* picture, int WebPPictureYUVAToARGB(WebPPicture* picture) { if (picture == NULL) return 0; - if (picture->y == NULL || picture->u == NULL || picture->v == NULL) { + if (picture->memory_ == NULL || picture->y == NULL || + picture->u == NULL || picture->v == NULL) { return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); } if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) { @@ -907,7 +780,7 @@ int WebPPictureYUVAToARGB(WebPPicture* picture) { WebPUpsampleLinePairFunc upsample = WebPGetLinePairConverter(ALPHA_IS_LAST); // First row, with replicated top samples. - upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width); + upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, width); cur_y += picture->y_stride; dst += argb_stride; // Center rows. @@ -928,11 +801,11 @@ int WebPPictureYUVAToARGB(WebPPicture* picture) { // Insert alpha values if needed, in replacement for the default 0xff ones. if (picture->colorspace & WEBP_CSP_ALPHA_BIT) { for (y = 0; y < height; ++y) { - uint32_t* const argb_dst = picture->argb + y * picture->argb_stride; + uint32_t* const dst = picture->argb + y * picture->argb_stride; const uint8_t* const src = picture->a + y * picture->a_stride; int x; for (x = 0; x < width; ++x) { - argb_dst[x] = (argb_dst[x] & 0x00ffffffu) | ((uint32_t)src[x] << 24); + dst[x] = (dst[x] & 0x00ffffffu) | (src[x] << 24); } } } @@ -940,8 +813,7 @@ int WebPPictureYUVAToARGB(WebPPicture* picture) { return 1; } -int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace, - float dithering) { +int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) { if (picture == NULL) return 0; if (picture->argb == NULL) { return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); @@ -957,8 +829,7 @@ int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace, PictureResetARGB(&tmp); // reset ARGB buffer so that it's not free()'d. tmp.use_argb = 0; tmp.colorspace = colorspace & WEBP_CSP_UV_MASK; - if (!ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride, dithering, - &tmp)) { + if (!ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride, &tmp)) { return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); } // Copy back the YUV specs into 'picture'. @@ -970,10 +841,6 @@ int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace, return 1; } -int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) { - return WebPPictureARGBToYUVADithered(picture, colorspace, 0.f); -} - //------------------------------------------------------------------------------ // Helper: clean up fully transparent area to help compressibility. @@ -1039,220 +906,67 @@ void WebPCleanupTransparentArea(WebPPicture* pic) { #undef SIZE #undef SIZE2 -//------------------------------------------------------------------------------ -// Blend color and remove transparency info - -#define BLEND(V0, V1, ALPHA) \ - ((((V0) * (255 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 16) -#define BLEND_10BIT(V0, V1, ALPHA) \ - ((((V0) * (1020 - (ALPHA)) + (V1) * (ALPHA)) * 0x101) >> 18) - -void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) { - const int red = (background_rgb >> 16) & 0xff; - const int green = (background_rgb >> 8) & 0xff; - const int blue = (background_rgb >> 0) & 0xff; - VP8Random rg; - int x, y; - if (pic == NULL) return; - VP8InitRandom(&rg, 0.f); - if (!pic->use_argb) { - const int uv_width = (pic->width >> 1); // omit last pixel during u/v loop - const int Y0 = RGBToY(red, green, blue, &rg); - // VP8RGBToU/V expects the u/v values summed over four pixels - const int U0 = RGBToU(4 * red, 4 * green, 4 * blue, &rg); - const int V0 = RGBToV(4 * red, 4 * green, 4 * blue, &rg); - const int has_alpha = pic->colorspace & WEBP_CSP_ALPHA_BIT; - if (!has_alpha || pic->a == NULL) return; // nothing to do - for (y = 0; y < pic->height; ++y) { - // Luma blending - uint8_t* const y_ptr = pic->y + y * pic->y_stride; - uint8_t* const a_ptr = pic->a + y * pic->a_stride; - for (x = 0; x < pic->width; ++x) { - const int alpha = a_ptr[x]; - if (alpha < 0xff) { - y_ptr[x] = BLEND(Y0, y_ptr[x], a_ptr[x]); - } - } - // Chroma blending every even line - if ((y & 1) == 0) { - uint8_t* const u = pic->u + (y >> 1) * pic->uv_stride; - uint8_t* const v = pic->v + (y >> 1) * pic->uv_stride; - uint8_t* const a_ptr2 = - (y + 1 == pic->height) ? a_ptr : a_ptr + pic->a_stride; - for (x = 0; x < uv_width; ++x) { - // Average four alpha values into a single blending weight. - // TODO(skal): might lead to visible contouring. Can we do better? - const int alpha = - a_ptr[2 * x + 0] + a_ptr[2 * x + 1] + - a_ptr2[2 * x + 0] + a_ptr2[2 * x + 1]; - u[x] = BLEND_10BIT(U0, u[x], alpha); - v[x] = BLEND_10BIT(V0, v[x], alpha); - } - if (pic->width & 1) { // rightmost pixel - const int alpha = 2 * (a_ptr[2 * x + 0] + a_ptr2[2 * x + 0]); - u[x] = BLEND_10BIT(U0, u[x], alpha); - v[x] = BLEND_10BIT(V0, v[x], alpha); - } - } - memset(a_ptr, 0xff, pic->width); - } - } else { - uint32_t* argb = pic->argb; - const uint32_t background = MakeARGB32(red, green, blue); - for (y = 0; y < pic->height; ++y) { - for (x = 0; x < pic->width; ++x) { - const int alpha = (argb[x] >> 24) & 0xff; - if (alpha != 0xff) { - if (alpha > 0) { - int r = (argb[x] >> 16) & 0xff; - int g = (argb[x] >> 8) & 0xff; - int b = (argb[x] >> 0) & 0xff; - r = BLEND(red, r, alpha); - g = BLEND(green, g, alpha); - b = BLEND(blue, b, alpha); - argb[x] = MakeARGB32(r, g, b); - } else { - argb[x] = background; - } - } - } - argb += pic->argb_stride; - } - } -} - -#undef BLEND -#undef BLEND_10BIT - -//------------------------------------------------------------------------------ -// local-min distortion -// -// For every pixel in the *reference* picture, we search for the local best -// match in the compressed image. This is not a symmetrical measure. - -// search radius. Shouldn't be too large. -#define RADIUS 2 - -static float AccumulateLSIM(const uint8_t* src, int src_stride, - const uint8_t* ref, int ref_stride, - int w, int h) { - int x, y; - double total_sse = 0.; - for (y = 0; y < h; ++y) { - const int y_0 = (y - RADIUS < 0) ? 0 : y - RADIUS; - const int y_1 = (y + RADIUS + 1 >= h) ? h : y + RADIUS + 1; - for (x = 0; x < w; ++x) { - const int x_0 = (x - RADIUS < 0) ? 0 : x - RADIUS; - const int x_1 = (x + RADIUS + 1 >= w) ? w : x + RADIUS + 1; - double best_sse = 255. * 255.; - const double value = (double)ref[y * ref_stride + x]; - int i, j; - for (j = y_0; j < y_1; ++j) { - const uint8_t* s = src + j * src_stride; - for (i = x_0; i < x_1; ++i) { - const double sse = (double)(s[i] - value) * (s[i] - value); - if (sse < best_sse) best_sse = sse; - } - } - total_sse += best_sse; - } - } - return (float)total_sse; -} -#undef RADIUS //------------------------------------------------------------------------------ // Distortion // Max value returned in case of exact similarity. static const double kMinDistortion_dB = 99.; -static float GetPSNR(const double v) { - return (float)((v > 0.) ? -4.3429448 * log(v / (255 * 255.)) - : kMinDistortion_dB); -} -int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref, +int WebPPictureDistortion(const WebPPicture* pic1, const WebPPicture* pic2, int type, float result[5]) { + int c; DistoStats stats[5]; int has_alpha; - int uv_w, uv_h; - if (src == NULL || ref == NULL || - src->width != ref->width || src->height != ref->height || - src->y == NULL || ref->y == NULL || - src->u == NULL || ref->u == NULL || - src->v == NULL || ref->v == NULL || + if (pic1 == NULL || pic2 == NULL || + pic1->width != pic2->width || pic1->height != pic2->height || + pic1->y == NULL || pic2->y == NULL || + pic1->u == NULL || pic2->u == NULL || + pic1->v == NULL || pic2->v == NULL || result == NULL) { return 0; } // TODO(skal): provide distortion for ARGB too. - if (src->use_argb == 1 || src->use_argb != ref->use_argb) { + if (pic1->use_argb == 1 || pic1->use_argb != pic2->use_argb) { return 0; } - has_alpha = !!(src->colorspace & WEBP_CSP_ALPHA_BIT); - if (has_alpha != !!(ref->colorspace & WEBP_CSP_ALPHA_BIT) || - (has_alpha && (src->a == NULL || ref->a == NULL))) { + has_alpha = !!(pic1->colorspace & WEBP_CSP_ALPHA_BIT); + if (has_alpha != !!(pic2->colorspace & WEBP_CSP_ALPHA_BIT) || + (has_alpha && (pic1->a == NULL || pic2->a == NULL))) { return 0; } memset(stats, 0, sizeof(stats)); - - uv_w = HALVE(src->width); - uv_h = HALVE(src->height); - if (type >= 2) { - float sse[4]; - sse[0] = AccumulateLSIM(src->y, src->y_stride, - ref->y, ref->y_stride, src->width, src->height); - sse[1] = AccumulateLSIM(src->u, src->uv_stride, - ref->u, ref->uv_stride, uv_w, uv_h); - sse[2] = AccumulateLSIM(src->v, src->uv_stride, - ref->v, ref->uv_stride, uv_w, uv_h); - sse[3] = has_alpha ? AccumulateLSIM(src->a, src->a_stride, - ref->a, ref->a_stride, - src->width, src->height) - : 0.f; - result[0] = GetPSNR(sse[0] / (src->width * src->height)); - result[1] = GetPSNR(sse[1] / (uv_w * uv_h)); - result[2] = GetPSNR(sse[2] / (uv_w * uv_h)); - result[3] = GetPSNR(sse[3] / (src->width * src->height)); - { - double total_sse = sse[0] + sse[1] + sse[2]; - int total_pixels = src->width * src->height + 2 * uv_w * uv_h; - if (has_alpha) { - total_pixels += src->width * src->height; - total_sse += sse[3]; - } - result[4] = GetPSNR(total_sse / total_pixels); - } - } else { - int c; - VP8SSIMAccumulatePlane(src->y, src->y_stride, - ref->y, ref->y_stride, - src->width, src->height, &stats[0]); - VP8SSIMAccumulatePlane(src->u, src->uv_stride, - ref->u, ref->uv_stride, - uv_w, uv_h, &stats[1]); - VP8SSIMAccumulatePlane(src->v, src->uv_stride, - ref->v, ref->uv_stride, - uv_w, uv_h, &stats[2]); - if (has_alpha) { - VP8SSIMAccumulatePlane(src->a, src->a_stride, - ref->a, ref->a_stride, - src->width, src->height, &stats[3]); - } - for (c = 0; c <= 4; ++c) { - if (type == 1) { - const double v = VP8SSIMGet(&stats[c]); - result[c] = (float)((v < 1.) ? -10.0 * log10(1. - v) - : kMinDistortion_dB); - } else { - const double v = VP8SSIMGetSquaredError(&stats[c]); - result[c] = GetPSNR(v); - } - // Accumulate forward - if (c < 4) VP8SSIMAddStats(&stats[c], &stats[4]); + VP8SSIMAccumulatePlane(pic1->y, pic1->y_stride, + pic2->y, pic2->y_stride, + pic1->width, pic1->height, &stats[0]); + VP8SSIMAccumulatePlane(pic1->u, pic1->uv_stride, + pic2->u, pic2->uv_stride, + (pic1->width + 1) >> 1, (pic1->height + 1) >> 1, + &stats[1]); + VP8SSIMAccumulatePlane(pic1->v, pic1->uv_stride, + pic2->v, pic2->uv_stride, + (pic1->width + 1) >> 1, (pic1->height + 1) >> 1, + &stats[2]); + if (has_alpha) { + VP8SSIMAccumulatePlane(pic1->a, pic1->a_stride, + pic2->a, pic2->a_stride, + pic1->width, pic1->height, &stats[3]); + } + for (c = 0; c <= 4; ++c) { + if (type == 1) { + const double v = VP8SSIMGet(&stats[c]); + result[c] = (float)((v < 1.) ? -10.0 * log10(1. - v) + : kMinDistortion_dB); + } else { + const double v = VP8SSIMGetSquaredError(&stats[c]); + result[c] = (float)((v > 0.) ? -4.3429448 * log(v / (255 * 255.)) + : kMinDistortion_dB); } + // Accumulate forward + if (c < 4) VP8SSIMAddStats(&stats[c], &stats[4]); } return 1; } @@ -1300,10 +1014,10 @@ size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \ return Encode(in, w, h, bps, IMPORTER, q, 0, out); \ } -ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB) -ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR) -ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA) -ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA) +ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB); +ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR); +ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA); +ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA); #undef ENCODE_FUNC @@ -1313,12 +1027,15 @@ size_t NAME(const uint8_t* in, int w, int h, int bps, uint8_t** out) { \ return Encode(in, w, h, bps, IMPORTER, LOSSLESS_DEFAULT_QUALITY, 1, out); \ } -LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGB, WebPPictureImportRGB) -LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR, WebPPictureImportBGR) -LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA, WebPPictureImportRGBA) -LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA) +LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGB, WebPPictureImportRGB); +LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR, WebPPictureImportBGR); +LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA, WebPPictureImportRGBA); +LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA); #undef LOSSLESS_ENCODE_FUNC //------------------------------------------------------------------------------ +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/drivers/webp/enc/quant.c b/drivers/webp/enc/quant.c index e1d202b5a3..ea153849c8 100644 --- a/drivers/webp/enc/quant.c +++ b/drivers/webp/enc/quant.c @@ -1,10 +1,8 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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 @@ -13,7 +11,6 @@ #include <assert.h> #include <math.h> -#include <stdlib.h> // for abs() #include "./vp8enci.h" #include "./cost.h" @@ -25,78 +22,16 @@ #define MID_ALPHA 64 // neutral value for susceptibility #define MIN_ALPHA 30 // lowest usable value for susceptibility -#define MAX_ALPHA 100 // higher meaningful value for susceptibility +#define 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 I4_PENALTY 4000 // Rate-penalty for quick i4/i16 decision - -// number of non-zero coeffs below which we consider the block very flat -// (and apply a penalty to complex predictions) -#define FLATNESS_LIMIT_I16 10 // I16 mode -#define FLATNESS_LIMIT_I4 3 // I4 mode -#define FLATNESS_LIMIT_UV 2 // UV mode -#define FLATNESS_PENALTY 140 // roughly ~1bit per block - #define MULT_8B(a, b) (((a) * (b) + 128) >> 8) -// #define DEBUG_BLOCK - -//------------------------------------------------------------------------------ - -#if defined(DEBUG_BLOCK) - -#include <stdio.h> -#include <stdlib.h> - -static void PrintBlockInfo(const VP8EncIterator* const it, - const VP8ModeScore* const rd) { - int i, j; - const int is_i16 = (it->mb_->type_ == 1); - printf("SOURCE / OUTPUT / ABS DELTA\n"); - for (j = 0; j < 24; ++j) { - if (j == 16) printf("\n"); // newline before the U/V block - for (i = 0; i < 16; ++i) printf("%3d ", it->yuv_in_[i + j * BPS]); - printf(" "); - for (i = 0; i < 16; ++i) printf("%3d ", it->yuv_out_[i + j * BPS]); - printf(" "); - for (i = 0; i < 16; ++i) { - printf("%1d ", abs(it->yuv_out_[i + j * BPS] - it->yuv_in_[i + j * BPS])); - } - printf("\n"); - } - printf("\nD:%d SD:%d R:%d H:%d nz:0x%x score:%d\n", - (int)rd->D, (int)rd->SD, (int)rd->R, (int)rd->H, (int)rd->nz, - (int)rd->score); - if (is_i16) { - printf("Mode: %d\n", rd->mode_i16); - printf("y_dc_levels:"); - for (i = 0; i < 16; ++i) printf("%3d ", rd->y_dc_levels[i]); - printf("\n"); - } else { - printf("Modes[16]: "); - for (i = 0; i < 16; ++i) printf("%d ", rd->modes_i4[i]); - printf("\n"); - } - printf("y_ac_levels:\n"); - for (j = 0; j < 16; ++j) { - for (i = is_i16 ? 1 : 0; i < 16; ++i) { - printf("%4d ", rd->y_ac_levels[j][i]); - } - printf("\n"); - } - printf("\n"); - printf("uv_levels (mode=%d):\n", rd->mode_uv); - for (j = 0; j < 8; ++j) { - for (i = 0; i < 16; ++i) { - printf("%4d ", rd->uv_levels[j][i]); - } - printf("\n"); - } -} - -#endif // DEBUG_BLOCK +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif //------------------------------------------------------------------------------ @@ -165,13 +100,31 @@ static const uint16_t kAcTable2[128] = { 385, 393, 401, 409, 416, 424, 432, 440 }; -static const uint8_t kBiasMatrices[3][2] = { // [luma-ac,luma-dc,chroma][dc,ac] - { 96, 110 }, { 96, 108 }, { 110, 115 } +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. +// Sharpening by (slightly) raising the hi-frequency coeffs (only for trellis). // Hack-ish but helpful for mid-bitrate range. Use with care. -#define SHARPEN_BITS 11 // number of descaling bits for sharpening bias static const uint8_t kFreqSharpening[16] = { 0, 30, 60, 90, 30, 60, 90, 90, @@ -184,30 +137,20 @@ static const uint8_t kFreqSharpening[16] = { // Returns the average quantizer static int ExpandMatrix(VP8Matrix* const m, int type) { - int i, sum; - for (i = 0; i < 2; ++i) { - const int is_ac_coeff = (i > 0); - const int bias = kBiasMatrices[type][is_ac_coeff]; - m->iq_[i] = (1 << QFIX) / m->q_[i]; - m->bias_[i] = BIAS(bias); - // zthresh_ is the exact value such that QUANTDIV(coeff, iQ, B) is: - // * zero if coeff <= zthresh - // * non-zero if coeff > zthresh - m->zthresh_[i] = ((1 << QFIX) - 1 - m->bias_[i]) / m->iq_[i]; - } + int i; + int sum = 0; for (i = 2; i < 16; ++i) { m->q_[i] = m->q_[1]; - m->iq_[i] = m->iq_[1]; - m->bias_[i] = m->bias_[1]; - m->zthresh_[i] = m->zthresh_[1]; } - for (sum = 0, i = 0; i < 16; ++i) { - if (type == 0) { // we only use sharpening for AC luma coeffs - m->sharpen_[i] = (kFreqSharpening[i] * m->q_[i]) >> SHARPEN_BITS; - } else { - m->sharpen_[i] = 0; - } - sum += m->q_[i]; + 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; } @@ -235,17 +178,17 @@ static void SetupMatrices(VP8Encoder* enc) { q16 = ExpandMatrix(&m->y2_, 1); quv = ExpandMatrix(&m->uv_, 2); - m->lambda_i4_ = (3 * q4 * q4) >> 7; - m->lambda_i16_ = (3 * q16 * q16); - m->lambda_uv_ = (3 * quv * quv) >> 6; - m->lambda_mode_ = (1 * q4 * q4) >> 7; - m->lambda_trellis_i4_ = (7 * q4 * q4) >> 3; - m->lambda_trellis_i16_ = (q16 * q16) >> 2; - m->lambda_trellis_uv_ = (quv *quv) << 1; - m->tlambda_ = (tlambda_scale * q4) >> 5; - - m->min_disto_ = 10 * m->y1_.q_[0]; // quantization-aware min disto - m->max_edge_ = 0; + // 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; + } } } @@ -254,21 +197,16 @@ static void SetupMatrices(VP8Encoder* enc) { // Very small filter-strength values have close to no visual effect. So we can // save a little decoding-CPU by turning filtering off for these. -#define FSTRENGTH_CUTOFF 2 +#define FSTRENGTH_CUTOFF 3 static void SetupFilterStrength(VP8Encoder* const enc) { int i; - // level0 is in [0..500]. Using '-f 50' as filter_strength is mid-filtering. - const int level0 = 5 * enc->config_->filter_strength; + const int level0 = enc->config_->filter_strength; for (i = 0; i < NUM_MB_SEGMENTS; ++i) { - VP8SegmentInfo* const m = &enc->dqm_[i]; - // We focus on the quantization of AC coeffs. - const int qstep = kAcTable[clip(m->quant_, 0, 127)] >> 2; - const int base_strength = - VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, qstep); - // Segments with lower complexity ('beta') will be less filtered. - const int f = base_strength * level0 / (256 + m->beta_); - m->fstrength_ = (f < FSTRENGTH_CUTOFF) ? 0 : (f > 63) ? 63 : f; + // 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_; @@ -286,90 +224,28 @@ static void SetupFilterStrength(VP8Encoder* const enc) { // We want to emulate jpeg-like behaviour where the expected "good" quality // is around q=75. Internally, our "good" middle is around c=50. So we // map accordingly using linear piece-wise function -static double QualityToCompression(double c) { - const double linear_c = (c < 0.75) ? c * (2. / 3.) : 2. * c - 1.; - // The file size roughly scales as pow(quantizer, 3.). Actually, the - // exponent is somewhere between 2.8 and 3.2, but we're mostly interested - // in the mid-quant range. So we scale the compressibility inversely to - // this power-law: quant ~= compression ^ 1/3. This law holds well for - // low quant. Finer modeling for high-quant would make use of kAcTable[] - // more explicitly. - const double v = pow(linear_c, 1 / 3.); - return v; -} - -static double QualityToJPEGCompression(double c, double alpha) { - // We map the complexity 'alpha' and quality setting 'c' to a compression - // exponent empirically matched to the compression curve of libjpeg6b. - // On average, the WebP output size will be roughly similar to that of a - // JPEG file compressed with same quality factor. - const double amin = 0.30; - const double amax = 0.85; - const double exp_min = 0.4; - const double exp_max = 0.9; - const double slope = (exp_min - exp_max) / (amax - amin); - // Linearly interpolate 'expn' from exp_min to exp_max - // in the [amin, amax] range. - const double expn = (alpha > amax) ? exp_min - : (alpha < amin) ? exp_max - : exp_max + slope * (alpha - amin); - const double v = pow(c, expn); - return v; -} - -static int SegmentsAreEquivalent(const VP8SegmentInfo* const S1, - const VP8SegmentInfo* const S2) { - return (S1->quant_ == S2->quant_) && (S1->fstrength_ == S2->fstrength_); -} - -static void SimplifySegments(VP8Encoder* const enc) { - int map[NUM_MB_SEGMENTS] = { 0, 1, 2, 3 }; - const int num_segments = enc->segment_hdr_.num_segments_; - int num_final_segments = 1; - int s1, s2; - for (s1 = 1; s1 < num_segments; ++s1) { // find similar segments - const VP8SegmentInfo* const S1 = &enc->dqm_[s1]; - int found = 0; - // check if we already have similar segment - for (s2 = 0; s2 < num_final_segments; ++s2) { - const VP8SegmentInfo* const S2 = &enc->dqm_[s2]; - if (SegmentsAreEquivalent(S1, S2)) { - found = 1; - break; - } - } - map[s1] = s2; - if (!found) { - if (num_final_segments != s1) { - enc->dqm_[num_final_segments] = enc->dqm_[s1]; - } - ++num_final_segments; - } - } - if (num_final_segments < num_segments) { // Remap - int i = enc->mb_w_ * enc->mb_h_; - while (i-- > 0) enc->mb_info_[i].segment_ = map[enc->mb_info_[i].segment_]; - enc->segment_hdr_.num_segments_ = num_final_segments; - // Replicate the trailing segment infos (it's mostly cosmetics) - for (i = num_final_segments; i < num_segments; ++i) { - enc->dqm_[i] = enc->dqm_[num_final_segments - 1]; - } - } +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->segment_hdr_.num_segments_; + const int num_segments = enc->config_->segments; const double amp = SNS_TO_DQ * enc->config_->sns_strength / 100. / 128.; - const double Q = quality / 100.; - const double c_base = enc->config_->emulate_jpeg_size ? - QualityToJPEGCompression(Q, enc->alpha_ / 255.) : - QualityToCompression(Q); + const double c_base = QualityToCompression(quality); for (i = 0; i < num_segments; ++i) { - // We modulate the base coefficient to accommodate for the quantization - // susceptibility and allow denser segments to be quantized more. - const double expn = 1. - amp * enc->dqm_[i].alpha_; + // 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.); @@ -405,11 +281,9 @@ void VP8SetSegmentParams(VP8Encoder* const enc, float quality) { enc->dq_uv_dc_ = dq_uv_dc; enc->dq_uv_ac_ = dq_uv_ac; - SetupFilterStrength(enc); // initialize segments' filtering, eventually - - if (num_segments > 1) SimplifySegments(enc); + SetupMatrices(enc); - SetupMatrices(enc); // finalize quantization matrices + SetupFilterStrength(enc); // initialize segments' filtering, eventually } //------------------------------------------------------------------------------ @@ -425,14 +299,16 @@ const int VP8I4ModeOffsets[NUM_BMODES] = { }; void VP8MakeLuma16Preds(const VP8EncIterator* const it) { - const uint8_t* const left = it->x_ ? it->y_left_ : NULL; - const uint8_t* const top = it->y_ ? it->y_top_ : NULL; + const VP8Encoder* const enc = it->enc_; + const uint8_t* const left = it->x_ ? enc->y_left_ : NULL; + const uint8_t* const top = it->y_ ? enc->y_top_ + it->x_ * 16 : NULL; VP8EncPredLuma16(it->yuv_p_, left, top); } void VP8MakeChroma8Preds(const VP8EncIterator* const it) { - const uint8_t* const left = it->x_ ? it->u_left_ : NULL; - const uint8_t* const top = it->y_ ? it->uv_top_ : NULL; + const VP8Encoder* const enc = it->enc_; + const uint8_t* const left = it->x_ ? enc->u_left_ : NULL; + const uint8_t* const top = it->y_ ? enc->uv_top_ + it->x_ * 16 : NULL; VP8EncPredChroma8(it->yuv_p_, left, top); } @@ -488,7 +364,6 @@ static void InitScore(VP8ModeScore* const rd) { rd->D = 0; rd->SD = 0; rd->R = 0; - rd->H = 0; rd->nz = 0; rd->score = MAX_COST; } @@ -497,7 +372,6 @@ static void CopyScore(VP8ModeScore* const dst, const VP8ModeScore* const src) { dst->D = src->D; dst->SD = src->SD; dst->R = src->R; - dst->H = src->H; dst->nz = src->nz; // note that nz is not accumulated, but just copied. dst->score = src->score; } @@ -506,7 +380,6 @@ static void AddScore(VP8ModeScore* const dst, const VP8ModeScore* const src) { dst->D += src->D; dst->SD += src->SD; dst->R += src->R; - dst->H += src->H; dst->nz |= src->nz; // here, new nz bits are accumulated. dst->score += src->score; } @@ -535,7 +408,7 @@ typedef struct { static WEBP_INLINE void SetRDScore(int lambda, VP8ModeScore* const rd) { // TODO: incorporate the "* 256" in the tables? - rd->score = (rd->R + rd->H) * lambda + 256 * (rd->D + rd->SD); + rd->score = rd->R * lambda + 256 * (rd->D + rd->SD); } static WEBP_INLINE score_t RDScoreTrellis(int lambda, score_t rate, @@ -598,10 +471,11 @@ static int TrellisQuantizeBlock(const VP8EncIterator* const it, // note: it's important to take sign of the _original_ coeff, // so we don't have to consider level < 0 afterward. const int sign = (in[j] < 0); - const int coeff0 = (sign ? -in[j] : in[j]) + mtx->sharpen_[j]; - int level0 = QUANTDIV(coeff0, iQ, B); - if (level0 > MAX_LEVEL) level0 = MAX_LEVEL; + int coeff0 = (sign ? -in[j] : in[j]) + mtx->sharpen_[j]; + int level0; + if (coeff0 > 2047) coeff0 = 2047; + level0 = QUANTDIV(coeff0, iQ, B); // test all alternate level values around level0. for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) { Node* const cur = &NODE(n, m); @@ -613,7 +487,7 @@ static int TrellisQuantizeBlock(const VP8EncIterator* const it, cur->sign = sign; cur->level = level; cur->ctx = (level == 0) ? 0 : (level == 1) ? 1 : 2; - if (level > MAX_LEVEL || level < 0) { // node is dead? + if (level >= 2048 || level < 0) { // node is dead? cur->cost = MAX_COST; continue; } @@ -706,10 +580,10 @@ static int ReconstructIntra16(VP8EncIterator* const it, VP8ModeScore* const rd, uint8_t* const yuv_out, int mode) { - VP8Encoder* const enc = it->enc_; + 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; - VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; + const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; int nz = 0; int n; int16_t tmp[16][16], dc_tmp[16]; @@ -718,7 +592,7 @@ static int ReconstructIntra16(VP8EncIterator* const it, VP8FTransform(src + VP8Scan[n], ref + VP8Scan[n], tmp[n]); } VP8FTransformWHT(tmp[0], dc_tmp); - nz |= VP8EncQuantizeBlockWHT(dc_tmp, rd->y_dc_levels, &dqm->y2_) << 24; + nz |= VP8EncQuantizeBlock(dc_tmp, rd->y_dc_levels, 0, &dqm->y2_) << 24; if (DO_TRELLIS_I16 && it->do_trellis_) { int x, y; @@ -813,18 +687,7 @@ static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd, //------------------------------------------------------------------------------ // RD-opt decision. Reconstruct each modes, evalue distortion and bit-cost. -// Pick the mode is lower RD-cost = Rate + lambda * Distortion. - -static void StoreMaxDelta(VP8SegmentInfo* const dqm, const int16_t DCs[16]) { - // We look at the first three AC coefficients to determine what is the average - // delta between each sub-4x4 block. - const int v0 = abs(DCs[1]); - const int v1 = abs(DCs[4]); - const int v2 = abs(DCs[5]); - int max_v = (v0 > v1) ? v1 : v0; - max_v = (v2 > max_v) ? v2 : max_v; - if (max_v > dqm->max_edge_) dqm->max_edge_ = max_v; -} +// Pick the mode is lower RD-cost = Rate + lamba * Distortion. static void SwapPtr(uint8_t** a, uint8_t** b) { uint8_t* const tmp = *a; @@ -836,23 +699,9 @@ static void SwapOut(VP8EncIterator* const it) { SwapPtr(&it->yuv_out_, &it->yuv_out2_); } -static score_t IsFlat(const int16_t* levels, int num_blocks, score_t thresh) { - score_t score = 0; - while (num_blocks-- > 0) { // TODO(skal): refine positional scoring? - int i; - for (i = 1; i < 16; ++i) { // omit DC, we're only interested in AC - score += (levels[i] != 0); - if (score > thresh) return 0; - } - levels += 16; - } - return 1; -} - static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* const rd) { - const int kNumBlocks = 16; - VP8Encoder* const enc = it->enc_; - VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; + const 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; @@ -860,7 +709,7 @@ static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* const rd) { int mode; rd->mode_i16 = -1; - for (mode = 0; mode < NUM_PRED_MODES; ++mode) { + for (mode = 0; mode < 4; ++mode) { uint8_t* const tmp_dst = it->yuv_out2_ + Y_OFF; // scratch buffer int nz; @@ -871,13 +720,8 @@ static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* const rd) { rd16.D = VP8SSE16x16(src, tmp_dst); rd16.SD = tlambda ? MULT_8B(tlambda, VP8TDisto16x16(src, tmp_dst, kWeightY)) : 0; - rd16.H = VP8FixedCostsI16[mode]; rd16.R = VP8GetCostLuma16(it, &rd16); - if (mode > 0 && - IsFlat(rd16.y_ac_levels[0], kNumBlocks, FLATNESS_LIMIT_I16)) { - // penalty to avoid flat area to be mispredicted by complex mode - rd16.R += FLATNESS_PENALTY * kNumBlocks; - } + rd16.R += VP8FixedCostsI16[mode]; // Since we always examine Intra16 first, we can overwrite *rd directly. SetRDScore(lambda, &rd16); @@ -892,13 +736,6 @@ static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* const rd) { } SetRDScore(dqm->lambda_mode_, rd); // finalize score for mode decision. VP8SetIntra16Mode(it, rd->mode_i16); - - // we have a blocky macroblock (only DCs are non-zero) with fairly high - // distortion, record max delta so we can later adjust the minimal filtering - // strength needed to smooth these blocks out. - if ((rd->nz & 0xffff) == 0 && rd->D > dqm->min_disto_) { - StoreMaxDelta(dqm, rd->y_dc_levels); - } } //------------------------------------------------------------------------------ @@ -928,11 +765,9 @@ static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) { } InitScore(&rd_best); - rd_best.H = 211; // '211' is the value of VP8BitCost(0, 145) - SetRDScore(dqm->lambda_mode_, &rd_best); + rd_best.score = 211; // '211' is the value of VP8BitCost(0, 145) VP8IteratorStartI4(it); do { - const int kNumBlocks = 1; VP8ModeScore rd_i4; int mode; int best_mode = -1; @@ -956,11 +791,8 @@ static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) { rd_tmp.SD = tlambda ? MULT_8B(tlambda, VP8TDisto4x4(src, tmp_dst, kWeightY)) : 0; - rd_tmp.H = mode_costs[mode]; rd_tmp.R = VP8GetCostLuma4(it, tmp_levels); - if (mode > 0 && IsFlat(tmp_levels, kNumBlocks, FLATNESS_LIMIT_I4)) { - rd_tmp.R += FLATNESS_PENALTY * kNumBlocks; - } + rd_tmp.R += mode_costs[mode]; SetRDScore(lambda, &rd_tmp); if (best_mode < 0 || rd_tmp.score < rd_i4.score) { @@ -972,17 +804,14 @@ static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) { } SetRDScore(dqm->lambda_mode_, &rd_i4); AddScore(&rd_best, &rd_i4); - if (rd_best.score >= rd->score) { - return 0; - } - total_header_bits += (int)rd_i4.H; // <- equal to mode_costs[best_mode]; - if (total_header_bits > enc->max_i4_header_bits_) { + total_header_bits += mode_costs[best_mode]; + if (rd_best.score >= rd->score || + total_header_bits > enc->max_i4_header_bits_) { return 0; } // Copy selected samples if not in the right place already. - if (best_block != best_blocks + VP8Scan[it->i4_]) { + 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)); @@ -998,7 +827,6 @@ static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) { //------------------------------------------------------------------------------ static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) { - const int kNumBlocks = 8; const VP8Encoder* const enc = it->enc_; const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; const int lambda = dqm->lambda_uv_; @@ -1010,7 +838,7 @@ static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) { rd->mode_uv = -1; InitScore(&rd_best); - for (mode = 0; mode < NUM_PRED_MODES; ++mode) { + for (mode = 0; mode < 4; ++mode) { VP8ModeScore rd_uv; // Reconstruct @@ -1019,11 +847,8 @@ static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) { // Compute RD-score rd_uv.D = VP8SSE16x8(src, tmp_dst); rd_uv.SD = 0; // TODO: should we call TDisto? it tends to flatten areas. - rd_uv.H = VP8FixedCostsUV[mode]; rd_uv.R = VP8GetCostUV(it, &rd_uv); - if (mode > 0 && IsFlat(rd_uv.uv_levels[0], kNumBlocks, FLATNESS_LIMIT_UV)) { - rd_uv.R += FLATNESS_PENALTY * kNumBlocks; - } + rd_uv.R += VP8FixedCostsUV[mode]; SetRDScore(lambda, &rd_uv); if (mode == 0 || rd_uv.score < rd_best.score) { @@ -1042,10 +867,10 @@ static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) { static void SimpleQuantize(VP8EncIterator* const it, VP8ModeScore* const rd) { const VP8Encoder* const enc = it->enc_; - const int is_i16 = (it->mb_->type_ == 1); + const int i16 = (it->mb_->type_ == 1); int nz = 0; - if (is_i16) { + if (i16) { nz = ReconstructIntra16(it, rd, it->yuv_out_ + Y_OFF, it->preds_[0]); } else { VP8IteratorStartI4(it); @@ -1064,66 +889,11 @@ static void SimpleQuantize(VP8EncIterator* const it, VP8ModeScore* const rd) { rd->nz = nz; } -// Refine intra16/intra4 sub-modes based on distortion only (not rate). -static void DistoRefine(VP8EncIterator* const it, int try_both_i4_i16) { - const int is_i16 = (it->mb_->type_ == 1); - score_t best_score = MAX_COST; - - if (try_both_i4_i16 || is_i16) { - int mode; - int best_mode = -1; - for (mode = 0; mode < NUM_PRED_MODES; ++mode) { - const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode]; - const uint8_t* const src = it->yuv_in_ + Y_OFF; - const score_t score = VP8SSE16x16(src, ref); - if (score < best_score) { - best_mode = mode; - best_score = score; - } - } - VP8SetIntra16Mode(it, best_mode); - } - if (try_both_i4_i16 || !is_i16) { - uint8_t modes_i4[16]; - // We don't evaluate the rate here, but just account for it through a - // constant penalty (i4 mode usually needs more bits compared to i16). - score_t score_i4 = (score_t)I4_PENALTY; - - VP8IteratorStartI4(it); - do { - int mode; - int best_sub_mode = -1; - score_t best_sub_score = MAX_COST; - const uint8_t* const src = it->yuv_in_ + Y_OFF + VP8Scan[it->i4_]; - - // TODO(skal): we don't really need the prediction pixels here, - // but just the distortion against 'src'. - VP8MakeIntra4Preds(it); - for (mode = 0; mode < NUM_BMODES; ++mode) { - const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[mode]; - const score_t score = VP8SSE4x4(src, ref); - if (score < best_sub_score) { - best_sub_mode = mode; - best_sub_score = score; - } - } - modes_i4[it->i4_] = best_sub_mode; - score_i4 += best_sub_score; - if (score_i4 >= best_score) break; - } while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF)); - if (score_i4 < best_score) { - VP8SetIntra4Mode(it, modes_i4); - } - } -} - //------------------------------------------------------------------------------ // Entry point -int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, - VP8RDLevel rd_opt) { +int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, int rd_opt) { int is_skipped; - const int method = it->enc_->method_; InitScore(rd); @@ -1132,21 +902,22 @@ int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, VP8MakeLuma16Preds(it); VP8MakeChroma8Preds(it); - if (rd_opt > RD_OPT_NONE) { - it->do_trellis_ = (rd_opt >= RD_OPT_TRELLIS_ALL); + // 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 (method >= 2) { + if (it->enc_->method_ >= 2) { PickBestIntra4(it, rd); } PickBestUV(it, rd); - if (rd_opt == RD_OPT_TRELLIS) { // finish off with trellis-optim now + if (rd_opt == 2) { it->do_trellis_ = 1; SimpleQuantize(it, rd); } } else { - // For method == 2, pick the best intra4/intra16 based on SSE (~tad slower). - // For method <= 1, we refine intra4 or intra16 (but don't re-examine mode). - DistoRefine(it, (method >= 2)); + // 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); @@ -1154,3 +925,6 @@ int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, return is_skipped; } +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/drivers/webp/enc/syntax.c b/drivers/webp/enc/syntax.c index 08cfe79ece..7c8c7b1a84 100644 --- a/drivers/webp/enc/syntax.c +++ b/drivers/webp/enc/syntax.c @@ -1,10 +1,8 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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 @@ -13,20 +11,35 @@ #include <assert.h> -#include "../utils/utils.h" -#include "../webp/format_constants.h" // RIFF constants -#include "../webp/mux_types.h" // ALPHA_FLAG +#include "../webp/format_constants.h" #include "./vp8enci.h" +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + //------------------------------------------------------------------------------ // Helper functions +// TODO(later): Move to webp/format_constants.h? +static void PutLE24(uint8_t* const data, uint32_t val) { + data[0] = (val >> 0) & 0xff; + data[1] = (val >> 8) & 0xff; + data[2] = (val >> 16) & 0xff; +} + +static void PutLE32(uint8_t* const data, uint32_t val) { + PutLE24(data, val); + data[3] = (val >> 24) & 0xff; +} + static int IsVP8XNeeded(const VP8Encoder* const enc) { return !!enc->has_alpha_; // Currently the only case when VP8X is needed. // This could change in the future. } static int PutPaddingByte(const WebPPicture* const pic) { + const uint8_t pad_byte[1] = { 0 }; return !!pic->writer(pad_byte, 1, pic); } @@ -60,14 +73,14 @@ static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) { assert(pic->width <= MAX_CANVAS_SIZE && pic->height <= MAX_CANVAS_SIZE); if (enc->has_alpha_) { - flags |= ALPHA_FLAG; + flags |= ALPHA_FLAG_BIT; } PutLE32(vp8x + TAG_SIZE, VP8X_CHUNK_SIZE); PutLE32(vp8x + CHUNK_HEADER_SIZE, flags); PutLE24(vp8x + CHUNK_HEADER_SIZE + 4, pic->width - 1); PutLE24(vp8x + CHUNK_HEADER_SIZE + 7, pic->height - 1); - if (!pic->writer(vp8x, sizeof(vp8x), pic)) { + if(!pic->writer(vp8x, sizeof(vp8x), pic)) { return VP8_ENC_ERROR_BAD_WRITE; } return VP8_ENC_OK; @@ -314,9 +327,7 @@ static size_t GeneratePartition0(VP8Encoder* const enc) { PutSegmentHeader(bw, enc); PutFilterHeader(bw, &enc->filter_hdr_); - VP8PutValue(bw, enc->num_parts_ == 8 ? 3 : - enc->num_parts_ == 4 ? 2 : - enc->num_parts_ == 2 ? 1 : 0, 2); + VP8PutValue(bw, enc->config_->partitions, 2); PutQuant(bw, enc); VP8PutBitUniform(bw, 0); // no proba update VP8WriteProbas(bw, &enc->proba_); @@ -421,3 +432,6 @@ int VP8EncWrite(VP8Encoder* const enc) { //------------------------------------------------------------------------------ +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/drivers/webp/enc/token.c b/drivers/webp/enc/token.c deleted file mode 100644 index e696642f16..0000000000 --- a/drivers/webp/enc/token.c +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. -// ----------------------------------------------------------------------------- -// -// Paginated token buffer -// -// A 'token' is a bit value associated with a probability, either fixed -// or a later-to-be-determined after statistics have been collected. -// For dynamic probability, we just record the slot id (idx) for the probability -// value in the final probability array (uint8_t* probas in VP8EmitTokens). -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <assert.h> -#include <stdlib.h> -#include <string.h> - -#include "./cost.h" -#include "./vp8enci.h" - -#if !defined(DISABLE_TOKEN_BUFFER) - -// we use pages to reduce the number of memcpy() -#define MAX_NUM_TOKEN 8192 // max number of token per page -#define FIXED_PROBA_BIT (1u << 14) - -struct VP8Tokens { - uint16_t tokens_[MAX_NUM_TOKEN]; // bit#15: bit - // bit #14: constant proba or idx - // bits 0..13: slot or constant proba - VP8Tokens* next_; -}; - -//------------------------------------------------------------------------------ - -void VP8TBufferInit(VP8TBuffer* const b) { - b->tokens_ = NULL; - b->pages_ = NULL; - b->last_page_ = &b->pages_; - b->left_ = 0; - b->error_ = 0; -} - -void VP8TBufferClear(VP8TBuffer* const b) { - if (b != NULL) { - const VP8Tokens* p = b->pages_; - while (p != NULL) { - const VP8Tokens* const next = p->next_; - free((void*)p); - p = next; - } - VP8TBufferInit(b); - } -} - -static int TBufferNewPage(VP8TBuffer* const b) { - VP8Tokens* const page = b->error_ ? NULL : (VP8Tokens*)malloc(sizeof(*page)); - if (page == NULL) { - b->error_ = 1; - return 0; - } - *b->last_page_ = page; - b->last_page_ = &page->next_; - b->left_ = MAX_NUM_TOKEN; - b->tokens_ = page->tokens_; - page->next_ = NULL; - return 1; -} - -//------------------------------------------------------------------------------ - -#define TOKEN_ID(t, b, ctx, p) \ - ((p) + NUM_PROBAS * ((ctx) + NUM_CTX * ((b) + NUM_BANDS * (t)))) - -static WEBP_INLINE int AddToken(VP8TBuffer* const b, - int bit, uint32_t proba_idx) { - assert(proba_idx < FIXED_PROBA_BIT); - assert(bit == 0 || bit == 1); - if (b->left_ > 0 || TBufferNewPage(b)) { - const int slot = --b->left_; - b->tokens_[slot] = (bit << 15) | proba_idx; - } - return bit; -} - -static WEBP_INLINE void AddConstantToken(VP8TBuffer* const b, - int bit, int proba) { - assert(proba < 256); - assert(bit == 0 || bit == 1); - if (b->left_ > 0 || TBufferNewPage(b)) { - const int slot = --b->left_; - b->tokens_[slot] = (bit << 15) | FIXED_PROBA_BIT | proba; - } -} - -int VP8RecordCoeffTokens(int ctx, int coeff_type, int first, int last, - const int16_t* const coeffs, - VP8TBuffer* const tokens) { - int n = first; - uint32_t base_id = TOKEN_ID(coeff_type, n, ctx, 0); - if (!AddToken(tokens, last >= 0, base_id + 0)) { - return 0; - } - - while (n < 16) { - const int c = coeffs[n++]; - const int sign = c < 0; - int v = sign ? -c : c; - if (!AddToken(tokens, v != 0, base_id + 1)) { - ctx = 0; - base_id = TOKEN_ID(coeff_type, VP8EncBands[n], ctx, 0); - continue; - } - if (!AddToken(tokens, v > 1, base_id + 2)) { - ctx = 1; - } else { - if (!AddToken(tokens, v > 4, base_id + 3)) { - if (AddToken(tokens, v != 2, base_id + 4)) - AddToken(tokens, v == 4, base_id + 5); - } else if (!AddToken(tokens, v > 10, base_id + 6)) { - if (!AddToken(tokens, v > 6, base_id + 7)) { - AddConstantToken(tokens, v == 6, 159); - } else { - AddConstantToken(tokens, v >= 9, 165); - AddConstantToken(tokens, !(v & 1), 145); - } - } else { - int mask; - const uint8_t* tab; - if (v < 3 + (8 << 1)) { // VP8Cat3 (3b) - AddToken(tokens, 0, base_id + 8); - AddToken(tokens, 0, base_id + 9); - v -= 3 + (8 << 0); - mask = 1 << 2; - tab = VP8Cat3; - } else if (v < 3 + (8 << 2)) { // VP8Cat4 (4b) - AddToken(tokens, 0, base_id + 8); - AddToken(tokens, 1, base_id + 9); - v -= 3 + (8 << 1); - mask = 1 << 3; - tab = VP8Cat4; - } else if (v < 3 + (8 << 3)) { // VP8Cat5 (5b) - AddToken(tokens, 1, base_id + 8); - AddToken(tokens, 0, base_id + 10); - v -= 3 + (8 << 2); - mask = 1 << 4; - tab = VP8Cat5; - } else { // VP8Cat6 (11b) - AddToken(tokens, 1, base_id + 8); - AddToken(tokens, 1, base_id + 10); - v -= 3 + (8 << 3); - mask = 1 << 10; - tab = VP8Cat6; - } - while (mask) { - AddConstantToken(tokens, !!(v & mask), *tab++); - mask >>= 1; - } - } - ctx = 2; - } - AddConstantToken(tokens, sign, 128); - base_id = TOKEN_ID(coeff_type, VP8EncBands[n], ctx, 0); - if (n == 16 || !AddToken(tokens, n <= last, base_id + 0)) { - return 1; // EOB - } - } - return 1; -} - -#undef TOKEN_ID - -//------------------------------------------------------------------------------ -// This function works, but isn't currently used. Saved for later. - -#if 0 - -static void Record(int bit, proba_t* const stats) { - proba_t p = *stats; - if (p >= 0xffff0000u) { // an overflow is inbound. - p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2. - } - // record bit count (lower 16 bits) and increment total count (upper 16 bits). - p += 0x00010000u + bit; - *stats = p; -} - -void VP8TokenToStats(const VP8TBuffer* const b, proba_t* const stats) { - const VP8Tokens* p = b->pages_; - while (p != NULL) { - const int N = (p->next_ == NULL) ? b->left_ : 0; - int n = MAX_NUM_TOKEN; - while (n-- > N) { - const uint16_t token = p->tokens_[n]; - if (!(token & FIXED_PROBA_BIT)) { - Record((token >> 15) & 1, stats + (token & 0x3fffu)); - } - } - p = p->next_; - } -} - -#endif // 0 - -//------------------------------------------------------------------------------ -// Final coding pass, with known probabilities - -int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw, - const uint8_t* const probas, int final_pass) { - const VP8Tokens* p = b->pages_; - (void)final_pass; - if (b->error_) return 0; - while (p != NULL) { - const VP8Tokens* const next = p->next_; - const int N = (next == NULL) ? b->left_ : 0; - int n = MAX_NUM_TOKEN; - while (n-- > N) { - const uint16_t token = p->tokens_[n]; - const int bit = (token >> 15) & 1; - if (token & FIXED_PROBA_BIT) { - VP8PutBit(bw, bit, token & 0xffu); // constant proba - } else { - VP8PutBit(bw, bit, probas[token & 0x3fffu]); - } - } - if (final_pass) free((void*)p); - p = next; - } - if (final_pass) b->pages_ = NULL; - return 1; -} - -// Size estimation -size_t VP8EstimateTokenSize(VP8TBuffer* const b, const uint8_t* const probas) { - size_t size = 0; - const VP8Tokens* p = b->pages_; - if (b->error_) return 0; - while (p != NULL) { - const VP8Tokens* const next = p->next_; - const int N = (next == NULL) ? b->left_ : 0; - int n = MAX_NUM_TOKEN; - while (n-- > N) { - const uint16_t token = p->tokens_[n]; - const int bit = token & (1 << 15); - if (token & FIXED_PROBA_BIT) { - size += VP8BitCost(bit, token & 0xffu); - } else { - size += VP8BitCost(bit, probas[token & 0x3fffu]); - } - } - p = next; - } - return size; -} - -//------------------------------------------------------------------------------ - -#else // DISABLE_TOKEN_BUFFER - -void VP8TBufferInit(VP8TBuffer* const b) { - (void)b; -} -void VP8TBufferClear(VP8TBuffer* const b) { - (void)b; -} - -#endif // !DISABLE_TOKEN_BUFFER - diff --git a/drivers/webp/enc/tree.c b/drivers/webp/enc/tree.c index e5d05e5221..8b25e5e488 100644 --- a/drivers/webp/enc/tree.c +++ b/drivers/webp/enc/tree.c @@ -1,24 +1,27 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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 of token probabilities, intra modes and segments. +// 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 } @@ -315,7 +318,7 @@ void VP8CodeIntraModes(VP8Encoder* const enc) { VP8EncIterator it; VP8IteratorInit(enc, &it); do { - const VP8MBInfo* const mb = it.mb_; + const VP8MBInfo* mb = it.mb_; const uint8_t* preds = it.preds_; if (enc->segment_hdr_.update_map_) { PutSegment(bw, mb->segment_, enc->proba_.segments_); @@ -340,7 +343,7 @@ void VP8CodeIntraModes(VP8Encoder* const enc) { } } PutUVMode(bw, mb->uv_mode_); - } while (VP8IteratorNext(&it)); + } while (VP8IteratorNext(&it, 0)); } //------------------------------------------------------------------------------ @@ -502,3 +505,6 @@ void VP8WriteProbas(VP8BitWriter* const bw, const VP8Proba* const probas) { } } +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/drivers/webp/enc/vp8enci.h b/drivers/webp/enc/vp8enci.h index 71adf6c38a..a77778c0d8 100644 --- a/drivers/webp/enc/vp8enci.h +++ b/drivers/webp/enc/vp8enci.h @@ -1,10 +1,8 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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. @@ -18,9 +16,8 @@ #include "../webp/encode.h" #include "../dsp/dsp.h" #include "../utils/bit_writer.h" -#include "../utils/thread.h" -#ifdef __cplusplus +#if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif @@ -29,9 +26,12 @@ extern "C" { // version numbers #define ENC_MAJ_VERSION 0 -#define ENC_MIN_VERSION 4 +#define ENC_MIN_VERSION 2 #define ENC_REV_VERSION 0 +// size of histogram used by CollectHistogram. +#define MAX_COEFF_THRESH 64 + // intra prediction modes enum { B_DC_PRED = 0, // 4x4 modes B_TM_PRED = 1, @@ -47,8 +47,7 @@ enum { B_DC_PRED = 0, // 4x4 modes // Luma16 or UV modes DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED, - H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED, - NUM_PRED_MODES = 4 + H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED }; enum { NUM_MB_SEGMENTS = 4, @@ -57,24 +56,16 @@ enum { NUM_MB_SEGMENTS = 4, NUM_BANDS = 8, NUM_CTX = 3, NUM_PROBAS = 11, - MAX_LF_LEVELS = 64, // Maximum loop filter level - MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost - MAX_LEVEL = 2047 // max level (note: max codable is 2047 + 67) + MAX_LF_LEVELS = 64, // Maximum loop filter level + MAX_VARIABLE_LEVEL = 67 // last (inclusive) level with variable cost }; -typedef enum { // Rate-distortion optimization levels - RD_OPT_NONE = 0, // no rd-opt - RD_OPT_BASIC = 1, // basic scoring (no trellis) - RD_OPT_TRELLIS = 2, // perform trellis-quant on the final decision only - RD_OPT_TRELLIS_ALL = 3 // trellis-quant for every scoring (much slower) -} VP8RDLevel; - // YUV-cache parameters. Cache is 16-pixels wide. // The original or reconstructed samples can be accessed using VP8Scan[] // The predicted blocks can be accessed using offsets to yuv_p_ and // the arrays VP8*ModeOffsets[]; // +----+ YUV Samples area. See VP8Scan[] for accessing the blocks. -// Y_OFF |YYYY| <- original samples ('yuv_in_') +// Y_OFF |YYYY| <- original samples (enc->yuv_in_) // |YYYY| // |YYYY| // |YYYY| @@ -169,17 +160,7 @@ typedef int64_t score_t; // type used for scores, rate, distortion static WEBP_INLINE int QUANTDIV(int n, int iQ, int B) { return (n * iQ + B) >> QFIX; } - -// size of histogram used by CollectHistogram. -#define MAX_COEFF_THRESH 31 -typedef struct VP8Histogram VP8Histogram; -struct VP8Histogram { - // TODO(skal): we only need to store the max_value and last_non_zero actually. - int distribution[MAX_COEFF_THRESH + 1]; -}; - -// Uncomment the following to remove token-buffer code: -// #define DISABLE_TOKEN_BUFFER +extern const uint8_t VP8Zigzag[16]; //------------------------------------------------------------------------------ // Headers @@ -248,19 +229,16 @@ typedef struct { int beta_; // filter-susceptibility, range [0,255]. int quant_; // final segment quantizer. int fstrength_; // final in-loop filtering strength - int max_edge_; // max edge delta (for filtering strength) - int min_disto_; // minimum distortion required to trigger filtering record // reactivities int lambda_i16_, lambda_i4_, lambda_uv_; int lambda_mode_, lambda_trellis_, tlambda_; int lambda_trellis_i16_, lambda_trellis_i4_, lambda_trellis_uv_; } VP8SegmentInfo; -// Handy transient struct to accumulate score and info during RD-optimization +// Handy transcient struct to accumulate score and info during RD-optimization // and mode evaluation. typedef struct { - score_t D, SD; // Distortion, spectral distortion - score_t H, R, score; // header bits, rate, score. + 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]; @@ -274,11 +252,12 @@ typedef struct { // 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_; // input samples - uint8_t* yuv_out_; // output samples - uint8_t* yuv_out2_; // secondary buffer swapped with yuv_out_. - uint8_t* yuv_p_; // scratch buffer for prediction + 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 @@ -294,43 +273,24 @@ typedef struct { uint64_t uv_bits_; // macroblock bit-cost for chroma LFStats* lf_stats_; // filter stats (borrowed from enc_) int do_trellis_; // if true, perform extra level optimisation - int count_down_; // number of mb still to be processed - int count_down0_; // starting counter value (for progress) + int done_; // true when scan is finished int percent0_; // saved initial progress percent - - uint8_t* y_left_; // left luma samples (addressable from index -1 to 15). - uint8_t* u_left_; // left u samples (addressable from index -1 to 7) - uint8_t* v_left_; // left v samples (addressable from index -1 to 7) - - uint8_t* y_top_; // top luma samples at position 'x_' - uint8_t* uv_top_; // top u/v samples at position 'x_', packed as 16 bytes - - // memory for storing y/u/v_left_ and yuv_in_/out_* - uint8_t yuv_left_mem_[17 + 16 + 16 + 8 + ALIGN_CST]; // memory for *_left_ - uint8_t yuv_mem_[3 * YUV_SIZE + PRED_SIZE + ALIGN_CST]; // memory for yuv_* } VP8EncIterator; // in iterator.c -// must be called first +// must be called first. void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it); -// restart a scan +// restart a scan. void VP8IteratorReset(VP8EncIterator* const it); -// reset iterator position to row 'y' -void VP8IteratorSetRow(VP8EncIterator* const it, int y); -// set count down (=number of iterations to go) -void VP8IteratorSetCountDown(VP8EncIterator* const it, int count_down); -// return true if iteration is finished -int VP8IteratorIsDone(const VP8EncIterator* const it); -// Import uncompressed samples from source. -// If tmp_32 is not NULL, import boundary samples too. -// tmp_32 is a 32-bytes scratch buffer that must be aligned in memory. -void VP8IteratorImport(VP8EncIterator* const it, uint8_t* tmp_32); +// import samples from source +void VP8IteratorImport(const VP8EncIterator* const it); // export decimated samples void VP8IteratorExport(const VP8EncIterator* const it); -// go to next macroblock. Returns false if not finished. -int VP8IteratorNext(VP8EncIterator* const it); -// save the yuv_out_ boundary values to top_/left_ arrays for next iterations. -void VP8IteratorSaveBoundary(VP8EncIterator* const it); +// 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); // Report progression based on macroblock rows. Return 0 for user-abort request. int VP8IteratorProgress(const VP8EncIterator* const it, int final_delta_percent); @@ -354,40 +314,44 @@ void VP8SetSegment(const VP8EncIterator* const it, int segment); //------------------------------------------------------------------------------ // Paginated token buffer -typedef struct VP8Tokens VP8Tokens; // struct details in token.c - -typedef struct { -#if !defined(DISABLE_TOKEN_BUFFER) - VP8Tokens* pages_; // first page - VP8Tokens** last_page_; // last page - uint16_t* tokens_; // set to (*last_page_)->tokens_ - int left_; // how many free tokens left before the page is full. -#endif - int error_; // true in case of malloc error -} VP8TBuffer; - -void VP8TBufferInit(VP8TBuffer* const b); // initialize an empty buffer -void VP8TBufferClear(VP8TBuffer* const b); // de-allocate pages memory +// WIP: #define USE_TOKEN_BUFFER -#if !defined(DISABLE_TOKEN_BUFFER) +#ifdef USE_TOKEN_BUFFER -// Finalizes bitstream when probabilities are known. -// Deletes the allocated token memory if final_pass is true. -int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw, - const uint8_t* const probas, int final_pass); +#define MAX_NUM_TOKEN 2048 -// record the coding of coefficients without knowing the probabilities yet -int VP8RecordCoeffTokens(int ctx, int coeff_type, int first, int last, - const int16_t* const coeffs, - VP8TBuffer* const tokens); +typedef struct VP8Tokens VP8Tokens; +struct VP8Tokens { + uint16_t tokens_[MAX_NUM_TOKEN]; // bit#15: bit, bits 0..14: slot + int left_; + VP8Tokens* next_; +}; -// Estimate the final coded size given a set of 'probas'. -size_t VP8EstimateTokenSize(VP8TBuffer* const b, const uint8_t* const probas); +typedef struct { + VP8Tokens* rows_; + uint16_t* tokens_; // set to (*last_)->tokens_ + VP8Tokens** last_; + int left_; + int error_; // true in case of malloc error +} VP8TBuffer; -// unused for now -void VP8TokenToStats(const VP8TBuffer* const b, proba_t* const stats); +void VP8TBufferInit(VP8TBuffer* const b); // initialize an empty buffer +int VP8TBufferNewPage(VP8TBuffer* const b); // allocate a new page +void VP8TBufferClear(VP8TBuffer* const b); // de-allocate memory + +int VP8EmitTokens(const VP8TBuffer* const b, VP8BitWriter* const bw, + const uint8_t* const probas); + +static WEBP_INLINE int VP8AddToken(VP8TBuffer* const b, + int bit, int proba_idx) { + if (b->left_ > 0 || VP8TBufferNewPage(b)) { + const int slot = --b->left_; + b->tokens_[slot] = (bit << 15) | proba_idx; + } + return bit; +} -#endif // !DISABLE_TOKEN_BUFFER +#endif // USE_TOKEN_BUFFER //------------------------------------------------------------------------------ // VP8Encoder @@ -412,7 +376,6 @@ struct VP8Encoder { // per-partition boolean decoders. VP8BitWriter bw_; // part0 VP8BitWriter parts_[MAX_NUM_PARTITIONS]; // token partitions - VP8TBuffer tokens_; // token buffer int percent_; // for progress @@ -420,7 +383,6 @@ struct VP8Encoder { int has_alpha_; uint8_t* alpha_data_; // non-NULL if transparency is present uint32_t alpha_data_size_; - WebPWorker alpha_worker_; // enhancement layer int use_layer_; @@ -432,7 +394,6 @@ struct VP8Encoder { VP8SegmentInfo dqm_[NUM_MB_SEGMENTS]; int base_quant_; // nominal quantizer value. Only used // for relative coding of segments' quant. - int alpha_; // global susceptibility (<=> complexity) int uv_alpha_; // U/V quantization susceptibility // global offset of quantizers, shared by all segments int dq_y1_dc_; @@ -448,20 +409,25 @@ struct VP8Encoder { int block_count_[3]; // quality/speed settings - int method_; // 0=fastest, 6=best/slowest. - VP8RDLevel rd_opt_level_; // Deduced from method_. - int max_i4_header_bits_; // partition #0 safeness factor - int thread_level_; // derived from config->thread_level - int do_search_; // derived from config->target_XXX - int use_tokens_; // if true, use token buffer + int method_; // 0=fastest, 6=best/slowest. + int rd_opt_level_; // Deduced from method_. + int max_i4_header_bits_; // partition #0 safeness factor // 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 bytes (8 U + 8 V) + // 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) }; @@ -489,11 +455,6 @@ void VP8EncFreeBitWriters(VP8Encoder* const enc); // in frame.c extern const uint8_t VP8EncBands[16 + 1]; -extern const uint8_t VP8Cat3[]; -extern const uint8_t VP8Cat4[]; -extern const uint8_t VP8Cat5[]; -extern const uint8_t VP8Cat6[]; - // Form all the four Intra16x16 predictions in the yuv_p_ cache void VP8MakeLuma16Preds(const VP8EncIterator* const it); // Form all the four Chroma8x8 predictions in the yuv_p_ cache @@ -505,9 +466,9 @@ void VP8MakeIntra4Preds(const VP8EncIterator* const it); int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd); int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]); int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd); -// Main coding calls +// Main stat / coding passes int VP8EncLoop(VP8Encoder* const enc); -int VP8EncTokenLoop(VP8Encoder* const enc); +int VP8StatLoop(VP8Encoder* const enc); // in webpenc.c // Assign an error code to a picture. Return false for convenience. @@ -524,14 +485,12 @@ int VP8EncAnalyze(VP8Encoder* const enc); // Sets up segment's quantization values, base_quant_ and filter strengths. void VP8SetSegmentParams(VP8Encoder* const enc, float quality); // Pick best modes and fills the levels. Returns true if skipped. -int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, - VP8RDLevel rd_opt); +int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, int rd_opt); // in alpha.c void VP8EncInitAlpha(VP8Encoder* const enc); // initialize alpha compression -int VP8EncStartAlpha(VP8Encoder* const enc); // start alpha coding process int VP8EncFinishAlpha(VP8Encoder* const enc); // finalize compressed data -int VP8EncDeleteAlpha(VP8Encoder* const enc); // delete compressed data +void VP8EncDeleteAlpha(VP8Encoder* const enc); // delete compressed data // in layer.c void VP8EncInitLayer(VP8Encoder* const enc); // init everything @@ -557,13 +516,9 @@ void VP8InitFilter(VP8EncIterator* const it); void VP8StoreFilterStats(VP8EncIterator* const it); void VP8AdjustFilterStrength(VP8EncIterator* const it); -// returns the approximate filtering strength needed to smooth a edge -// step of 'delta', given a sharpness parameter 'sharpness'. -int VP8FilterStrengthFromDelta(int sharpness, int delta); - //------------------------------------------------------------------------------ -#ifdef __cplusplus +#if defined(__cplusplus) || defined(c_plusplus) } // extern "C" #endif diff --git a/drivers/webp/enc/vp8l.c b/drivers/webp/enc/vp8l.c index 15726318e2..9c202f8d36 100644 --- a/drivers/webp/enc/vp8l.c +++ b/drivers/webp/enc/vp8l.c @@ -1,10 +1,8 @@ // Copyright 2012 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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/ // ----------------------------------------------------------------------------- // // main entry for the lossless encoder. @@ -25,6 +23,10 @@ #include "../utils/utils.h" #include "../webp/format_constants.h" +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + #define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer. #define MAX_HUFF_IMAGE_SIZE (16 * 1024 * 1024) #define MAX_COLORS_FOR_GRAPH 64 @@ -35,8 +37,7 @@ static int CompareColors(const void* p1, const void* p2) { const uint32_t a = *(const uint32_t*)p1; const uint32_t b = *(const uint32_t*)p2; - assert(a != b); - return (a < b) ? -1 : 1; + return (a < b) ? -1 : (a > b) ? 1 : 0; } // If number of colors in the image is less than or equal to MAX_PALETTE_SIZE, @@ -84,7 +85,7 @@ static int AnalyzeAndCreatePalette(const WebPPicture* const pic, argb += pic->argb_stride; } - // TODO(skal): could we reuse in_use[] to speed up EncodePalette()? + // TODO(skal): could we reuse in_use[] to speed up ApplyPalette()? num_colors = 0; for (i = 0; i < (int)(sizeof(in_use) / sizeof(in_use[0])); ++i) { if (in_use[i]) { @@ -164,6 +165,9 @@ static int VP8LEncAnalyze(VP8LEncoder* const enc, WebPImageHint image_hint) { } if (pred_entropy < 0.95 * non_pred_entropy) { enc->use_predict_ = 1; + // TODO(vikasa): Observed some correlation of cross_color transform with + // predict. Need to investigate this further and add separate heuristic + // for setting use_cross_color flag. enc->use_cross_color_ = 1; } } @@ -216,7 +220,7 @@ static int GetHuffBitLengthsAndCodes( } // Create Huffman trees. - for (i = 0; ok && (i < histogram_image_size); ++i) { + for (i = 0; i < histogram_image_size; ++i) { HuffmanTreeCode* const codes = &huffman_codes[5 * i]; VP8LHistogram* const histo = histogram_image->histograms[i]; ok = ok && VP8LCreateHuffmanTree(histo->literal_, 15, codes + 0); @@ -227,11 +231,7 @@ static int GetHuffBitLengthsAndCodes( } End: - if (!ok) { - free(mem_buf); - // If one VP8LCreateHuffmanTree() above fails, we need to clean up behind. - memset(huffman_codes, 0, 5 * histogram_image_size * sizeof(*huffman_codes)); - } + if (!ok) free(mem_buf); return ok; } @@ -406,10 +406,9 @@ static int StoreHuffmanCode(VP8LBitWriter* const bw, } static void WriteHuffmanCode(VP8LBitWriter* const bw, - const HuffmanTreeCode* const code, - int code_index) { - const int depth = code->code_lengths[code_index]; - const int symbol = code->codes[code_index]; + const HuffmanTreeCode* const code, int index) { + const int depth = code->code_lengths[index]; + const int symbol = code->codes[index]; VP8LWriteBits(bw, depth, symbol); } @@ -444,12 +443,12 @@ static void StoreImageToBitMask( int bits, n_bits; int code, distance; - VP8LPrefixEncode(v->len, &code, &n_bits, &bits); + PrefixEncode(v->len, &code, &n_bits, &bits); WriteHuffmanCode(bw, codes, 256 + code); VP8LWriteBits(bw, n_bits, bits); distance = PixOrCopyDistance(v); - VP8LPrefixEncode(distance, &code, &n_bits, &bits); + PrefixEncode(distance, &code, &n_bits, &bits); WriteHuffmanCode(bw, codes + 4, code); VP8LWriteBits(bw, n_bits, bits); } @@ -530,12 +529,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, sizeof(*histogram_symbols)); assert(histogram_bits >= MIN_HUFFMAN_BITS); assert(histogram_bits <= MAX_HUFFMAN_BITS); - - if (histogram_image == NULL || histogram_symbols == NULL) { - free(histogram_image); - free(histogram_symbols); - return 0; - } + if (histogram_image == NULL || histogram_symbols == NULL) goto Error; // Calculate backward references from ARGB image. if (!VP8LGetBackwardReferences(width, height, argb, quality, cache_bits, @@ -558,9 +552,6 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, !GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) { goto Error; } - // Free combined histograms. - free(histogram_image); - histogram_image = NULL; // Color Cache parameters. VP8LWriteBits(bw, 1, use_color_cache); @@ -580,10 +571,10 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, uint32_t i; if (histogram_argb == NULL) goto Error; for (i = 0; i < histogram_image_xysize; ++i) { - const int symbol_index = histogram_symbols[i] & 0xffff; - histogram_argb[i] = 0xff000000 | (symbol_index << 8); - if (symbol_index >= max_index) { - max_index = symbol_index + 1; + const int index = histogram_symbols[i] & 0xffff; + histogram_argb[i] = 0xff000000 | (index << 8); + if (index >= max_index) { + max_index = index + 1; } } histogram_image_size = max_index; @@ -607,6 +598,9 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, ClearHuffmanTreeIfOnlyOneSymbol(codes); } } + // Free combined histograms. + free(histogram_image); + histogram_image = NULL; // Store actual literals. StoreImageToBitMask(bw, width, histogram_bits, &refs, @@ -614,7 +608,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw, ok = 1; Error: - free(histogram_image); + if (!ok) free(histogram_image); VP8LClearBackwardRefs(&refs); if (huffman_codes != NULL) { @@ -695,7 +689,7 @@ static int ApplyCrossColorFilter(const VP8LEncoder* const enc, const int ccolor_transform_bits = enc->transform_bits_; const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits); const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits); - const int step = (quality < 25) ? 32 : (quality > 50) ? 8 : 16; + const int step = (quality == 0) ? 32 : 8; VP8LColorSpaceTransform(width, height, ccolor_transform_bits, step, enc->argb_, enc->transform_data_); @@ -712,6 +706,13 @@ static int ApplyCrossColorFilter(const VP8LEncoder* const enc, // ----------------------------------------------------------------------------- +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 WebPEncodingError WriteRiffHeader(const WebPPicture* const pic, size_t riff_size, size_t vp8l_size) { uint8_t riff[RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8L_SIGNATURE_SIZE] = { @@ -806,94 +807,61 @@ static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, return err; } -static void ApplyPalette(uint32_t* src, uint32_t* dst, - uint32_t src_stride, uint32_t dst_stride, - const uint32_t* palette, int palette_size, - int width, int height, int xbits, uint8_t* row) { - int i, x, y; - int use_LUT = 1; - for (i = 0; i < palette_size; ++i) { - if ((palette[i] & 0xffff00ffu) != 0) { - use_LUT = 0; - break; - } - } +// Bundles multiple (2, 4 or 8) pixels into a single pixel. +// Returns the new xsize. +static void BundleColorMap(const WebPPicture* const pic, + int xbits, uint32_t* bundled_argb, int xs) { + int y; + const int bit_depth = 1 << (3 - xbits); + uint32_t code = 0; + const uint32_t* argb = pic->argb; + const int width = pic->width; + const int height = pic->height; - if (use_LUT) { - uint8_t inv_palette[MAX_PALETTE_SIZE] = { 0 }; - for (i = 0; i < palette_size; ++i) { - const int color = (palette[i] >> 8) & 0xff; - inv_palette[color] = i; - } - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - const int color = (src[x] >> 8) & 0xff; - row[x] = inv_palette[color]; - } - VP8LBundleColorMap(row, width, xbits, dst); - src += src_stride; - dst += dst_stride; - } - } else { - // Use 1 pixel cache for ARGB pixels. - uint32_t last_pix = palette[0]; - int last_idx = 0; - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - const uint32_t pix = src[x]; - if (pix != last_pix) { - for (i = 0; i < palette_size; ++i) { - if (pix == palette[i]) { - last_idx = i; - last_pix = pix; - break; - } - } - } - row[x] = last_idx; + for (y = 0; y < height; ++y) { + int x; + for (x = 0; x < width; ++x) { + const int mask = (1 << xbits) - 1; + const int xsub = x & mask; + if (xsub == 0) { + code = 0; } - VP8LBundleColorMap(row, width, xbits, dst); - src += src_stride; - dst += dst_stride; + // TODO(vikasa): simplify the bundling logic. + code |= (argb[x] & 0xff00) << (bit_depth * xsub); + bundled_argb[y * xs + (x >> xbits)] = 0xff000000 | code; } + argb += pic->argb_stride; } } // Note: Expects "enc->palette_" to be set properly. // Also, "enc->palette_" will be modified after this call and should not be used // later. -static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, - VP8LEncoder* const enc, int quality) { +static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, + VP8LEncoder* const enc, int quality) { WebPEncodingError err = VP8_ENC_OK; - int i; + int i, x, y; const WebPPicture* const pic = enc->pic_; - uint32_t* src = pic->argb; - uint32_t* dst; + uint32_t* argb = pic->argb; const int width = pic->width; const int height = pic->height; uint32_t* const palette = enc->palette_; const int palette_size = enc->palette_size_; - uint8_t* row = NULL; - int xbits; // Replace each input pixel by corresponding palette index. - // This is done line by line. - if (palette_size <= 4) { - xbits = (palette_size <= 2) ? 3 : 2; - } else { - xbits = (palette_size <= 16) ? 1 : 0; + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + const uint32_t pix = argb[x]; + for (i = 0; i < palette_size; ++i) { + if (pix == palette[i]) { + argb[x] = 0xff000000u | (i << 8); + break; + } + } + } + argb += pic->argb_stride; } - err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height); - if (err != VP8_ENC_OK) goto Error; - dst = enc->argb_; - - row = (uint8_t*)WebPSafeMalloc((uint64_t)width, sizeof(*row)); - if (row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; - - ApplyPalette(src, dst, pic->argb_stride, enc->current_width_, - palette, palette_size, width, height, xbits, row); - // Save palette to bitstream. VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); VP8LWriteBits(bw, 2, COLOR_INDEXING_TRANSFORM); @@ -907,21 +875,36 @@ static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, goto Error; } + if (palette_size <= 16) { + // Image can be packed (multiple pixels per uint32_t). + int xbits = 1; + if (palette_size <= 2) { + xbits = 3; + } else if (palette_size <= 4) { + xbits = 2; + } + err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height); + if (err != VP8_ENC_OK) goto Error; + BundleColorMap(pic, xbits, enc->argb_, enc->current_width_); + } + Error: - free(row); return err; } // ----------------------------------------------------------------------------- -static int GetHistoBits(int method, int use_palette, int width, int height) { - const uint64_t hist_size = sizeof(VP8LHistogram); +static int GetHistoBits(const WebPConfig* const config, + const WebPPicture* const pic) { + const int width = pic->width; + const int height = pic->height; + const size_t hist_size = sizeof(VP8LHistogram); // Make tile size a function of encoding method (Range: 0 to 6). - int histo_bits = (use_palette ? 9 : 7) - method; + int histo_bits = 7 - config->method; while (1) { - const uint64_t huff_image_size = VP8LSubSampleSize(width, histo_bits) * - VP8LSubSampleSize(height, histo_bits) * - hist_size; + const size_t huff_image_size = VP8LSubSampleSize(width, histo_bits) * + VP8LSubSampleSize(height, histo_bits) * + hist_size; if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break; ++histo_bits; } @@ -929,14 +912,13 @@ static int GetHistoBits(int method, int use_palette, int width, int height) { (histo_bits > MAX_HUFFMAN_BITS) ? MAX_HUFFMAN_BITS : histo_bits; } -static void FinishEncParams(VP8LEncoder* const enc) { +static void InitEncParams(VP8LEncoder* const enc) { const WebPConfig* const config = enc->config_; - const WebPPicture* const pic = enc->pic_; + const WebPPicture* const picture = enc->pic_; const int method = config->method; const float quality = config->quality; - const int use_palette = enc->use_palette_; enc->transform_bits_ = (method < 4) ? 5 : (method > 4) ? 3 : 4; - enc->histo_bits_ = GetHistoBits(method, use_palette, pic->width, pic->height); + enc->histo_bits_ = GetHistoBits(config, picture); enc->cache_bits_ = (quality <= 25.f) ? 0 : 7; } @@ -952,9 +934,6 @@ static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config, } enc->config_ = config; enc->pic_ = picture; - - VP8LDspInit(); - return enc; } @@ -981,6 +960,8 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, goto Error; } + InitEncParams(enc); + // --------------------------------------------------------------------------- // Analyze image (entropy, num_palettes etc) @@ -989,10 +970,8 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, goto Error; } - FinishEncParams(enc); - if (enc->use_palette_) { - err = EncodePalette(bw, enc, quality); + err = ApplyPalette(bw, enc, quality); if (err != VP8_ENC_OK) goto Error; // Color cache is disabled for palette. enc->cache_bits_ = 0; @@ -1166,3 +1145,6 @@ int VP8LEncodeImage(const WebPConfig* const config, //------------------------------------------------------------------------------ +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/drivers/webp/enc/vp8li.h b/drivers/webp/enc/vp8li.h index 96d6faed64..eae90dd61f 100644 --- a/drivers/webp/enc/vp8li.h +++ b/drivers/webp/enc/vp8li.h @@ -1,10 +1,8 @@ // Copyright 2012 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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/ // ----------------------------------------------------------------------------- // // Lossless encoder: internal header. @@ -19,7 +17,7 @@ #include "../webp/encode.h" #include "../webp/format_constants.h" -#ifdef __cplusplus +#if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif @@ -63,7 +61,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, //------------------------------------------------------------------------------ -#ifdef __cplusplus +#if defined(__cplusplus) || defined(c_plusplus) } // extern "C" #endif diff --git a/drivers/webp/enc/webpenc.c b/drivers/webp/enc/webpenc.c index 207cce6beb..3c275589fc 100644 --- a/drivers/webp/enc/webpenc.c +++ b/drivers/webp/enc/webpenc.c @@ -1,10 +1,8 @@ // Copyright 2011 Google Inc. All Rights Reserved. // -// Use of this source code is governed by a BSD-style license -// that can be found in the COPYING file in the root of the source -// tree. An additional intellectual property rights grant can be found -// in the file PATENTS. All contributing project authors may -// be found in the AUTHORS file in the root of the source tree. +// 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 @@ -22,6 +20,10 @@ // #define PRINT_MEMORY_INFO +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + #ifdef PRINT_MEMORY_INFO #include <stdio.h> #endif @@ -91,53 +93,34 @@ static void ResetBoundaryPredictions(VP8Encoder* const enc) { enc->nz_[-1] = 0; // constant } -// Mapping from config->method_ to coding tools used. -//-------------------+---+---+---+---+---+---+---+ -// Method | 0 | 1 | 2 | 3 |(4)| 5 | 6 | -//-------------------+---+---+---+---+---+---+---+ -// fast probe | x | | | x | | | | -//-------------------+---+---+---+---+---+---+---+ -// dynamic proba | ~ | x | x | x | x | x | x | -//-------------------+---+---+---+---+---+---+---+ -// fast mode analysis| | | | | x | x | x | -//-------------------+---+---+---+---+---+---+---+ -// basic rd-opt | | | | x | x | x | x | -//-------------------+---+---+---+---+---+---+---+ -// disto-score i4/16 | | | x | | | | | -//-------------------+---+---+---+---+---+---+---+ -// rd-opt i4/16 | | | ~ | x | x | x | x | -//-------------------+---+---+---+---+---+---+---+ -// token buffer (opt)| | | | x | x | x | x | -//-------------------+---+---+---+---+---+---+---+ -// Trellis | | | | | | x |Ful| -//-------------------+---+---+---+---+---+---+---+ -// full-SNS | | | | | x | x | x | -//-------------------+---+---+---+---+---+---+---+ +// 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 WebPConfig* const config = enc->config_; - const int method = config->method; - const int limit = 100 - config->partition_limit; + const int method = enc->config_->method; + const int limit = 100 - enc->config_->partition_limit; enc->method_ = method; - enc->rd_opt_level_ = (method >= 6) ? RD_OPT_TRELLIS_ALL - : (method >= 5) ? RD_OPT_TRELLIS - : (method >= 3) ? RD_OPT_BASIC - : RD_OPT_NONE; + enc->rd_opt_level_ = (method >= 6) ? 3 + : (method >= 5) ? 2 + : (method >= 3) ? 1 + : 0; enc->max_i4_header_bits_ = 256 * 16 * 16 * // upper bound: up to 16bit per 4x4 block (limit * limit) / (100 * 100); // ... modulated with a quadratic curve. - - enc->thread_level_ = config->thread_level; - - enc->do_search_ = (config->target_size > 0 || config->target_PSNR > 0); - if (!config->low_memory) { -#if !defined(DISABLE_TOKEN_BUFFER) - enc->use_tokens_ = (enc->rd_opt_level_ >= RD_OPT_BASIC); // need rd stats -#endif - if (enc->use_tokens_) { - enc->num_parts_ = 1; // doesn't work with multi-partition - } - } } // Memory scaling with dimensions: @@ -153,7 +136,7 @@ static void MapConfigToTools(VP8Encoder* const enc) { // non-zero: 196 // lf-stats: 2048 // total: 68635 -// Transient object sizes: +// Transcient object sizes: // VP8EncIterator: 352 // VP8ModeScore: 912 // VP8SegmentInfo: 532 @@ -171,16 +154,20 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, const int preds_h = 4 * mb_h + 1; const size_t preds_size = preds_w * preds_h * sizeof(uint8_t); const int top_stride = mb_w * 16; - const size_t nz_size = (mb_w + 1) * sizeof(uint32_t) + ALIGN_CST; + const size_t 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 * sizeof(uint8_t) // top-luma/u/v - + ALIGN_CST; // align all + 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) + ALIGN_CST : 0; VP8Encoder* enc; uint8_t* mem; const uint64_t size = (uint64_t)sizeof(VP8Encoder) // main struct + ALIGN_CST // cache alignment + + cache_size // working caches + info_size // modes info + preds_size // prediction modes + samples_size // top/left samples @@ -191,15 +178,16 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, 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, info_size, + sizeof(VP8Encoder) + ALIGN_CST, cache_size, info_size, preds_size, samples_size, nz_size, lf_stats_size, size); - printf("Transient object sizes:\n" + printf("Transcient object sizes:\n" " VP8EncIterator: %ld\n" " VP8ModeScore: %ld\n" " VP8SegmentInfo: %ld\n" @@ -224,11 +212,19 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, 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*)DO_ALIGN(mem); + enc->nz_ = 1 + (uint32_t*)mem; mem += nz_size; enc->lf_stats_ = lf_stats_size ? (LFStats*)DO_ALIGN(mem) : NULL; mem += lf_stats_size; @@ -238,7 +234,13 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, enc->y_top_ = (uint8_t*)mem; enc->uv_top_ = enc->y_top_ + top_stride; mem += 2 * top_stride; - assert(mem <= (uint8_t*)enc + size); + 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; @@ -257,27 +259,23 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, VP8EncInitLayer(enc); #endif - VP8TBufferInit(&enc->tokens_); return enc; } -static int DeleteVP8Encoder(VP8Encoder* enc) { - int ok = 1; +static void DeleteVP8Encoder(VP8Encoder* enc) { if (enc != NULL) { - ok = VP8EncDeleteAlpha(enc); + VP8EncDeleteAlpha(enc); #ifdef WEBP_EXPERIMENTAL_FEATURES VP8EncDeleteLayer(enc); #endif - VP8TBufferClear(&enc->tokens_); free(enc); } - return ok; } //------------------------------------------------------------------------------ static double GetPSNR(uint64_t err, uint64_t size) { - return (err > 0 && size > 0) ? 10. * log10(255. * 255. * size / err) : 99.; + return err ? 10. * log10(255. * 255. * size / err) : 99.; } static void FinalizePSNR(const VP8Encoder* const enc) { @@ -334,7 +332,7 @@ int WebPReportProgress(const WebPPicture* const pic, //------------------------------------------------------------------------------ int WebPEncode(const WebPConfig* config, WebPPicture* pic) { - int ok = 0; + int ok; if (pic == NULL) return 0; @@ -353,48 +351,32 @@ int WebPEncode(const WebPConfig* config, WebPPicture* pic) { if (!config->lossless) { VP8Encoder* enc = NULL; if (pic->y == NULL || pic->u == NULL || pic->v == NULL) { - // Make sure we have YUVA samples. - float dithering = 0.f; - if (config->preprocessing & 2) { - const float x = config->quality / 100.f; - const float x2 = x * x; - // slowly decreasing from max dithering at low quality (q->0) - // to 0.5 dithering amplitude at high quality (q->100) - dithering = 1.0f + (0.5f - 1.0f) * x2 * x2; - } - if (!WebPPictureARGBToYUVADithered(pic, WEBP_YUV420, dithering)) { - return 0; + if (pic->argb != NULL) { + if (!WebPPictureARGBToYUVA(pic, WEBP_YUV420)) return 0; + } else { + return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); } } enc = InitVP8Encoder(config, pic); if (enc == NULL) return 0; // pic->error is already set. // Note: each of the tasks below account for 20% in the progress report. - ok = VP8EncAnalyze(enc); - - // Analysis is done, proceed to actual coding. - ok = ok && VP8EncStartAlpha(enc); // possibly done in parallel - if (!enc->use_tokens_) { - ok = ok && VP8EncLoop(enc); - } else { - ok = ok && VP8EncTokenLoop(enc); - } - ok = ok && VP8EncFinishAlpha(enc); + ok = VP8EncAnalyze(enc) + && VP8StatLoop(enc) + && VP8EncLoop(enc) + && VP8EncFinishAlpha(enc) #ifdef WEBP_EXPERIMENTAL_FEATURES - ok = ok && VP8EncFinishLayer(enc); + && VP8EncFinishLayer(enc) #endif - - ok = ok && VP8EncWrite(enc); + && VP8EncWrite(enc); StoreStats(enc); if (!ok) { VP8EncFreeBitWriters(enc); } - ok &= DeleteVP8Encoder(enc); // must always be called, even if !ok + DeleteVP8Encoder(enc); } else { - // Make sure we have ARGB samples. - if (pic->argb == NULL && !WebPPictureYUVAToARGB(pic)) { - return 0; - } + if (pic->argb == NULL) + return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem. } @@ -402,3 +384,6 @@ int WebPEncode(const WebPConfig* config, WebPPicture* pic) { return ok; } +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif |