From 1bf198a22bd11b15ce74085939757b3361404d89 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Tue, 7 May 2024 10:44:59 +0200 Subject: [PATCH] Allow transform_bits to be different during encoding. The spec allows it but it is currently forced to the same value for simplicity. Change-Id: I26197dbf3342f7a72115cc7f7805c154313a2afb --- examples/cwebp.c | 10 ++++++++-- src/enc/alpha_enc.c | 1 + src/enc/vp8l_enc.c | 34 ++++++++++++++++++++++------------ src/enc/vp8li_enc.h | 3 ++- src/webp/encode.h | 7 ++++--- 5 files changed, 37 insertions(+), 18 deletions(-) diff --git a/examples/cwebp.c b/examples/cwebp.c index 7d33f06e0..d47f0be04 100644 --- a/examples/cwebp.c +++ b/examples/cwebp.c @@ -178,8 +178,14 @@ static void PrintFullLosslessInfo(const WebPAuxStats* const stats, if (stats->lossless_features & 8) fprintf(stderr, " PALETTE"); fprintf(stderr, "\n"); } - fprintf(stderr, " * Precision Bits: histogram=%d transform=%d cache=%d\n", - stats->histogram_bits, stats->transform_bits, stats->cache_bits); + fprintf(stderr, " * Precision Bits: histogram=%d", stats->histogram_bits); + if (stats->lossless_features & 1) { + fprintf(stderr, " prediction=%d", stats->transform_bits); + } + if (stats->lossless_features & 2) { + fprintf(stderr, " cross-color=%d", stats->cross_color_transform_bits); + } + fprintf(stderr, " cache=%d\n", stats->cache_bits); if (stats->palette_size > 0) { fprintf(stderr, " * Palette size: %d\n", stats->palette_size); } diff --git a/src/enc/alpha_enc.c b/src/enc/alpha_enc.c index c11a261c8..035709440 100644 --- a/src/enc/alpha_enc.c +++ b/src/enc/alpha_enc.c @@ -276,6 +276,7 @@ static int ApplyFiltersAndEncode(const uint8_t* alpha, int width, int height, stats->lossless_features = best.stats.lossless_features; stats->histogram_bits = best.stats.histogram_bits; stats->transform_bits = best.stats.transform_bits; + stats->cross_color_transform_bits = best.stats.cross_color_transform_bits; stats->cache_bits = best.stats.cache_bits; stats->palette_size = best.stats.palette_size; stats->lossless_size = best.stats.lossless_size; diff --git a/src/enc/vp8l_enc.c b/src/enc/vp8l_enc.c index 1cac0ab23..c50c46d20 100644 --- a/src/enc/vp8l_enc.c +++ b/src/enc/vp8l_enc.c @@ -281,7 +281,7 @@ static int EncoderAnalyze(VP8LEncoder* const enc, const int method = config->method; const int low_effort = (config->method == 0); int i; - int use_palette; + int use_palette, transform_bits; int n_lz77s; // If set to 0, analyze the cache with the computed cache value. If 1, also // analyze with no-cache. @@ -298,7 +298,9 @@ static int EncoderAnalyze(VP8LEncoder* const enc, // Empirical bit sizes. enc->histo_bits_ = GetHistoBits(method, use_palette, pic->width, pic->height); - enc->transform_bits_ = GetTransformBits(method, enc->histo_bits_); + transform_bits = GetTransformBits(method, enc->histo_bits_); + enc->predictor_transform_bits_ = transform_bits; + enc->cross_color_transform_bits_ = transform_bits; if (low_effort) { // AnalyzeEntropy is somewhat slow. @@ -312,8 +314,8 @@ static int EncoderAnalyze(VP8LEncoder* const enc, // Try out multiple LZ77 on images with few colors. n_lz77s = (enc->palette_size_ > 0 && enc->palette_size_ <= 16) ? 2 : 1; if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride, use_palette, - enc->palette_size_, enc->transform_bits_, - &min_entropy_ix, red_and_blue_always_zero)) { + enc->palette_size_, transform_bits, &min_entropy_ix, + red_and_blue_always_zero)) { return 0; } if (method == 6 && config->quality == 100) { @@ -1069,7 +1071,7 @@ static int ApplyPredictFilter(VP8LEncoder* const enc, int width, int height, int quality, int low_effort, int used_subtract_green, VP8LBitWriter* const bw, int percent_range, int* const percent) { - const int pred_bits = enc->transform_bits_; + const int pred_bits = enc->predictor_transform_bits_; const int transform_width = VP8LSubSampleSize(width, pred_bits); const int transform_height = VP8LSubSampleSize(height, pred_bits); // we disable near-lossless quantization if palette is used. @@ -1093,11 +1095,11 @@ static int ApplyPredictFilter(VP8LEncoder* const enc, int width, int height, percent); } -static int ApplyCrossColorFilter(const VP8LEncoder* const enc, int width, - int height, int quality, int low_effort, +static int ApplyCrossColorFilter(VP8LEncoder* const enc, int width, int height, + int quality, int low_effort, VP8LBitWriter* const bw, int percent_range, int* const percent) { - const int ccolor_transform_bits = enc->transform_bits_; + const int ccolor_transform_bits = enc->cross_color_transform_bits_; const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits); const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits); @@ -1200,10 +1202,14 @@ static int AllocateTransformBuffer(VP8LEncoder* const enc, int width, enc->use_predict_ ? (width + 1) * 2 + (width * 2 + sizeof(uint32_t) - 1) / sizeof(uint32_t) : 0; + const int min_transform_bits = + (enc->predictor_transform_bits_ < enc->cross_color_transform_bits_) + ? enc->predictor_transform_bits_ + : enc->cross_color_transform_bits_; const uint64_t transform_data_size = (enc->use_predict_ || enc->use_cross_color_) - ? (uint64_t)VP8LSubSampleSize(width, enc->transform_bits_) * - VP8LSubSampleSize(height, enc->transform_bits_) + ? (uint64_t)VP8LSubSampleSize(width, min_transform_bits) * + VP8LSubSampleSize(height, min_transform_bits) : 0; const uint64_t max_alignment_in_words = (WEBP_ALIGN_CST + sizeof(uint32_t) - 1) / sizeof(uint32_t); @@ -1628,7 +1634,8 @@ static int EncodeStreamHook(void* input, void* data2) { if (enc->use_subtract_green_) stats->lossless_features |= 4; if (enc->use_palette_) stats->lossless_features |= 8; stats->histogram_bits = enc->histo_bits_; - stats->transform_bits = enc->transform_bits_; + stats->transform_bits = enc->predictor_transform_bits_; + stats->cross_color_transform_bits = enc->cross_color_transform_bits_; stats->cache_bits = enc->cache_bits_; stats->palette_size = enc->palette_size_; stats->lossless_size = (int)(best_size - byte_position); @@ -1738,7 +1745,10 @@ int VP8LEncodeStream(const WebPConfig* const config, } // Copy the values that were computed for the main encoder. enc_side->histo_bits_ = enc_main->histo_bits_; - enc_side->transform_bits_ = enc_main->transform_bits_; + enc_side->predictor_transform_bits_ = + enc_main->predictor_transform_bits_; + enc_side->cross_color_transform_bits_ = + enc_main->cross_color_transform_bits_; enc_side->palette_size_ = enc_main->palette_size_; memcpy(enc_side->palette_, enc_main->palette_, sizeof(enc_main->palette_)); diff --git a/src/enc/vp8li_enc.h b/src/enc/vp8li_enc.h index 59006cf20..f137dcf5f 100644 --- a/src/enc/vp8li_enc.h +++ b/src/enc/vp8li_enc.h @@ -59,7 +59,8 @@ typedef struct { // Encoding parameters derived from quality parameter. int histo_bits_; - int transform_bits_; // <= MAX_TRANSFORM_BITS. + int predictor_transform_bits_; // <= MAX_TRANSFORM_BITS + int cross_color_transform_bits_; // <= MAX_TRANSFORM_BITS int cache_bits_; // If equal to 0, don't use color cache. // Encoding parameters derived from image characteristics. diff --git a/src/webp/encode.h b/src/webp/encode.h index f3d59297c..4be0aec10 100644 --- a/src/webp/encode.h +++ b/src/webp/encode.h @@ -20,7 +20,7 @@ extern "C" { #endif -#define WEBP_ENCODER_ABI_VERSION 0x020f // MAJOR(8b) + MINOR(8b) +#define WEBP_ENCODER_ABI_VERSION 0x0210 // MAJOR(8b) + MINOR(8b) // Note: forward declaring enumerations is not allowed in (strict) C and C++, // the types are left here for reference. @@ -224,14 +224,15 @@ struct WebPAuxStats { uint32_t lossless_features; // bit0:predictor bit1:cross-color transform // bit2:subtract-green bit3:color indexing int histogram_bits; // number of precision bits of histogram - int transform_bits; // precision bits for transform + int transform_bits; // precision bits for predictor transform int cache_bits; // number of bits for color cache lookup int palette_size; // number of color in palette, if used int lossless_size; // final lossless size int lossless_hdr_size; // lossless header (transform, huffman etc) size int lossless_data_size; // lossless image data size + int cross_color_transform_bits; // precision bits for cross-color transform - uint32_t pad[2]; // padding for later use + uint32_t pad[1]; // padding for later use }; // Signature for output function. Should return true if writing was successful.