| Index: third_party/libwebp/enc/alpha.c
|
| diff --git a/third_party/libwebp/enc/alpha.c b/third_party/libwebp/enc/alpha.c
|
| index e636c967237da33af76d9044f1d3255c39397e76..21d4b5cbde387b4f19151ff084960958d3869426 100644
|
| --- a/third_party/libwebp/enc/alpha.c
|
| +++ b/third_party/libwebp/enc/alpha.c
|
| @@ -19,10 +19,6 @@
|
| #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.
|
| @@ -71,7 +67,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) | 0xff000000u;
|
| + dst[i] = src[i] << 8; // we leave A/R/B channels zero'd.
|
| }
|
| src += width;
|
| dst += picture.argb_stride;
|
| @@ -81,8 +77,10 @@ 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 moderate default quality setting for alpha.
|
| - config.quality = 10.f * effort_level;
|
| + // Set a low default quality for encoding alpha. Ensure that Alpha quality at
|
| + // lower methods (3 and below) is less than the threshold for triggering
|
| + // costly 'BackwardReferencesTraceBackwards'.
|
| + config.quality = 8.f * effort_level;
|
| assert(config.quality >= 0 && config.quality <= 100.f);
|
|
|
| ok = VP8LBitWriterInit(&tmp_bw, (width * height) >> 3);
|
| @@ -99,12 +97,19 @@ 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,
|
| - VP8BitWriter* const bw,
|
| - WebPAuxStats* const stats) {
|
| + FilterTrial* result) {
|
| int ok = 0;
|
| const uint8_t* alpha_src;
|
| WebPFilterFunc filter_func;
|
| @@ -125,8 +130,8 @@ 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(bw, expected_size);
|
| - VP8BitWriterAppend(bw, &header, ALPHA_HEADER_LEN);
|
| + VP8BitWriterInit(&result->bw, expected_size);
|
| + VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN);
|
|
|
| filter_func = WebPFilters[filter];
|
| if (filter_func != NULL) {
|
| @@ -137,12 +142,14 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
|
| }
|
|
|
| if (method == ALPHA_NO_COMPRESSION) {
|
| - ok = VP8BitWriterAppend(bw, alpha_src, width * height);
|
| - ok = ok && !bw->error_;
|
| + ok = VP8BitWriterAppend(&result->bw, alpha_src, width * height);
|
| + ok = ok && !result->bw.error_;
|
| } else {
|
| - ok = EncodeLossless(alpha_src, width, height, effort_level, bw, stats);
|
| - VP8BitWriterFinish(bw);
|
| + ok = EncodeLossless(alpha_src, width, height, effort_level,
|
| + &result->bw, &result->stats);
|
| + VP8BitWriterFinish(&result->bw);
|
| }
|
| + result->score = VP8BitWriterSize(&result->bw);
|
| return ok;
|
| }
|
|
|
| @@ -177,6 +184,85 @@ static int GetNumColors(const uint8_t* data, int width, int height,
|
| 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,
|
| @@ -207,6 +293,11 @@ 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;
|
| @@ -225,105 +316,19 @@ static int EncodeAlpha(VP8Encoder* const enc,
|
| }
|
|
|
| if (ok) {
|
| - VP8BitWriter bw;
|
| - int test_filter;
|
| - uint8_t* filtered_alpha = NULL;
|
| - int try_filter_none = (effort_level > 3);
|
| -
|
| - if (filter == WEBP_FILTER_FAST) { // Quick estimate of the best candidate.
|
| - const int kMinColorsForFilterNone = 16;
|
| - const int kMaxColorsForFilterNone = 192;
|
| - const int num_colors = GetNumColors(quant_alpha, width, height, width);
|
| - // For low number of colors, NONE yeilds better compression.
|
| - filter = (num_colors <= kMinColorsForFilterNone) ? WEBP_FILTER_NONE :
|
| - EstimateBestFilter(quant_alpha, width, height, width);
|
| - // For large number of colors, try FILTER_NONE in addition to the best
|
| - // filter as well.
|
| - if (num_colors > kMaxColorsForFilterNone) {
|
| - try_filter_none = 1;
|
| - }
|
| - }
|
| -
|
| - // Test for WEBP_FILTER_NONE for higher effort levels.
|
| - if (try_filter_none || filter == WEBP_FILTER_NONE) {
|
| - ok = EncodeAlphaInternal(quant_alpha, width, height,
|
| - method, WEBP_FILTER_NONE, reduce_levels,
|
| - effort_level, NULL, &bw, pic->stats);
|
| -
|
| - if (!ok) {
|
| - VP8BitWriterWipeOut(&bw);
|
| - goto End;
|
| - }
|
| - }
|
| - // Stop?
|
| - if (filter == WEBP_FILTER_NONE) {
|
| - goto Ok;
|
| - }
|
| -
|
| - filtered_alpha = (uint8_t*)malloc(data_size);
|
| - ok = (filtered_alpha != NULL);
|
| - if (!ok) {
|
| - goto End;
|
| + 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;
|
| }
|
| -
|
| - // Try the other mode(s).
|
| - {
|
| - WebPAuxStats best_stats;
|
| - size_t best_score = try_filter_none ?
|
| - VP8BitWriterSize(&bw) : (size_t)~0U;
|
| - int wipe_tmp_bw = try_filter_none;
|
| -
|
| - memset(&best_stats, 0, sizeof(best_stats)); // prevent spurious warning
|
| - if (pic->stats != NULL) best_stats = *pic->stats;
|
| - for (test_filter =
|
| - try_filter_none ? WEBP_FILTER_HORIZONTAL : WEBP_FILTER_NONE;
|
| - 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);
|
| - }
|
| - if (wipe_tmp_bw) {
|
| - VP8BitWriterWipeOut(&tmp_bw);
|
| - }
|
| - wipe_tmp_bw = 1; // For next filter trial for WEBP_FILTER_BEST.
|
| - }
|
| - 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
|
|
|
| @@ -403,6 +408,3 @@ int VP8EncDeleteAlpha(VP8Encoder* const enc) {
|
| return ok;
|
| }
|
|
|
| -#if defined(__cplusplus) || defined(c_plusplus)
|
| -} // extern "C"
|
| -#endif
|
|
|