mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-28 14:38:21 +01:00
Updated VP8LGetBackwardReferences and color cache.
- The optimal cache bits is evaluated inside the method 'VP8LGetBackwardReferences'. - The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache bits to use (passing 0 implies disabling the local color cache). - The local color cache is disabled for lowerf (<= 25) quality levels (as before). - Enabled local color cache for palette images as well. This saves additional 0.017% bytes with a slight (2-3%) improvement in the compression speed. - Removed 'use_2d_locality' parameter from method VP8LGetBackwardReferences, as this option is not an option now (after we freeze the lossless bit-stream). Change-Id: I33430401e465474fa1be899f330387cd2b466280
This commit is contained in:
parent
aa42f4231f
commit
95a9bd85c4
@ -810,32 +810,48 @@ static void BackwardReferences2DLocality(int xsize,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int CalculateBestCacheSize(const uint32_t* const argb,
|
||||||
|
int xsize, int ysize, int quality,
|
||||||
|
VP8LHashChain* const hash_chain,
|
||||||
|
VP8LBackwardRefs* const refs,
|
||||||
|
int* const best_cache_bits);
|
||||||
|
|
||||||
VP8LBackwardRefs* VP8LGetBackwardReferences(
|
VP8LBackwardRefs* VP8LGetBackwardReferences(
|
||||||
int width, int height, const uint32_t* const argb, int quality,
|
int width, int height, const uint32_t* const argb, int quality,
|
||||||
int cache_bits, int use_2d_locality, VP8LHashChain* const hash_chain,
|
int* cache_bits, VP8LHashChain* const hash_chain,
|
||||||
VP8LBackwardRefs refs_array[2]) {
|
VP8LBackwardRefs refs_array[2]) {
|
||||||
int lz77_is_useful;
|
int lz77_is_useful;
|
||||||
double bit_cost_lz77, bit_cost_rle;
|
double bit_cost_lz77, bit_cost_rle;
|
||||||
VP8LBackwardRefs* best = NULL;
|
VP8LBackwardRefs* best = NULL;
|
||||||
VP8LBackwardRefs* const refs_lz77 = &refs_array[0];
|
VP8LBackwardRefs* const refs_lz77 = &refs_array[0];
|
||||||
VP8LBackwardRefs* const refs_rle = &refs_array[1];
|
VP8LBackwardRefs* const refs_rle = &refs_array[1];
|
||||||
VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits);
|
VP8LHistogram* histo = NULL;
|
||||||
if (histo == NULL) return NULL;
|
|
||||||
|
|
||||||
if (!BackwardReferencesLz77(width, height, argb, cache_bits, quality,
|
if (!CalculateBestCacheSize(argb, width, height, quality, hash_chain,
|
||||||
|
refs_lz77, cache_bits)) {
|
||||||
|
goto Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(vikasa): Use the refs_lz77 computed (with cache_bits=0) in the method
|
||||||
|
// 'CalculateBestCacheSize' to regenerate the lz77 references corresponding to
|
||||||
|
// the optimum cache_bits and avoid calling 'BackwardReferencesLz77' here.
|
||||||
|
if (!BackwardReferencesLz77(width, height, argb, *cache_bits, quality,
|
||||||
hash_chain, refs_lz77)) {
|
hash_chain, refs_lz77)) {
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
if (!BackwardReferencesRle(width, height, argb, cache_bits, refs_rle)) {
|
if (!BackwardReferencesRle(width, height, argb, *cache_bits, refs_rle)) {
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
histo = VP8LAllocateHistogram(*cache_bits);
|
||||||
|
if (histo == NULL) goto Error;
|
||||||
|
|
||||||
{
|
{
|
||||||
// Evaluate LZ77 coding.
|
// Evaluate LZ77 coding.
|
||||||
VP8LHistogramCreate(histo, refs_lz77, cache_bits);
|
VP8LHistogramCreate(histo, refs_lz77, *cache_bits);
|
||||||
bit_cost_lz77 = VP8LHistogramEstimateBits(histo);
|
bit_cost_lz77 = VP8LHistogramEstimateBits(histo);
|
||||||
// Evaluate RLE coding.
|
// Evaluate RLE coding.
|
||||||
VP8LHistogramCreate(histo, refs_rle, cache_bits);
|
VP8LHistogramCreate(histo, refs_rle, *cache_bits);
|
||||||
bit_cost_rle = VP8LHistogramEstimateBits(histo);
|
bit_cost_rle = VP8LHistogramEstimateBits(histo);
|
||||||
// Decide if LZ77 is useful.
|
// Decide if LZ77 is useful.
|
||||||
lz77_is_useful = (bit_cost_lz77 < bit_cost_rle);
|
lz77_is_useful = (bit_cost_lz77 < bit_cost_rle);
|
||||||
@ -853,11 +869,11 @@ VP8LBackwardRefs* VP8LGetBackwardReferences(
|
|||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
if (BackwardReferencesTraceBackwards(width, height, argb, quality,
|
if (BackwardReferencesTraceBackwards(width, height, argb, quality,
|
||||||
cache_bits, hash_chain,
|
*cache_bits, hash_chain,
|
||||||
refs_trace)) {
|
refs_trace)) {
|
||||||
double bit_cost_trace;
|
double bit_cost_trace;
|
||||||
// Evaluate LZ77 coding.
|
// Evaluate LZ77 coding.
|
||||||
VP8LHistogramCreate(histo, refs_trace, cache_bits);
|
VP8LHistogramCreate(histo, refs_trace, *cache_bits);
|
||||||
bit_cost_trace = VP8LHistogramEstimateBits(histo);
|
bit_cost_trace = VP8LHistogramEstimateBits(histo);
|
||||||
if (bit_cost_trace < bit_cost_lz77) {
|
if (bit_cost_trace < bit_cost_lz77) {
|
||||||
best = refs_trace;
|
best = refs_trace;
|
||||||
@ -868,7 +884,7 @@ VP8LBackwardRefs* VP8LGetBackwardReferences(
|
|||||||
best = refs_rle;
|
best = refs_rle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_2d_locality) BackwardReferences2DLocality(width, best);
|
BackwardReferences2DLocality(width, best);
|
||||||
|
|
||||||
Error:
|
Error:
|
||||||
VP8LFreeHistogram(histo);
|
VP8LFreeHistogram(histo);
|
||||||
@ -930,30 +946,33 @@ static double ComputeCacheEntropy(const uint32_t* const argb,
|
|||||||
return entropy;
|
return entropy;
|
||||||
}
|
}
|
||||||
|
|
||||||
// *best_cache_bits will contain how many bits are to be used for a color cache.
|
// Evaluate optimal cache bits for the local color cache.
|
||||||
|
// The input *best_cache_bits sets the maximum cache bits to use (passing 0
|
||||||
|
// implies disabling the local color cache). The local color cache is also
|
||||||
|
// disabled for the lower (<= 25) quality.
|
||||||
// Returns 0 in case of memory error.
|
// Returns 0 in case of memory error.
|
||||||
int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
|
static int CalculateBestCacheSize(const uint32_t* const argb,
|
||||||
int xsize, int ysize, int quality,
|
int xsize, int ysize, int quality,
|
||||||
VP8LHashChain* const hash_chain,
|
VP8LHashChain* const hash_chain,
|
||||||
VP8LBackwardRefs* const refs,
|
VP8LBackwardRefs* const refs,
|
||||||
int* const best_cache_bits) {
|
int* const best_cache_bits) {
|
||||||
int eval_low = 1;
|
int eval_low = 1;
|
||||||
int eval_high = 1;
|
int eval_high = 1;
|
||||||
double entropy_low = MAX_ENTROPY;
|
double entropy_low = MAX_ENTROPY;
|
||||||
double entropy_high = MAX_ENTROPY;
|
double entropy_high = MAX_ENTROPY;
|
||||||
const double cost_mul = 5e-4;
|
const double cost_mul = 5e-4;
|
||||||
int cache_bits_low = 0;
|
int cache_bits_low = 0;
|
||||||
int cache_bits_high = *best_cache_bits;
|
int cache_bits_high = (quality <= 25) ? 0 : *best_cache_bits;
|
||||||
|
|
||||||
assert(cache_bits_high <= MAX_COLOR_CACHE_BITS);
|
assert(cache_bits_high <= MAX_COLOR_CACHE_BITS);
|
||||||
|
|
||||||
if (cache_bits_high == 0) {
|
if (cache_bits_high == 0) {
|
||||||
|
*best_cache_bits = 0;
|
||||||
// Local color cache is disabled.
|
// Local color cache is disabled.
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (!BackwardReferencesLz77(xsize, ysize, argb, cache_bits_low, quality,
|
||||||
if (!BackwardReferencesLz77(xsize, ysize, argb, 0, quality, hash_chain,
|
hash_chain, refs)) {
|
||||||
refs)) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Do a binary search to find the optimal entropy for cache_bits.
|
// Do a binary search to find the optimal entropy for cache_bits.
|
||||||
|
@ -185,21 +185,16 @@ static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) {
|
|||||||
// Main entry points
|
// Main entry points
|
||||||
|
|
||||||
// Evaluates best possible backward references for specified quality.
|
// Evaluates best possible backward references for specified quality.
|
||||||
// Further optimize for 2D locality if use_2d_locality flag is set.
|
// The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache
|
||||||
|
// bits to use (passing 0 implies disabling the local color cache).
|
||||||
|
// The optimal cache bits is evaluated and set for the *cache_bits parameter.
|
||||||
// The return value is the pointer to the best of the two backward refs viz,
|
// The return value is the pointer to the best of the two backward refs viz,
|
||||||
// refs[0] or refs[1].
|
// refs[0] or refs[1].
|
||||||
VP8LBackwardRefs* VP8LGetBackwardReferences(
|
VP8LBackwardRefs* VP8LGetBackwardReferences(
|
||||||
int width, int height, const uint32_t* const argb, int quality,
|
int width, int height, const uint32_t* const argb, int quality,
|
||||||
int cache_bits, int use_2d_locality, VP8LHashChain* const hash_chain,
|
int* cache_bits, VP8LHashChain* const hash_chain,
|
||||||
VP8LBackwardRefs refs[2]);
|
VP8LBackwardRefs refs[2]);
|
||||||
|
|
||||||
// Produce an estimate for a good color cache size for the image.
|
|
||||||
int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
|
|
||||||
int xsize, int ysize, int quality,
|
|
||||||
VP8LHashChain* const hash_chain,
|
|
||||||
VP8LBackwardRefs* const ref,
|
|
||||||
int* const best_cache_bits);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -281,12 +281,6 @@ static int GetTransformBits(int method, int histo_bits) {
|
|||||||
return (histo_bits > max_transform_bits) ? max_transform_bits : histo_bits;
|
return (histo_bits > max_transform_bits) ? max_transform_bits : histo_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int GetCacheBits(int use_palette, float quality) {
|
|
||||||
// Color cache is disabled for compression at lower quality or when a palette
|
|
||||||
// is used.
|
|
||||||
return (use_palette || (quality <= 25.f)) ? 0 : MAX_COLOR_CACHE_BITS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int EvalSubtractGreenForPalette(int palette_size, float quality) {
|
static int EvalSubtractGreenForPalette(int palette_size, float quality) {
|
||||||
// Evaluate non-palette encoding (subtract green, prediction transforms etc)
|
// Evaluate non-palette encoding (subtract green, prediction transforms etc)
|
||||||
// for palette size in the mid-range (17-96) as for larger number of colors,
|
// for palette size in the mid-range (17-96) as for larger number of colors,
|
||||||
@ -351,7 +345,6 @@ static int AnalyzeAndInit(VP8LEncoder* const enc, WebPImageHint image_hint) {
|
|||||||
enc->use_subtract_green_ = 1;
|
enc->use_subtract_green_ = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
enc->cache_bits_ = GetCacheBits(enc->use_palette_, quality);
|
|
||||||
|
|
||||||
if (!enc->use_palette_) {
|
if (!enc->use_palette_) {
|
||||||
if (image_hint == WEBP_HINT_PHOTO) {
|
if (image_hint == WEBP_HINT_PHOTO) {
|
||||||
@ -690,21 +683,28 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
|
|||||||
HuffmanTreeToken* tokens = NULL;
|
HuffmanTreeToken* tokens = NULL;
|
||||||
HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } };
|
HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } };
|
||||||
const uint16_t histogram_symbols[1] = { 0 }; // only one tree, one symbol
|
const uint16_t histogram_symbols[1] = { 0 }; // only one tree, one symbol
|
||||||
VP8LHistogramSet* const histogram_image = VP8LAllocateHistogramSet(1, 0);
|
int cache_bits = 0;
|
||||||
|
VP8LHistogramSet* histogram_image = NULL;
|
||||||
HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc(
|
HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc(
|
||||||
3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
|
3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
|
||||||
if (histogram_image == NULL || huff_tree == NULL) {
|
if (huff_tree == NULL) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate backward references from ARGB image.
|
// Calculate backward references from ARGB image.
|
||||||
refs = VP8LGetBackwardReferences(width, height, argb, quality, 0, 1,
|
refs = VP8LGetBackwardReferences(width, height, argb, quality, &cache_bits,
|
||||||
hash_chain, refs_array);
|
hash_chain, refs_array);
|
||||||
if (refs == NULL) {
|
if (refs == NULL) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
histogram_image = VP8LAllocateHistogramSet(1, cache_bits);
|
||||||
|
if (histogram_image == NULL) {
|
||||||
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
|
goto Error;
|
||||||
|
}
|
||||||
|
|
||||||
// Build histogram image and symbols from backward references.
|
// Build histogram image and symbols from backward references.
|
||||||
VP8LHistogramStoreRefs(refs, histogram_image->histograms[0]);
|
VP8LHistogramStoreRefs(refs, histogram_image->histograms[0]);
|
||||||
|
|
||||||
@ -756,19 +756,16 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
VP8LHashChain* const hash_chain,
|
VP8LHashChain* const hash_chain,
|
||||||
VP8LBackwardRefs refs_array[2],
|
VP8LBackwardRefs refs_array[2],
|
||||||
int width, int height, int quality,
|
int width, int height, int quality,
|
||||||
int cache_bits,
|
int* cache_bits,
|
||||||
int histogram_bits,
|
int histogram_bits,
|
||||||
size_t init_byte_position,
|
size_t init_byte_position,
|
||||||
int* const hdr_size,
|
int* const hdr_size,
|
||||||
int* const data_size) {
|
int* const data_size) {
|
||||||
WebPEncodingError err = VP8_ENC_OK;
|
WebPEncodingError err = VP8_ENC_OK;
|
||||||
const int use_2d_locality = 1;
|
|
||||||
const int use_color_cache = (cache_bits > 0);
|
|
||||||
const uint32_t histogram_image_xysize =
|
const uint32_t histogram_image_xysize =
|
||||||
VP8LSubSampleSize(width, histogram_bits) *
|
VP8LSubSampleSize(width, histogram_bits) *
|
||||||
VP8LSubSampleSize(height, histogram_bits);
|
VP8LSubSampleSize(height, histogram_bits);
|
||||||
VP8LHistogramSet* histogram_image =
|
VP8LHistogramSet* histogram_image = NULL;
|
||||||
VP8LAllocateHistogramSet(histogram_image_xysize, cache_bits);
|
|
||||||
int histogram_image_size = 0;
|
int histogram_image_size = 0;
|
||||||
size_t bit_array_size = 0;
|
size_t bit_array_size = 0;
|
||||||
HuffmanTree* huff_tree = NULL;
|
HuffmanTree* huff_tree = NULL;
|
||||||
@ -785,25 +782,31 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
assert(data_size != NULL);
|
assert(data_size != NULL);
|
||||||
|
|
||||||
VP8LBackwardRefsInit(&refs, refs_array[0].block_size_);
|
VP8LBackwardRefsInit(&refs, refs_array[0].block_size_);
|
||||||
if (histogram_image == NULL || histogram_symbols == NULL) {
|
if (histogram_symbols == NULL) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*cache_bits = MAX_COLOR_CACHE_BITS;
|
||||||
// 'best_refs' is the reference to the best backward refs and points to one
|
// 'best_refs' is the reference to the best backward refs and points to one
|
||||||
// of refs_array[0] or refs_array[1].
|
// of refs_array[0] or refs_array[1].
|
||||||
// Calculate backward references from ARGB image.
|
// Calculate backward references from ARGB image.
|
||||||
best_refs = VP8LGetBackwardReferences(width, height, argb, quality,
|
best_refs = VP8LGetBackwardReferences(width, height, argb, quality,
|
||||||
cache_bits, use_2d_locality,
|
cache_bits, hash_chain, refs_array);
|
||||||
hash_chain, refs_array);
|
|
||||||
if (best_refs == NULL || !VP8LBackwardRefsCopy(best_refs, &refs)) {
|
if (best_refs == NULL || !VP8LBackwardRefsCopy(best_refs, &refs)) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
histogram_image =
|
||||||
|
VP8LAllocateHistogramSet(histogram_image_xysize, *cache_bits);
|
||||||
|
if (histogram_image == NULL) {
|
||||||
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
|
goto Error;
|
||||||
|
}
|
||||||
|
|
||||||
// Build histogram image and symbols from backward references.
|
// Build histogram image and symbols from backward references.
|
||||||
if (!VP8LGetHistoImageSymbols(width, height, &refs,
|
if (!VP8LGetHistoImageSymbols(width, height, &refs, quality, histogram_bits,
|
||||||
quality, histogram_bits, cache_bits,
|
*cache_bits, histogram_image,
|
||||||
histogram_image,
|
|
||||||
histogram_symbols)) {
|
histogram_symbols)) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
@ -823,9 +826,11 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
histogram_image = NULL;
|
histogram_image = NULL;
|
||||||
|
|
||||||
// Color Cache parameters.
|
// Color Cache parameters.
|
||||||
VP8LPutBits(bw, use_color_cache, 1);
|
if (*cache_bits > 0) {
|
||||||
if (use_color_cache) {
|
VP8LPutBits(bw, 1, 1);
|
||||||
VP8LPutBits(bw, cache_bits, 4);
|
VP8LPutBits(bw, *cache_bits, 4);
|
||||||
|
} else {
|
||||||
|
VP8LPutBits(bw, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Huffman image + meta huffman.
|
// Huffman image + meta huffman.
|
||||||
@ -1306,23 +1311,12 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
|
|
||||||
VP8LPutBits(bw, !TRANSFORM_PRESENT, 1); // No more transforms.
|
VP8LPutBits(bw, !TRANSFORM_PRESENT, 1); // No more transforms.
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Estimate the color cache size.
|
|
||||||
|
|
||||||
if (!VP8LCalculateEstimateForCacheSize(enc->argb_, enc->current_width_,
|
|
||||||
height, quality, &enc->hash_chain_,
|
|
||||||
&enc->refs_[0], &enc->cache_bits_)) {
|
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
|
||||||
goto Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Encode and write the transformed image.
|
// Encode and write the transformed image.
|
||||||
|
|
||||||
err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_,
|
err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_,
|
||||||
enc->current_width_, height, quality,
|
enc->current_width_, height, quality,
|
||||||
enc->cache_bits_, enc->histo_bits_,
|
&enc->cache_bits_, enc->histo_bits_, byte_position,
|
||||||
byte_position, &hdr_size, &data_size);
|
&hdr_size, &data_size);
|
||||||
if (err != VP8_ENC_OK) goto Error;
|
if (err != VP8_ENC_OK) goto Error;
|
||||||
|
|
||||||
if (picture->stats != NULL) {
|
if (picture->stats != NULL) {
|
||||||
|
Loading…
Reference in New Issue
Block a user