diff --git a/README b/README index 5613185a..171e0005 100644 --- a/README +++ b/README @@ -140,6 +140,8 @@ options: -f ............... filter strength (0=off..100) -sharpness ....... filter sharpness (0:most .. 7:least sharp) -strong ................ use strong filter instead of simple. + -partition_limit . limit quality to fit the 512k limit on + the first partition (0=no degradation ... 100=full) -alpha_comp ...... set the transparency-compression -noalpha ............... discard any transparency information. -pass ............ analysis pass number (1..10) diff --git a/examples/cwebp.c b/examples/cwebp.c index 0665cc04..259ef377 100644 --- a/examples/cwebp.c +++ b/examples/cwebp.c @@ -676,6 +676,9 @@ static void HelpLong(void) { printf(" -sharpness ....... " "filter sharpness (0:most .. 7:least sharp)\n"); printf(" -strong ................ use strong filter instead of simple.\n"); + printf(" -partition_limit . limit quality to fit the 512k limit on\n"); + printf(" " + "the first partition (0=no degradation ... 100=full)\n"); printf(" -alpha_comp ...... set the transparency-compression\n"); printf(" -noalpha ............... discard any transparency information.\n"); printf(" -pass ............ analysis pass number (1..10)\n"); @@ -712,10 +715,15 @@ static const char* const kErrorMessages[] = { "BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer", "NULL_PARAMETER: NULL parameter passed to function", "INVALID_CONFIGURATION: configuration is invalid", - "BAD_DIMENSION: Bad picture dimension", - "PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k", - "PARTITION_OVERFLOW: Partition is too big to fir 16M", - "BAD_WRITE: Picture writer returned an error" + "BAD_DIMENSION: Bad picture dimension. Maximum width and height " + "allowed is 16383 pixels.", + "PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k.\n" + "To reduce the size of this partition, try using less segments " + "with the -segments option, and eventually reduce the number of " + "header bits using -partition_limit. More details are available " + "in the manual (`man cwebp`)", + "PARTITION_OVERFLOW: Partition is too big to fit 16M", + "BAD_WRITE: Picture writer returned an I/O error" }; //----------------------------------------------------------------------------- @@ -789,6 +797,8 @@ int main(int argc, const char *argv[]) { config.preprocessing = strtol(argv[++c], NULL, 0); } else if (!strcmp(argv[c], "-segments") && c < argc - 1) { config.segments = strtol(argv[++c], NULL, 0); + } else if (!strcmp(argv[c], "-partition_limit") && c < argc - 1) { + config.partition_limit = strtol(argv[++c], NULL, 0); } else if (!strcmp(argv[c], "-alpha_comp") && c < argc - 1) { config.alpha_compression = strtol(argv[++c], NULL, 0); } else if (!strcmp(argv[c], "-noalpha")) { diff --git a/man/cwebp.1 b/man/cwebp.1 index b7f02b65..3ab648f6 100644 --- a/man/cwebp.1 +++ b/man/cwebp.1 @@ -1,5 +1,5 @@ .\" Hey, EMACS: -*- nroff -*- -.TH CWEBP 1 "June 20, 2011" +.TH CWEBP 1 "August 23, 2011" .SH NAME cwebp \- compress an image file to a WebP file .SH SYNOPSIS @@ -86,6 +86,25 @@ used thanks to the \fB\-f\fP option). Strong filtering is off by default. Change the number of partitions to use during the segmentation of the sns algorithm. Segments should be in range 1 to 4. Default value is 4. .TP +.B \-partition_limit int +Degrade quality by limiting the number of bits used by some macroblocks. +Range is 0 (no degradation, the default) to 100 (full degradation). +Useful values are usually around 30-70 for moderately large images. +In the VP8 format, the so-called control partition has a limit of 512k and +is used to store the following information: whether the macroblock is skipped, +which segment it belongs to, whether it is coded as intra 4x4 or intra 16x16 +mode, and finally the prediction modes to use for each of the sub-blocks. +For a very large image, 512k only leaves room to few bits per 16x16 macroblock. +The absolute minimum is 4 bits per macroblock. Skip, segment, and mode +information can use up almost all these 4 bits (although the case is unlikely), +which is problematic for very large images. The partition_limit factor controls +how frequently the most bit-costly mode (intra 4x4) will be used. This is +useful in case the 512k limit is reached and the following message is displayed: +\fIError code: 6 (PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k)\fP. +If using \fB-partition_limit\fP is not enough to meet the 512k constraint, one +should use less segments in order to save more header bits per macroblock. +See the \fB-segments\fP option. +.TP .B \-size int Specify a target size (in bytes) to try and reach for the compressed output. Compressor will make several pass of partial encoding in order to get as diff --git a/src/enc/config.c b/src/enc/config.c index fc7c75dc..5749a88b 100644 --- a/src/enc/config.c +++ b/src/enc/config.c @@ -42,6 +42,7 @@ int WebPConfigInitInternal(WebPConfig* const config, config->preprocessing = 0; config->autofilter = 0; config->alpha_compression = 0; + config->partition_limit = 0; // TODO(skal): tune. switch (preset) { @@ -106,6 +107,8 @@ int WebPValidateConfig(const WebPConfig* const config) { return 0; if (config->partitions < 0 || config->partitions > 3) return 0; + if (config->partition_limit < 0 || config->partition_limit > 100) + return 0; if (config->alpha_compression < 0) return 0; return 1; diff --git a/src/enc/quant.c b/src/enc/quant.c index b0dd463d..ba0f9532 100644 --- a/src/enc/quant.c +++ b/src/enc/quant.c @@ -757,8 +757,13 @@ static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) { const int tlambda = dqm->tlambda_; const uint8_t* const src0 = it->yuv_in_ + Y_OFF; uint8_t* const best_blocks = it->yuv_out2_ + Y_OFF; + int total_header_bits = 0; VP8ModeScore rd_best; + if (enc->max_i4_header_bits_ == 0) { + return 0; + } + InitScore(&rd_best); rd_best.score = 211; // '211' is the value of VP8BitCost(0, 145) VP8IteratorStartI4(it); @@ -799,7 +804,9 @@ 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) { + 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. diff --git a/src/enc/vp8enci.h b/src/enc/vp8enci.h index dbab162b..d623432b 100644 --- a/src/enc/vp8enci.h +++ b/src/enc/vp8enci.h @@ -359,8 +359,9 @@ struct VP8Encoder { int block_count_[3]; // quality/speed settings - int method_; // 0=fastest, 6=best/slowest. - int rd_opt_level_; // Deduced from method_. + 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) diff --git a/src/enc/webpenc.c b/src/enc/webpenc.c index 819dd639..f2ec1c38 100644 --- a/src/enc/webpenc.c +++ b/src/enc/webpenc.c @@ -112,11 +112,15 @@ static void ResetBoundaryPredictions(VP8Encoder* const enc) { static void MapConfigToTools(VP8Encoder* const enc) { const int method = enc->config_->method; + const int limit = 100 - enc->config_->partition_limit; enc->method_ = method; 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. } // Memory scaling with dimensions: diff --git a/src/webp/encode.h b/src/webp/encode.h index af6f0a2c..c5f1917b 100644 --- a/src/webp/encode.h +++ b/src/webp/encode.h @@ -69,6 +69,8 @@ typedef struct { int preprocessing; // preprocessing filter (0=none, 1=segment-smooth) int partitions; // log2(number of token partitions) in [0..3] // Default is set to 0 for easier progressive decoding. + int partition_limit; // quality degradation allowed to fit the 512k limit on + // prediction modes coding (0=no degradation, 100=full) int alpha_compression; // Algorithm for optimizing the alpha plane (0 = none) } WebPConfig;