mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-27 22:28:22 +01:00
Refactor LZ77 handling in preparation for a new method.
Change-Id: If305c328c8f508bd778d1af108e4eb979fbd2eca
This commit is contained in:
parent
67de68b5d9
commit
a1779a017b
@ -688,26 +688,34 @@ extern int VP8LBackwardReferencesTraceBackwards(
|
|||||||
const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst);
|
const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst);
|
||||||
static VP8LBackwardRefs* GetBackwardReferences(
|
static VP8LBackwardRefs* GetBackwardReferences(
|
||||||
int width, int height, const uint32_t* const argb, int quality,
|
int width, int height, const uint32_t* const argb, int quality,
|
||||||
int* const cache_bits, const VP8LHashChain* const hash_chain,
|
int lz77_types_to_try, int* const cache_bits,
|
||||||
VP8LBackwardRefs* best, VP8LBackwardRefs* worst) {
|
const VP8LHashChain* const hash_chain, VP8LBackwardRefs* best,
|
||||||
|
VP8LBackwardRefs* worst) {
|
||||||
const int cache_bits_initial = *cache_bits;
|
const int cache_bits_initial = *cache_bits;
|
||||||
double bit_cost_best = -1;
|
double bit_cost_best = -1;
|
||||||
VP8LHistogram* histo = NULL;
|
VP8LHistogram* histo = NULL;
|
||||||
int i, i_best = 0;
|
int lz77_type, lz77_type_best = 0;
|
||||||
|
|
||||||
histo = VP8LAllocateHistogram(MAX_COLOR_CACHE_BITS);
|
histo = VP8LAllocateHistogram(MAX_COLOR_CACHE_BITS);
|
||||||
if (histo == NULL) goto Error;
|
if (histo == NULL) goto Error;
|
||||||
|
|
||||||
// Try out RLE first, then LZ77.
|
for (lz77_type = 1; lz77_types_to_try;
|
||||||
for (i = 0; i <= 1; ++i) {
|
lz77_types_to_try &= ~lz77_type, lz77_type <<= 1) {
|
||||||
int res;
|
int res = 0;
|
||||||
double bit_cost;
|
double bit_cost;
|
||||||
int cache_bits_tmp = cache_bits_initial;
|
int cache_bits_tmp = cache_bits_initial;
|
||||||
// First, try out backward references with no cache (0 bits).
|
if ((lz77_types_to_try & lz77_type) == 0) continue;
|
||||||
if (i == 0) {
|
switch (lz77_type) {
|
||||||
res = BackwardReferencesRle(width, height, argb, 0, worst);
|
case kLZ77RLE:
|
||||||
} else {
|
res = BackwardReferencesRle(width, height, argb, 0, worst);
|
||||||
res = BackwardReferencesLz77(width, height, argb, 0, hash_chain, worst);
|
break;
|
||||||
|
case kLZ77Standard:
|
||||||
|
// Compute LZ77 with no cache (0 bits), as the ideal LZ77 with a color
|
||||||
|
// cache is not that different in practice.
|
||||||
|
res = BackwardReferencesLz77(width, height, argb, 0, hash_chain, worst);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
}
|
}
|
||||||
if (!res) goto Error;
|
if (!res) goto Error;
|
||||||
|
|
||||||
@ -724,19 +732,20 @@ static VP8LBackwardRefs* GetBackwardReferences(
|
|||||||
// Keep the best backward references.
|
// Keep the best backward references.
|
||||||
VP8LHistogramCreate(histo, worst, cache_bits_tmp);
|
VP8LHistogramCreate(histo, worst, cache_bits_tmp);
|
||||||
bit_cost = VP8LHistogramEstimateBits(histo);
|
bit_cost = VP8LHistogramEstimateBits(histo);
|
||||||
if (i == 0 || bit_cost < bit_cost_best) {
|
if (lz77_type_best == 0 || bit_cost < bit_cost_best) {
|
||||||
VP8LBackwardRefs* const tmp = worst;
|
VP8LBackwardRefs* const tmp = worst;
|
||||||
worst = best;
|
worst = best;
|
||||||
best = tmp;
|
best = tmp;
|
||||||
bit_cost_best = bit_cost;
|
bit_cost_best = bit_cost;
|
||||||
*cache_bits = cache_bits_tmp;
|
*cache_bits = cache_bits_tmp;
|
||||||
i_best = i;
|
lz77_type_best = lz77_type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
assert(lz77_type_best > 0);
|
||||||
|
|
||||||
// Improve on simple LZ77 but only for high quality (TraceBackwards is
|
// Improve on simple LZ77 but only for high quality (TraceBackwards is
|
||||||
// costly).
|
// costly).
|
||||||
if (i_best == 1 && quality >= 25) {
|
if (lz77_type_best == kLZ77Standard && quality >= 25) {
|
||||||
if (VP8LBackwardReferencesTraceBackwards(width, height, argb, *cache_bits,
|
if (VP8LBackwardReferencesTraceBackwards(width, height, argb, *cache_bits,
|
||||||
hash_chain, best, worst)) {
|
hash_chain, best, worst)) {
|
||||||
double bit_cost_trace;
|
double bit_cost_trace;
|
||||||
@ -748,21 +757,22 @@ static VP8LBackwardRefs* GetBackwardReferences(
|
|||||||
|
|
||||||
BackwardReferences2DLocality(width, best);
|
BackwardReferences2DLocality(width, best);
|
||||||
|
|
||||||
Error:
|
Error:
|
||||||
VP8LFreeHistogram(histo);
|
VP8LFreeHistogram(histo);
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 low_effort, int* const cache_bits,
|
int low_effort, int lz77_types_to_try, int* const cache_bits,
|
||||||
const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1,
|
const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1,
|
||||||
VP8LBackwardRefs* const refs_tmp2) {
|
VP8LBackwardRefs* const refs_tmp2) {
|
||||||
if (low_effort) {
|
if (low_effort) {
|
||||||
return GetBackwardReferencesLowEffort(width, height, argb, cache_bits,
|
return GetBackwardReferencesLowEffort(width, height, argb, cache_bits,
|
||||||
hash_chain, refs_tmp1);
|
hash_chain, refs_tmp1);
|
||||||
} else {
|
} else {
|
||||||
return GetBackwardReferences(width, height, argb, quality, cache_bits,
|
return GetBackwardReferences(width, height, argb, quality,
|
||||||
hash_chain, refs_tmp1, refs_tmp2);
|
lz77_types_to_try, cache_bits, hash_chain,
|
||||||
|
refs_tmp1, refs_tmp2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,6 +213,11 @@ static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) {
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Main entry points
|
// Main entry points
|
||||||
|
|
||||||
|
enum VP8LLZ77Type {
|
||||||
|
kLZ77Standard = 1,
|
||||||
|
kLZ77RLE = 2,
|
||||||
|
};
|
||||||
|
|
||||||
// Evaluates best possible backward references for specified quality.
|
// Evaluates best possible backward references for specified quality.
|
||||||
// The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache
|
// The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache
|
||||||
// bits to use (passing 0 implies disabling the local color cache).
|
// bits to use (passing 0 implies disabling the local color cache).
|
||||||
@ -221,7 +226,7 @@ static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) {
|
|||||||
// 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 low_effort, int* const cache_bits,
|
int low_effort, int lz77_types_to_try, int* const cache_bits,
|
||||||
const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1,
|
const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1,
|
||||||
VP8LBackwardRefs* const refs_tmp2);
|
VP8LBackwardRefs* const refs_tmp2);
|
||||||
|
|
||||||
|
@ -356,9 +356,17 @@ static int GetTransformBits(int method, int histo_bits) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Se of parameters to be used in each iteration of the cruncher.
|
||||||
|
typedef struct {
|
||||||
|
int entropy_idx_;
|
||||||
|
int lz77s_types_to_try_;
|
||||||
|
} CrunchConfig;
|
||||||
|
|
||||||
|
#define CRUNCH_CONFIGS_MAX kNumEntropyIx
|
||||||
|
|
||||||
static int AnalyzeAndInit(VP8LEncoder* const enc,
|
static int AnalyzeAndInit(VP8LEncoder* const enc,
|
||||||
int entropy_idx[kNumEntropyIx],
|
CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX],
|
||||||
int* const num_entropy_idx,
|
int* const crunch_configs_size,
|
||||||
int* const red_and_blue_always_zero) {
|
int* const red_and_blue_always_zero) {
|
||||||
const WebPPicture* const pic = enc->pic_;
|
const WebPPicture* const pic = enc->pic_;
|
||||||
const int width = pic->width;
|
const int width = pic->width;
|
||||||
@ -386,8 +394,9 @@ static int AnalyzeAndInit(VP8LEncoder* const enc,
|
|||||||
|
|
||||||
if (low_effort) {
|
if (low_effort) {
|
||||||
// AnalyzeEntropy is somewhat slow.
|
// AnalyzeEntropy is somewhat slow.
|
||||||
entropy_idx[0] = use_palette ? kPalette : kSpatialSubGreen;
|
crunch_configs[0].entropy_idx_ = use_palette ? kPalette : kSpatialSubGreen;
|
||||||
*num_entropy_idx = 1;
|
crunch_configs[0].lz77s_types_to_try_ = kLZ77Standard | kLZ77RLE;
|
||||||
|
*crunch_configs_size = 1;
|
||||||
} else {
|
} else {
|
||||||
EntropyIx min_entropy_ix;
|
EntropyIx min_entropy_ix;
|
||||||
if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride, use_palette,
|
if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride, use_palette,
|
||||||
@ -395,19 +404,22 @@ static int AnalyzeAndInit(VP8LEncoder* const enc,
|
|||||||
&min_entropy_ix, red_and_blue_always_zero)) {
|
&min_entropy_ix, red_and_blue_always_zero)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
*crunch_configs_size = 0;
|
||||||
if (method == 6 && config->quality == 100) {
|
if (method == 6 && config->quality == 100) {
|
||||||
// Go brute force on all transforms.
|
// Go brute force on all transforms.
|
||||||
*num_entropy_idx = 0;
|
|
||||||
for (i = 0; i < kNumEntropyIx; ++i) {
|
for (i = 0; i < kNumEntropyIx; ++i) {
|
||||||
if (i != kPalette || use_palette) {
|
if (i != kPalette || use_palette) {
|
||||||
entropy_idx[(*num_entropy_idx)++] = i;
|
crunch_configs[*crunch_configs_size].entropy_idx_ = i;
|
||||||
assert(*num_entropy_idx <= kNumEntropyIx);
|
crunch_configs[(*crunch_configs_size)++].lz77s_types_to_try_ =
|
||||||
|
kLZ77Standard | kLZ77RLE;
|
||||||
|
assert(*crunch_configs_size <= CRUNCH_CONFIGS_MAX);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Only choose the guessed best transform.
|
// Only choose the guessed best transform.
|
||||||
entropy_idx[0] = min_entropy_ix;
|
crunch_configs[*crunch_configs_size].entropy_idx_ = min_entropy_ix;
|
||||||
*num_entropy_idx = 1;
|
crunch_configs[(*crunch_configs_size)++].lz77s_types_to_try_ =
|
||||||
|
kLZ77Standard | kLZ77RLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -771,7 +783,8 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
|
|||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
refs = VP8LGetBackwardReferences(width, height, argb, quality, 0, &cache_bits,
|
refs = VP8LGetBackwardReferences(width, height, argb, quality, 0,
|
||||||
|
kLZ77Standard | kLZ77RLE, &cache_bits,
|
||||||
hash_chain, refs_tmp1, refs_tmp2);
|
hash_chain, refs_tmp1, refs_tmp2);
|
||||||
if (refs == NULL) {
|
if (refs == NULL) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
@ -829,17 +842,12 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw,
|
static WebPEncodingError EncodeImageInternal(
|
||||||
const uint32_t* const argb,
|
VP8LBitWriter* const bw, const uint32_t* const argb,
|
||||||
VP8LHashChain* const hash_chain,
|
VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[3], int width,
|
||||||
VP8LBackwardRefs refs_array[3],
|
int height, int quality, int low_effort, int use_cache, int lz77s_to_try,
|
||||||
int width, int height, int quality,
|
int* cache_bits, int histogram_bits, size_t init_byte_position,
|
||||||
int low_effort,
|
int* const hdr_size, int* const data_size) {
|
||||||
int use_cache, int* cache_bits,
|
|
||||||
int histogram_bits,
|
|
||||||
size_t init_byte_position,
|
|
||||||
int* const hdr_size,
|
|
||||||
int* const data_size) {
|
|
||||||
WebPEncodingError err = VP8_ENC_OK;
|
WebPEncodingError err = VP8_ENC_OK;
|
||||||
const uint32_t histogram_image_xysize =
|
const uint32_t histogram_image_xysize =
|
||||||
VP8LSubSampleSize(width, histogram_bits) *
|
VP8LSubSampleSize(width, histogram_bits) *
|
||||||
@ -881,9 +889,9 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
refs_best = VP8LGetBackwardReferences(width, height, argb, quality,
|
refs_best = VP8LGetBackwardReferences(
|
||||||
low_effort, cache_bits, hash_chain,
|
width, height, argb, quality, low_effort, lz77s_to_try, cache_bits,
|
||||||
&refs_array[0], &refs_array[1]);
|
hash_chain, &refs_array[0], &refs_array[1]);
|
||||||
if (refs_best == NULL) {
|
if (refs_best == NULL) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
@ -1506,8 +1514,8 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
int hdr_size = 0;
|
int hdr_size = 0;
|
||||||
int data_size = 0;
|
int data_size = 0;
|
||||||
int use_delta_palette = 0;
|
int use_delta_palette = 0;
|
||||||
int entropy_idx[kNumEntropyIx];
|
CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX];
|
||||||
int num_entropy_idx = 0;
|
int num_crunch_configs = 0;
|
||||||
int idx;
|
int idx;
|
||||||
int red_and_blue_always_zero = 0;
|
int red_and_blue_always_zero = 0;
|
||||||
size_t best_size = 0;
|
size_t best_size = 0;
|
||||||
@ -1521,18 +1529,19 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Analyze image (entropy, num_palettes etc)
|
// Analyze image (entropy, num_palettes etc)
|
||||||
|
|
||||||
if (!AnalyzeAndInit(enc, entropy_idx, &num_entropy_idx,
|
if (!AnalyzeAndInit(enc, crunch_configs, &num_crunch_configs,
|
||||||
&red_and_blue_always_zero)) {
|
&red_and_blue_always_zero)) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (idx = 0; idx < num_entropy_idx; ++idx) {
|
for (idx = 0; idx < num_crunch_configs; ++idx) {
|
||||||
enc->use_palette_ = (entropy_idx[idx] == kPalette);
|
const int entropy_idx = crunch_configs[idx].entropy_idx_;
|
||||||
enc->use_subtract_green_ = (entropy_idx[idx] == kSubGreen) ||
|
enc->use_palette_ = (entropy_idx == kPalette);
|
||||||
(entropy_idx[idx] == kSpatialSubGreen);
|
enc->use_subtract_green_ =
|
||||||
enc->use_predict_ = (entropy_idx[idx] == kSpatial) ||
|
(entropy_idx == kSubGreen) || (entropy_idx == kSpatialSubGreen);
|
||||||
(entropy_idx[idx] == kSpatialSubGreen);
|
enc->use_predict_ =
|
||||||
|
(entropy_idx == kSpatial) || (entropy_idx == kSpatialSubGreen);
|
||||||
if (low_effort) {
|
if (low_effort) {
|
||||||
enc->use_cross_color_ = 0;
|
enc->use_cross_color_ = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -1626,10 +1635,11 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Encode and write the transformed image.
|
// Encode and write the transformed image.
|
||||||
err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_,
|
err = EncodeImageInternal(
|
||||||
enc->current_width_, height, quality, low_effort,
|
bw, enc->argb_, &enc->hash_chain_, enc->refs_, enc->current_width_,
|
||||||
use_cache, &enc->cache_bits_, enc->histo_bits_,
|
height, quality, low_effort, use_cache,
|
||||||
byte_position, &hdr_size, &data_size);
|
crunch_configs[idx].lz77s_types_to_try_, &enc->cache_bits_,
|
||||||
|
enc->histo_bits_, byte_position, &hdr_size, &data_size);
|
||||||
if (err != VP8_ENC_OK) goto Error;
|
if (err != VP8_ENC_OK) goto Error;
|
||||||
|
|
||||||
// If we are better than what we already have.
|
// If we are better than what we already have.
|
||||||
@ -1655,7 +1665,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Reset the bit writer for the following iteration if any.
|
// Reset the bit writer for the following iteration if any.
|
||||||
if (num_entropy_idx > 1) VP8LBitWriterReset(&bw_init, bw);
|
if (num_crunch_configs > 1) VP8LBitWriterReset(&bw_init, bw);
|
||||||
}
|
}
|
||||||
VP8LBitWriterSwap(&bw_best, bw);
|
VP8LBitWriterSwap(&bw_best, bw);
|
||||||
|
|
||||||
@ -1664,6 +1674,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
VP8LBitWriterWipeOut(&bw_best);
|
VP8LBitWriterWipeOut(&bw_best);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
#undef CRUNCH_CONFIGS_MAX
|
||||||
|
|
||||||
int VP8LEncodeImage(const WebPConfig* const config,
|
int VP8LEncodeImage(const WebPConfig* const config,
|
||||||
const WebPPicture* const picture) {
|
const WebPPicture* const picture) {
|
||||||
|
Loading…
Reference in New Issue
Block a user