mirror of
https://github.com/webmproject/libwebp.git
synced 2025-07-14 21:09:55 +02:00
Improve near lossless compression when a prediction filter is used.
The old implementation in enc/near_lossless.c performing a separate preprocessing step is used only when a prediction filter is not used, otherwise a new implementation integrated into lossless_enc.c is used. It retains the same logic for converting near lossless quality into max number of bits dropped, and for adjusting the number of bits based on the smoothness of the image at a given pixel. As before, borders are not changed. Then, instead of quantizing raw component values, the residual after subtract green and after prediction is quantized according to the resulting number of bits, taking care to not cross the boundary between 255 and 0 after decoding. Ties are resolved by moving closer to the prediction instead of by bankers’ rounding. This results in about 15% size decrease for the same quality. Change-Id: If3e9c388158c2e3e75ef88876703f40b932f671f
This commit is contained in:
committed by
Pascal Massimino
parent
e15afbce5d
commit
f2e1efbeb7
@ -97,22 +97,11 @@ static void NearLossless(int xsize, int ysize, uint32_t* argb,
|
||||
}
|
||||
}
|
||||
|
||||
static int QualityToLimitBits(int quality) {
|
||||
// quality mapping:
|
||||
// 0..19 -> 5
|
||||
// 20..39 -> 4
|
||||
// 40..59 -> 3
|
||||
// 60..79 -> 2
|
||||
// 80..99 -> 1
|
||||
// 100 -> 0
|
||||
return MAX_LIMIT_BITS - quality / 20;
|
||||
}
|
||||
|
||||
int VP8ApplyNearLossless(int xsize, int ysize, uint32_t* argb, int quality) {
|
||||
int i;
|
||||
uint32_t* const copy_buffer =
|
||||
(uint32_t*)WebPSafeMalloc(xsize * 3, sizeof(*copy_buffer));
|
||||
const int limit_bits = QualityToLimitBits(quality);
|
||||
const int limit_bits = VP8LNearLosslessBits(quality);
|
||||
assert(argb != NULL);
|
||||
assert(limit_bits >= 0);
|
||||
assert(limit_bits <= MAX_LIMIT_BITS);
|
||||
|
@ -1008,6 +1008,7 @@ static void ApplySubtractGreen(VP8LEncoder* const enc, int width, int height,
|
||||
static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc,
|
||||
int width, int height,
|
||||
int quality, int low_effort,
|
||||
int used_subtract_green,
|
||||
VP8LBitWriter* const bw) {
|
||||
const int pred_bits = enc->transform_bits_;
|
||||
const int transform_width = VP8LSubSampleSize(width, pred_bits);
|
||||
@ -1015,7 +1016,8 @@ static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc,
|
||||
|
||||
VP8LResidualImage(width, height, pred_bits, low_effort, enc->argb_,
|
||||
enc->argb_scratch_, enc->transform_data_,
|
||||
enc->config_->exact);
|
||||
enc->config_->near_lossless, enc->config_->exact,
|
||||
used_subtract_green);
|
||||
VP8LPutBits(bw, TRANSFORM_PRESENT, 1);
|
||||
VP8LPutBits(bw, PREDICTOR_TRANSFORM, 2);
|
||||
assert(pred_bits >= 2);
|
||||
@ -1129,21 +1131,26 @@ static void ClearTransformBuffer(VP8LEncoder* const enc) {
|
||||
static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc,
|
||||
int width, int height) {
|
||||
WebPEncodingError err = VP8_ENC_OK;
|
||||
const int tile_size = 1 << enc->transform_bits_;
|
||||
const uint64_t image_size = width * height;
|
||||
// Ensure enough size for tiles, as well as for two scanlines and two
|
||||
// extra pixels for CopyImageWithPrediction.
|
||||
// VP8LResidualImage needs room for 2 scanlines of uint32 pixels with an extra
|
||||
// pixel in each, plus 2 regular scanlines of bytes.
|
||||
// TODO(skal): Clean up by using arithmetic in bytes instead of words.
|
||||
const uint64_t argb_scratch_size =
|
||||
enc->use_predict_ ? tile_size * width + width + 2 : 0;
|
||||
const int transform_data_size =
|
||||
enc->use_predict_
|
||||
? (width + 1) * 2 +
|
||||
(width * 2 + sizeof(uint32_t) - 1) / sizeof(uint32_t)
|
||||
: 0;
|
||||
const uint64_t transform_data_size =
|
||||
(enc->use_predict_ || enc->use_cross_color_)
|
||||
? VP8LSubSampleSize(width, enc->transform_bits_) *
|
||||
VP8LSubSampleSize(height, enc->transform_bits_)
|
||||
VP8LSubSampleSize(height, enc->transform_bits_)
|
||||
: 0;
|
||||
const uint64_t max_alignment_in_words =
|
||||
(WEBP_ALIGN_CST + sizeof(uint32_t) - 1) / sizeof(uint32_t);
|
||||
const uint64_t mem_size =
|
||||
image_size + WEBP_ALIGN_CST +
|
||||
argb_scratch_size + WEBP_ALIGN_CST +
|
||||
(uint64_t)transform_data_size;
|
||||
image_size + max_alignment_in_words +
|
||||
argb_scratch_size + max_alignment_in_words +
|
||||
transform_data_size;
|
||||
uint32_t* mem = enc->transform_mem_;
|
||||
if (mem == NULL || mem_size > enc->transform_mem_size_) {
|
||||
ClearTransformBuffer(enc);
|
||||
@ -1412,7 +1419,8 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
||||
}
|
||||
|
||||
// Apply near-lossless preprocessing.
|
||||
use_near_lossless = !enc->use_palette_ && (config->near_lossless < 100);
|
||||
use_near_lossless =
|
||||
(config->near_lossless < 100) && !enc->use_palette_ && !enc->use_predict_;
|
||||
if (use_near_lossless) {
|
||||
if (!VP8ApplyNearLossless(width, height, picture->argb,
|
||||
config->near_lossless)) {
|
||||
@ -1464,7 +1472,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
||||
|
||||
if (enc->use_predict_) {
|
||||
err = ApplyPredictFilter(enc, enc->current_width_, height, quality,
|
||||
low_effort, bw);
|
||||
low_effort, enc->use_subtract_green_, bw);
|
||||
if (err != VP8_ENC_OK) goto Error;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user