mirror of
https://github.com/webmproject/libwebp.git
synced 2024-11-20 12:28:26 +01:00
Merge "Reduce memory footprint for encoding WebP lossless."
This commit is contained in:
commit
35d7d095dd
@ -21,8 +21,6 @@
|
|||||||
|
|
||||||
#define VALUES_IN_BYTE 256
|
#define VALUES_IN_BYTE 256
|
||||||
|
|
||||||
#define HASH_BITS 18
|
|
||||||
#define HASH_SIZE (1 << HASH_BITS)
|
|
||||||
#define HASH_MULTIPLIER (0xc6a4a7935bd1e995ULL)
|
#define HASH_MULTIPLIER (0xc6a4a7935bd1e995ULL)
|
||||||
|
|
||||||
#define MAX_ENTROPY (1e30f)
|
#define MAX_ENTROPY (1e30f)
|
||||||
@ -34,14 +32,6 @@
|
|||||||
#define MIN_LENGTH 2
|
#define MIN_LENGTH 2
|
||||||
#define MAX_LENGTH 4096
|
#define MAX_LENGTH 4096
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
// Stores the most recently added position with the given hash value.
|
|
||||||
int32_t hash_to_first_index_[HASH_SIZE];
|
|
||||||
// chain_[pos] stores the previous position with the same hash value
|
|
||||||
// for every pixel in the image.
|
|
||||||
int32_t* chain_;
|
|
||||||
} HashChain;
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
static const uint8_t plane_to_code_lut[128] = {
|
static const uint8_t plane_to_code_lut[128] = {
|
||||||
@ -79,29 +69,44 @@ static WEBP_INLINE int FindMatchLength(const uint32_t* const array1,
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// VP8LBackwardRefs
|
// VP8LBackwardRefs
|
||||||
|
|
||||||
void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs) {
|
static void ClearBackwardRefs(VP8LBackwardRefs* const refs) {
|
||||||
if (refs != NULL) {
|
|
||||||
refs->refs = NULL;
|
|
||||||
refs->size = 0;
|
|
||||||
refs->max_size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs) {
|
|
||||||
if (refs != NULL) {
|
|
||||||
WebPSafeFree(refs->refs);
|
|
||||||
VP8LInitBackwardRefs(refs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int VP8LBackwardRefsAlloc(VP8LBackwardRefs* const refs, int max_size) {
|
|
||||||
assert(refs != NULL);
|
assert(refs != NULL);
|
||||||
refs->size = 0;
|
refs->size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VP8LBackwardRefsDelete(VP8LBackwardRefs* const refs) {
|
||||||
|
if (refs != NULL) {
|
||||||
|
WebPSafeFree(refs->refs);
|
||||||
|
WebPSafeFree(refs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VP8LBackwardRefs* VP8LBackwardRefsNew(int max_size) {
|
||||||
|
VP8LBackwardRefs* const refs =
|
||||||
|
(VP8LBackwardRefs*)WebPSafeMalloc(1ULL, sizeof(*refs));
|
||||||
|
if (refs == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ClearBackwardRefs(refs);
|
||||||
refs->max_size = 0;
|
refs->max_size = 0;
|
||||||
refs->refs = (PixOrCopy*)WebPSafeMalloc((uint64_t)max_size,
|
refs->refs = (PixOrCopy*)WebPSafeMalloc((uint64_t)max_size,
|
||||||
sizeof(*refs->refs));
|
sizeof(*refs->refs));
|
||||||
if (refs->refs == NULL) return 0;
|
if (refs->refs == NULL) {
|
||||||
|
WebPSafeFree(refs);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
refs->max_size = max_size;
|
refs->max_size = max_size;
|
||||||
|
return refs;
|
||||||
|
}
|
||||||
|
|
||||||
|
int VP8LBackwardRefsCopy(const VP8LBackwardRefs* const src,
|
||||||
|
VP8LBackwardRefs* const dst) {
|
||||||
|
assert(src != NULL && dst != NULL);
|
||||||
|
if (dst->max_size != src->max_size) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
dst->size = src->size;
|
||||||
|
memcpy(dst->refs, src->refs, src->size * sizeof(*src->refs));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,9 +119,19 @@ static WEBP_INLINE uint64_t GetPixPairHash64(const uint32_t* const argb) {
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HashChain* HashChainNew(int size) {
|
static void HashChainInit(VP8LHashChain* const p) {
|
||||||
int i;
|
int i;
|
||||||
HashChain* const p = (HashChain*)WebPSafeMalloc(1ULL, sizeof(*p));
|
assert(p != NULL);
|
||||||
|
for (i = 0; i < p->size_; ++i) {
|
||||||
|
p->chain_[i] = -1;
|
||||||
|
}
|
||||||
|
for (i = 0; i < HASH_SIZE; ++i) {
|
||||||
|
p->hash_to_first_index_[i] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VP8LHashChain* VP8LHashChainNew(int size) {
|
||||||
|
VP8LHashChain* const p = (VP8LHashChain*)WebPSafeMalloc(1ULL, sizeof(*p));
|
||||||
if (p == NULL) {
|
if (p == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -125,16 +140,12 @@ static HashChain* HashChainNew(int size) {
|
|||||||
WebPSafeFree(p);
|
WebPSafeFree(p);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
for (i = 0; i < size; ++i) {
|
p->size_ = size;
|
||||||
p->chain_[i] = -1;
|
HashChainInit(p);
|
||||||
}
|
|
||||||
for (i = 0; i < HASH_SIZE; ++i) {
|
|
||||||
p->hash_to_first_index_[i] = -1;
|
|
||||||
}
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void HashChainDelete(HashChain* const p) {
|
void VP8LHashChainDelete(VP8LHashChain* const p) {
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
WebPSafeFree(p->chain_);
|
WebPSafeFree(p->chain_);
|
||||||
WebPSafeFree(p);
|
WebPSafeFree(p);
|
||||||
@ -142,7 +153,7 @@ static void HashChainDelete(HashChain* const p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Insertion of two pixels at a time.
|
// Insertion of two pixels at a time.
|
||||||
static void HashChainInsert(HashChain* const p,
|
static void HashChainInsert(VP8LHashChain* const p,
|
||||||
const uint32_t* const argb, int pos) {
|
const uint32_t* const argb, int pos) {
|
||||||
const uint64_t hash_code = GetPixPairHash64(argb);
|
const uint64_t hash_code = GetPixPairHash64(argb);
|
||||||
p->chain_[pos] = p->hash_to_first_index_[hash_code];
|
p->chain_[pos] = p->hash_to_first_index_[hash_code];
|
||||||
@ -167,7 +178,7 @@ static void GetParamsForHashChainFindCopy(int quality, int xsize,
|
|||||||
*iter_limit = (cache_bits > 0) ? iter_neg : iter_neg / 2;
|
*iter_limit = (cache_bits > 0) ? iter_neg : iter_neg / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int HashChainFindCopy(const HashChain* const p,
|
static int HashChainFindCopy(const VP8LHashChain* const p,
|
||||||
int base_position, int xsize_signed,
|
int base_position, int xsize_signed,
|
||||||
const uint32_t* const argb, int max_len,
|
const uint32_t* const argb, int max_len,
|
||||||
int window_size, int iter_pos, int iter_limit,
|
int window_size, int iter_pos, int iter_limit,
|
||||||
@ -260,7 +271,7 @@ static void BackwardReferencesRle(int xsize, int ysize,
|
|||||||
const int pix_count = xsize * ysize;
|
const int pix_count = xsize * ysize;
|
||||||
int match_len = 0;
|
int match_len = 0;
|
||||||
int i;
|
int i;
|
||||||
refs->size = 0;
|
ClearBackwardRefs(refs);
|
||||||
PushBackCopy(refs, match_len); // i=0 case
|
PushBackCopy(refs, match_len); // i=0 case
|
||||||
refs->refs[refs->size++] = PixOrCopyCreateLiteral(argb[0]);
|
refs->refs[refs->size++] = PixOrCopyCreateLiteral(argb[0]);
|
||||||
for (i = 1; i < pix_count; ++i) {
|
for (i = 1; i < pix_count; ++i) {
|
||||||
@ -278,28 +289,27 @@ static void BackwardReferencesRle(int xsize, int ysize,
|
|||||||
static int BackwardReferencesHashChain(int xsize, int ysize,
|
static int BackwardReferencesHashChain(int xsize, int ysize,
|
||||||
const uint32_t* const argb,
|
const uint32_t* const argb,
|
||||||
int cache_bits, int quality,
|
int cache_bits, int quality,
|
||||||
|
VP8LHashChain* const hash_chain,
|
||||||
VP8LBackwardRefs* const refs) {
|
VP8LBackwardRefs* const refs) {
|
||||||
int i;
|
int i;
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
int cc_init = 0;
|
int cc_init = 0;
|
||||||
const int use_color_cache = (cache_bits > 0);
|
const int use_color_cache = (cache_bits > 0);
|
||||||
const int pix_count = xsize * ysize;
|
const int pix_count = xsize * ysize;
|
||||||
HashChain* const hash_chain = HashChainNew(pix_count);
|
|
||||||
VP8LColorCache hashers;
|
VP8LColorCache hashers;
|
||||||
int window_size = WINDOW_SIZE;
|
int window_size = WINDOW_SIZE;
|
||||||
int iter_pos = 1;
|
int iter_pos = 1;
|
||||||
int iter_limit = -1;
|
int iter_limit = -1;
|
||||||
|
|
||||||
if (hash_chain == NULL) return 0;
|
|
||||||
if (use_color_cache) {
|
if (use_color_cache) {
|
||||||
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
|
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
|
||||||
if (!cc_init) goto Error;
|
if (!cc_init) goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClearBackwardRefs(refs);
|
||||||
refs->size = 0;
|
|
||||||
GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
|
GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
|
||||||
&window_size, &iter_pos, &iter_limit);
|
&window_size, &iter_pos, &iter_limit);
|
||||||
|
HashChainInit(hash_chain);
|
||||||
for (i = 0; i < pix_count; ) {
|
for (i = 0; i < pix_count; ) {
|
||||||
// Alternative#1: Code the pixels starting at 'i' using backward reference.
|
// Alternative#1: Code the pixels starting at 'i' using backward reference.
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
@ -375,7 +385,6 @@ static int BackwardReferencesHashChain(int xsize, int ysize,
|
|||||||
ok = 1;
|
ok = 1;
|
||||||
Error:
|
Error:
|
||||||
if (cc_init) VP8LColorCacheClear(&hashers);
|
if (cc_init) VP8LColorCacheClear(&hashers);
|
||||||
HashChainDelete(hash_chain);
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,6 +401,7 @@ typedef struct {
|
|||||||
static int BackwardReferencesTraceBackwards(
|
static int BackwardReferencesTraceBackwards(
|
||||||
int xsize, int ysize, int recursive_cost_model,
|
int xsize, int ysize, int recursive_cost_model,
|
||||||
const uint32_t* const argb, int quality, int cache_bits,
|
const uint32_t* const argb, int quality, int cache_bits,
|
||||||
|
VP8LHashChain* const hash_chain,
|
||||||
VP8LBackwardRefs* const refs);
|
VP8LBackwardRefs* const refs);
|
||||||
|
|
||||||
static void ConvertPopulationCountTableToBitEstimates(
|
static void ConvertPopulationCountTableToBitEstimates(
|
||||||
@ -417,40 +427,45 @@ static void ConvertPopulationCountTableToBitEstimates(
|
|||||||
|
|
||||||
static int CostModelBuild(CostModel* const m, int xsize, int ysize,
|
static int CostModelBuild(CostModel* const m, int xsize, int ysize,
|
||||||
int recursion_level, const uint32_t* const argb,
|
int recursion_level, const uint32_t* const argb,
|
||||||
int quality, int cache_bits) {
|
int quality, int cache_bits,
|
||||||
|
VP8LHashChain* const hash_chain,
|
||||||
|
VP8LBackwardRefs* const refs) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
VP8LHistogram histo;
|
VP8LHistogram* histo = NULL;
|
||||||
VP8LBackwardRefs refs;
|
|
||||||
|
|
||||||
if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize)) goto Error;
|
|
||||||
|
|
||||||
|
ClearBackwardRefs(refs);
|
||||||
if (recursion_level > 0) {
|
if (recursion_level > 0) {
|
||||||
if (!BackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1,
|
if (!BackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1,
|
||||||
argb, quality, cache_bits, &refs)) {
|
argb, quality, cache_bits, hash_chain,
|
||||||
|
refs)) {
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!BackwardReferencesHashChain(xsize, ysize, argb, cache_bits, quality,
|
if (!BackwardReferencesHashChain(xsize, ysize, argb, cache_bits, quality,
|
||||||
&refs)) {
|
hash_chain, refs)) {
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VP8LHistogramCreate(&histo, &refs, cache_bits);
|
histo = VP8LAllocateHistogram(cache_bits);
|
||||||
|
if (histo == NULL) goto Error;
|
||||||
|
|
||||||
|
VP8LHistogramCreate(histo, refs, cache_bits);
|
||||||
|
|
||||||
ConvertPopulationCountTableToBitEstimates(
|
ConvertPopulationCountTableToBitEstimates(
|
||||||
VP8LHistogramNumCodes(histo.palette_code_bits_),
|
VP8LHistogramNumCodes(histo->palette_code_bits_),
|
||||||
histo.literal_, m->literal_);
|
histo->literal_, m->literal_);
|
||||||
ConvertPopulationCountTableToBitEstimates(
|
ConvertPopulationCountTableToBitEstimates(
|
||||||
VALUES_IN_BYTE, histo.red_, m->red_);
|
VALUES_IN_BYTE, histo->red_, m->red_);
|
||||||
ConvertPopulationCountTableToBitEstimates(
|
ConvertPopulationCountTableToBitEstimates(
|
||||||
VALUES_IN_BYTE, histo.blue_, m->blue_);
|
VALUES_IN_BYTE, histo->blue_, m->blue_);
|
||||||
ConvertPopulationCountTableToBitEstimates(
|
ConvertPopulationCountTableToBitEstimates(
|
||||||
VALUES_IN_BYTE, histo.alpha_, m->alpha_);
|
VALUES_IN_BYTE, histo->alpha_, m->alpha_);
|
||||||
ConvertPopulationCountTableToBitEstimates(
|
ConvertPopulationCountTableToBitEstimates(
|
||||||
NUM_DISTANCE_CODES, histo.distance_, m->distance_);
|
NUM_DISTANCE_CODES, histo->distance_, m->distance_);
|
||||||
ok = 1;
|
ok = 1;
|
||||||
|
|
||||||
Error:
|
Error:
|
||||||
VP8LClearBackwardRefs(&refs);
|
VP8LFreeHistogram(histo);
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,7 +497,8 @@ static WEBP_INLINE double GetDistanceCost(const CostModel* const m,
|
|||||||
|
|
||||||
static int BackwardReferencesHashChainDistanceOnly(
|
static int BackwardReferencesHashChainDistanceOnly(
|
||||||
int xsize, int ysize, int recursive_cost_model, const uint32_t* const argb,
|
int xsize, int ysize, int recursive_cost_model, const uint32_t* const argb,
|
||||||
int quality, int cache_bits, uint32_t* const dist_array) {
|
int quality, int cache_bits, VP8LHashChain* const hash_chain,
|
||||||
|
VP8LBackwardRefs* const refs, uint32_t* const dist_array) {
|
||||||
int i;
|
int i;
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
int cc_init = 0;
|
int cc_init = 0;
|
||||||
@ -491,7 +507,6 @@ static int BackwardReferencesHashChainDistanceOnly(
|
|||||||
float* const cost =
|
float* const cost =
|
||||||
(float*)WebPSafeMalloc((uint64_t)pix_count, sizeof(*cost));
|
(float*)WebPSafeMalloc((uint64_t)pix_count, sizeof(*cost));
|
||||||
CostModel* cost_model = (CostModel*)WebPSafeMalloc(1ULL, sizeof(*cost_model));
|
CostModel* cost_model = (CostModel*)WebPSafeMalloc(1ULL, sizeof(*cost_model));
|
||||||
HashChain* hash_chain = HashChainNew(pix_count);
|
|
||||||
VP8LColorCache hashers;
|
VP8LColorCache hashers;
|
||||||
const double mul0 = (recursive_cost_model != 0) ? 1.0 : 0.68;
|
const double mul0 = (recursive_cost_model != 0) ? 1.0 : 0.68;
|
||||||
const double mul1 = (recursive_cost_model != 0) ? 1.0 : 0.82;
|
const double mul1 = (recursive_cost_model != 0) ? 1.0 : 0.82;
|
||||||
@ -500,7 +515,7 @@ static int BackwardReferencesHashChainDistanceOnly(
|
|||||||
int iter_pos = 1;
|
int iter_pos = 1;
|
||||||
int iter_limit = -1;
|
int iter_limit = -1;
|
||||||
|
|
||||||
if (cost == NULL || cost_model == NULL || hash_chain == NULL) goto Error;
|
if (cost == NULL || cost_model == NULL) goto Error;
|
||||||
|
|
||||||
if (use_color_cache) {
|
if (use_color_cache) {
|
||||||
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
|
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
|
||||||
@ -508,7 +523,7 @@ static int BackwardReferencesHashChainDistanceOnly(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, argb,
|
if (!CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, argb,
|
||||||
quality, cache_bits)) {
|
quality, cache_bits, hash_chain, refs)) {
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,6 +534,7 @@ static int BackwardReferencesHashChainDistanceOnly(
|
|||||||
dist_array[0] = 0;
|
dist_array[0] = 0;
|
||||||
GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
|
GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
|
||||||
&window_size, &iter_pos, &iter_limit);
|
&window_size, &iter_pos, &iter_limit);
|
||||||
|
HashChainInit(hash_chain);
|
||||||
for (i = 0; i < pix_count; ++i) {
|
for (i = 0; i < pix_count; ++i) {
|
||||||
double prev_cost = 0.0;
|
double prev_cost = 0.0;
|
||||||
int shortmax;
|
int shortmax;
|
||||||
@ -596,7 +612,6 @@ static int BackwardReferencesHashChainDistanceOnly(
|
|||||||
ok = 1;
|
ok = 1;
|
||||||
Error:
|
Error:
|
||||||
if (cc_init) VP8LColorCacheClear(&hashers);
|
if (cc_init) VP8LColorCacheClear(&hashers);
|
||||||
HashChainDelete(hash_chain);
|
|
||||||
WebPSafeFree(cost_model);
|
WebPSafeFree(cost_model);
|
||||||
WebPSafeFree(cost);
|
WebPSafeFree(cost);
|
||||||
return ok;
|
return ok;
|
||||||
@ -625,6 +640,7 @@ static int BackwardReferencesHashChainFollowChosenPath(
|
|||||||
int xsize, int ysize, const uint32_t* const argb,
|
int xsize, int ysize, const uint32_t* const argb,
|
||||||
int quality, int cache_bits,
|
int quality, int cache_bits,
|
||||||
const uint32_t* const chosen_path, int chosen_path_size,
|
const uint32_t* const chosen_path, int chosen_path_size,
|
||||||
|
VP8LHashChain* const hash_chain,
|
||||||
VP8LBackwardRefs* const refs) {
|
VP8LBackwardRefs* const refs) {
|
||||||
const int pix_count = xsize * ysize;
|
const int pix_count = xsize * ysize;
|
||||||
const int use_color_cache = (cache_bits > 0);
|
const int use_color_cache = (cache_bits > 0);
|
||||||
@ -637,19 +653,17 @@ static int BackwardReferencesHashChainFollowChosenPath(
|
|||||||
int window_size = WINDOW_SIZE;
|
int window_size = WINDOW_SIZE;
|
||||||
int iter_pos = 1;
|
int iter_pos = 1;
|
||||||
int iter_limit = -1;
|
int iter_limit = -1;
|
||||||
HashChain* hash_chain = HashChainNew(pix_count);
|
|
||||||
VP8LColorCache hashers;
|
VP8LColorCache hashers;
|
||||||
|
|
||||||
if (hash_chain == NULL) goto Error;
|
|
||||||
|
|
||||||
if (use_color_cache) {
|
if (use_color_cache) {
|
||||||
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
|
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
|
||||||
if (!cc_init) goto Error;
|
if (!cc_init) goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
refs->size = 0;
|
ClearBackwardRefs(refs);
|
||||||
GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
|
GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
|
||||||
&window_size, &iter_pos, &iter_limit);
|
&window_size, &iter_pos, &iter_limit);
|
||||||
|
HashChainInit(hash_chain);
|
||||||
for (ix = 0; ix < chosen_path_size; ++ix, ++size) {
|
for (ix = 0; ix < chosen_path_size; ++ix, ++size) {
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
@ -692,7 +706,6 @@ static int BackwardReferencesHashChainFollowChosenPath(
|
|||||||
ok = 1;
|
ok = 1;
|
||||||
Error:
|
Error:
|
||||||
if (cc_init) VP8LColorCacheClear(&hashers);
|
if (cc_init) VP8LColorCacheClear(&hashers);
|
||||||
HashChainDelete(hash_chain);
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -701,6 +714,7 @@ static int BackwardReferencesTraceBackwards(int xsize, int ysize,
|
|||||||
int recursive_cost_model,
|
int recursive_cost_model,
|
||||||
const uint32_t* const argb,
|
const uint32_t* const argb,
|
||||||
int quality, int cache_bits,
|
int quality, int cache_bits,
|
||||||
|
VP8LHashChain* const hash_chain,
|
||||||
VP8LBackwardRefs* const refs) {
|
VP8LBackwardRefs* const refs) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
const int dist_array_size = xsize * ysize;
|
const int dist_array_size = xsize * ysize;
|
||||||
@ -712,14 +726,14 @@ static int BackwardReferencesTraceBackwards(int xsize, int ysize,
|
|||||||
if (dist_array == NULL) goto Error;
|
if (dist_array == NULL) goto Error;
|
||||||
|
|
||||||
if (!BackwardReferencesHashChainDistanceOnly(
|
if (!BackwardReferencesHashChainDistanceOnly(
|
||||||
xsize, ysize, recursive_cost_model, argb, quality, cache_bits,
|
xsize, ysize, recursive_cost_model, argb, quality, cache_bits, hash_chain,
|
||||||
dist_array)) {
|
refs, dist_array)) {
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size);
|
TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size);
|
||||||
if (!BackwardReferencesHashChainFollowChosenPath(
|
if (!BackwardReferencesHashChainFollowChosenPath(
|
||||||
xsize, ysize, argb, quality, cache_bits, chosen_path, chosen_path_size,
|
xsize, ysize, argb, quality, cache_bits, chosen_path, chosen_path_size,
|
||||||
refs)) {
|
hash_chain, refs)) {
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
ok = 1;
|
ok = 1;
|
||||||
@ -740,82 +754,64 @@ static void BackwardReferences2DLocality(int xsize,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int VP8LGetBackwardReferences(int width, int height,
|
VP8LBackwardRefs* VP8LGetBackwardReferences(
|
||||||
const uint32_t* const argb,
|
int width, int height, const uint32_t* const argb, int quality,
|
||||||
int quality, int cache_bits, int use_2d_locality,
|
int cache_bits, int use_2d_locality, VP8LHashChain* const hash_chain,
|
||||||
VP8LBackwardRefs* const best) {
|
VP8LBackwardRefs* const refs_array[2]) {
|
||||||
int ok = 0;
|
|
||||||
int lz77_is_useful;
|
int lz77_is_useful;
|
||||||
VP8LBackwardRefs refs_rle, refs_lz77;
|
|
||||||
const int num_pix = width * height;
|
const int num_pix = width * height;
|
||||||
|
VP8LBackwardRefs* best = NULL;
|
||||||
|
VP8LBackwardRefs* const refs_lz77 = refs_array[0];
|
||||||
|
VP8LBackwardRefs* const refs_rle = refs_array[1];
|
||||||
|
|
||||||
VP8LBackwardRefsAlloc(&refs_rle, num_pix);
|
ClearBackwardRefs(refs_lz77);
|
||||||
VP8LBackwardRefsAlloc(&refs_lz77, num_pix);
|
|
||||||
VP8LInitBackwardRefs(best);
|
|
||||||
if (refs_rle.refs == NULL || refs_lz77.refs == NULL) {
|
|
||||||
Error1:
|
|
||||||
VP8LClearBackwardRefs(&refs_rle);
|
|
||||||
VP8LClearBackwardRefs(&refs_lz77);
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!BackwardReferencesHashChain(width, height, argb, cache_bits, quality,
|
if (!BackwardReferencesHashChain(width, height, argb, cache_bits, quality,
|
||||||
&refs_lz77)) {
|
hash_chain, refs_lz77)) {
|
||||||
goto End;
|
goto End;
|
||||||
}
|
}
|
||||||
// Backward Reference using RLE only.
|
ClearBackwardRefs(refs_rle);
|
||||||
BackwardReferencesRle(width, height, argb, &refs_rle);
|
BackwardReferencesRle(width, height, argb, refs_rle);
|
||||||
|
|
||||||
{
|
{
|
||||||
double bit_cost_lz77, bit_cost_rle;
|
double bit_cost_lz77, bit_cost_rle;
|
||||||
VP8LHistogram* const histo =
|
VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits);
|
||||||
(VP8LHistogram*)WebPSafeMalloc(1ULL, sizeof(*histo));
|
if (histo == NULL) goto End;
|
||||||
if (histo == NULL) goto Error1;
|
// 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);
|
||||||
WebPSafeFree(histo);
|
VP8LFreeHistogram(histo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choose appropriate backward reference.
|
// Choose appropriate backward reference.
|
||||||
if (lz77_is_useful) {
|
if (lz77_is_useful) {
|
||||||
// TraceBackwards is costly. Don't execute it at lower quality.
|
// TraceBackwards is costly. Don't execute it at lower quality.
|
||||||
const int try_lz77_trace_backwards = (quality >= 25);
|
const int try_lz77_trace_backwards = (quality >= 25);
|
||||||
*best = refs_lz77; // default guess: lz77 is better
|
best = refs_lz77; // default guess: lz77 is better
|
||||||
VP8LClearBackwardRefs(&refs_rle);
|
|
||||||
if (try_lz77_trace_backwards) {
|
if (try_lz77_trace_backwards) {
|
||||||
// Set recursion level for large images using a color cache.
|
// Set recursion level for large images using a color cache.
|
||||||
const int recursion_level =
|
const int recursion_level =
|
||||||
(num_pix < 320 * 200) && (cache_bits > 0) ? 1 : 0;
|
(num_pix < 320 * 200) && (cache_bits > 0) ? 1 : 0;
|
||||||
VP8LBackwardRefs refs_trace;
|
VP8LBackwardRefs* const refs_trace = refs_array[1];
|
||||||
if (!VP8LBackwardRefsAlloc(&refs_trace, num_pix)) {
|
ClearBackwardRefs(refs_trace);
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
if (BackwardReferencesTraceBackwards(width, height, recursion_level, argb,
|
if (BackwardReferencesTraceBackwards(width, height, recursion_level, argb,
|
||||||
quality, cache_bits, &refs_trace)) {
|
quality, cache_bits, hash_chain,
|
||||||
VP8LClearBackwardRefs(&refs_lz77);
|
refs_trace)) {
|
||||||
*best = refs_trace;
|
best = refs_trace;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
VP8LClearBackwardRefs(&refs_lz77);
|
best = refs_rle;
|
||||||
*best = refs_rle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_2d_locality) BackwardReferences2DLocality(width, best);
|
if (use_2d_locality) BackwardReferences2DLocality(width, best);
|
||||||
|
|
||||||
ok = 1;
|
|
||||||
|
|
||||||
End:
|
End:
|
||||||
if (!ok) {
|
return best;
|
||||||
VP8LClearBackwardRefs(best);
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns entropy for the given cache bits.
|
// Returns entropy for the given cache bits.
|
||||||
@ -828,17 +824,17 @@ static double ComputeCacheEntropy(const uint32_t* const argb,
|
|||||||
uint32_t k;
|
uint32_t k;
|
||||||
const int use_color_cache = (cache_bits > 0);
|
const int use_color_cache = (cache_bits > 0);
|
||||||
int cc_init = 0;
|
int cc_init = 0;
|
||||||
double entropy;
|
double entropy = MAX_ENTROPY;
|
||||||
const double kSmallPenaltyForLargeCache = 4.0;
|
const double kSmallPenaltyForLargeCache = 4.0;
|
||||||
VP8LColorCache hashers;
|
VP8LColorCache hashers;
|
||||||
VP8LHistogram histo;
|
VP8LHistogram* histo = VP8LAllocateHistogram(cache_bits);
|
||||||
|
if (histo == NULL) goto Error;
|
||||||
|
|
||||||
if (use_color_cache) {
|
if (use_color_cache) {
|
||||||
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
|
cc_init = VP8LColorCacheInit(&hashers, cache_bits);
|
||||||
if (!cc_init) return MAX_ENTROPY;
|
if (!cc_init) goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
VP8LHistogramInit(&histo, cache_bits);
|
|
||||||
for (i = 0; i < refs->size; ++i) {
|
for (i = 0; i < refs->size; ++i) {
|
||||||
const PixOrCopy* const v = &refs->refs[i];
|
const PixOrCopy* const v = &refs->refs[i];
|
||||||
if (PixOrCopyIsLiteral(v)) {
|
if (PixOrCopyIsLiteral(v)) {
|
||||||
@ -847,12 +843,12 @@ static double ComputeCacheEntropy(const uint32_t* const argb,
|
|||||||
// push pixel as a cache index
|
// push pixel as a cache index
|
||||||
const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]);
|
const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]);
|
||||||
const PixOrCopy token = PixOrCopyCreateCacheIdx(ix);
|
const PixOrCopy token = PixOrCopyCreateCacheIdx(ix);
|
||||||
VP8LHistogramAddSinglePixOrCopy(&histo, &token);
|
VP8LHistogramAddSinglePixOrCopy(histo, &token);
|
||||||
} else {
|
} else {
|
||||||
VP8LHistogramAddSinglePixOrCopy(&histo, v);
|
VP8LHistogramAddSinglePixOrCopy(histo, v);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
VP8LHistogramAddSinglePixOrCopy(&histo, v);
|
VP8LHistogramAddSinglePixOrCopy(histo, v);
|
||||||
}
|
}
|
||||||
if (use_color_cache) {
|
if (use_color_cache) {
|
||||||
for (k = 0; k < PixOrCopyLength(v); ++k) {
|
for (k = 0; k < PixOrCopyLength(v); ++k) {
|
||||||
@ -864,15 +860,19 @@ static double ComputeCacheEntropy(const uint32_t* const argb,
|
|||||||
assert(pixel_index == xsize * ysize);
|
assert(pixel_index == xsize * ysize);
|
||||||
(void)xsize; // xsize is not used in non-debug compilations otherwise.
|
(void)xsize; // xsize is not used in non-debug compilations otherwise.
|
||||||
(void)ysize; // ysize is not used in non-debug compilations otherwise.
|
(void)ysize; // ysize is not used in non-debug compilations otherwise.
|
||||||
entropy = VP8LHistogramEstimateBits(&histo) +
|
entropy = VP8LHistogramEstimateBits(histo) +
|
||||||
kSmallPenaltyForLargeCache * cache_bits;
|
kSmallPenaltyForLargeCache * cache_bits;
|
||||||
|
Error:
|
||||||
if (cc_init) VP8LColorCacheClear(&hashers);
|
if (cc_init) VP8LColorCacheClear(&hashers);
|
||||||
|
VP8LFreeHistogram(histo);
|
||||||
return entropy;
|
return entropy;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns how many bits are to be used for a color cache.
|
// Returns how many bits are to be used for a color cache.
|
||||||
int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
|
int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
|
||||||
int xsize, int ysize, int quality,
|
int xsize, int ysize, int quality,
|
||||||
|
VP8LHashChain* const hash_chain,
|
||||||
|
VP8LBackwardRefs* const refs,
|
||||||
int* const best_cache_bits) {
|
int* const best_cache_bits) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
int eval_low = 1;
|
int eval_low = 1;
|
||||||
@ -881,22 +881,22 @@ int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
|
|||||||
double entropy_high = MAX_ENTROPY;
|
double entropy_high = MAX_ENTROPY;
|
||||||
int cache_bits_low = 0;
|
int cache_bits_low = 0;
|
||||||
int cache_bits_high = MAX_COLOR_CACHE_BITS;
|
int cache_bits_high = MAX_COLOR_CACHE_BITS;
|
||||||
VP8LBackwardRefs refs;
|
|
||||||
|
|
||||||
if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize) ||
|
ClearBackwardRefs(refs);
|
||||||
!BackwardReferencesHashChain(xsize, ysize, argb, 0, quality, &refs)) {
|
if (!BackwardReferencesHashChain(xsize, ysize, argb, 0, quality, hash_chain,
|
||||||
|
refs)) {
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
// Do a binary search to find the optimal entropy for cache_bits.
|
// Do a binary search to find the optimal entropy for cache_bits.
|
||||||
while (cache_bits_high - cache_bits_low > 1) {
|
while (cache_bits_high - cache_bits_low > 1) {
|
||||||
if (eval_low) {
|
if (eval_low) {
|
||||||
entropy_low =
|
entropy_low =
|
||||||
ComputeCacheEntropy(argb, xsize, ysize, &refs, cache_bits_low);
|
ComputeCacheEntropy(argb, xsize, ysize, refs, cache_bits_low);
|
||||||
eval_low = 0;
|
eval_low = 0;
|
||||||
}
|
}
|
||||||
if (eval_high) {
|
if (eval_high) {
|
||||||
entropy_high =
|
entropy_high =
|
||||||
ComputeCacheEntropy(argb, xsize, ysize, &refs, cache_bits_high);
|
ComputeCacheEntropy(argb, xsize, ysize, refs, cache_bits_high);
|
||||||
eval_high = 0;
|
eval_high = 0;
|
||||||
}
|
}
|
||||||
if (entropy_high < entropy_low) {
|
if (entropy_high < entropy_low) {
|
||||||
@ -911,6 +911,5 @@ int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
|
|||||||
}
|
}
|
||||||
ok = 1;
|
ok = 1;
|
||||||
Error:
|
Error:
|
||||||
VP8LClearBackwardRefs(&refs);
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
@ -115,34 +115,59 @@ static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy* const p) {
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// VP8LBackwardRefs
|
// VP8LBackwardRefs
|
||||||
|
|
||||||
typedef struct {
|
#define HASH_BITS 18
|
||||||
|
#define HASH_SIZE (1 << HASH_BITS)
|
||||||
|
|
||||||
|
typedef struct VP8LHashChain VP8LHashChain;
|
||||||
|
struct VP8LHashChain {
|
||||||
|
// Stores the most recently added position with the given hash value.
|
||||||
|
int32_t hash_to_first_index_[HASH_SIZE];
|
||||||
|
// chain_[pos] stores the previous position with the same hash value
|
||||||
|
// for every pixel in the image.
|
||||||
|
int32_t* chain_;
|
||||||
|
// This is the maximum size of the hash_chain that can be constructed.
|
||||||
|
// Typically this is the pixel count (width x height) for a given image.
|
||||||
|
int size_;
|
||||||
|
};
|
||||||
|
|
||||||
|
VP8LHashChain* VP8LHashChainNew(int size);
|
||||||
|
void VP8LHashChainDelete(VP8LHashChain* const p);
|
||||||
|
|
||||||
|
typedef struct VP8LBackwardRefs VP8LBackwardRefs;
|
||||||
|
struct VP8LBackwardRefs {
|
||||||
PixOrCopy* refs;
|
PixOrCopy* refs;
|
||||||
int size; // currently used
|
int size; // currently used
|
||||||
int max_size; // maximum capacity
|
int max_size; // maximum capacity
|
||||||
} VP8LBackwardRefs;
|
};
|
||||||
|
|
||||||
// Initialize the object. Must be called first. 'refs' can be NULL.
|
// Release backward references. 'refs' can be NULL.
|
||||||
void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs);
|
void VP8LBackwardRefsDelete(VP8LBackwardRefs* const refs);
|
||||||
|
|
||||||
// Release memory and re-initialize the object. 'refs' can be NULL.
|
// Allocate 'max_size' references. Returns NULL in case of memory error.
|
||||||
void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs);
|
VP8LBackwardRefs* VP8LBackwardRefsNew(int max_size);
|
||||||
|
|
||||||
// Allocate 'max_size' references. Returns false in case of memory error.
|
// Copies the 'src' backward refs to the 'dst'. Returns 0 if there's mismatch
|
||||||
int VP8LBackwardRefsAlloc(VP8LBackwardRefs* const refs, int max_size);
|
// in the capacity (max_size) of 'src' and 'dst' refs.
|
||||||
|
int VP8LBackwardRefsCopy(const VP8LBackwardRefs* const src,
|
||||||
|
VP8LBackwardRefs* const dst);
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// 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.
|
// Further optimize for 2D locality if use_2d_locality flag is set.
|
||||||
int VP8LGetBackwardReferences(int width, int height,
|
// The return value is the pointer to the best of the two backward refs viz,
|
||||||
const uint32_t* const argb,
|
// refs[0] or refs[1].
|
||||||
int quality, int cache_bits, int use_2d_locality,
|
VP8LBackwardRefs* VP8LGetBackwardReferences(
|
||||||
VP8LBackwardRefs* const best);
|
int width, int height, const uint32_t* const argb, int quality,
|
||||||
|
int cache_bits, int use_2d_locality, VP8LHashChain* const hash_chain,
|
||||||
|
VP8LBackwardRefs* const refs[2]);
|
||||||
|
|
||||||
// Produce an estimate for a good color cache size for the image.
|
// Produce an estimate for a good color cache size for the image.
|
||||||
int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
|
int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
|
||||||
int xsize, int ysize, int quality,
|
int xsize, int ysize, int quality,
|
||||||
|
VP8LHashChain* const hash_chain,
|
||||||
|
VP8LBackwardRefs* const ref,
|
||||||
int* const best_cache_bits);
|
int* const best_cache_bits);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -29,12 +29,35 @@
|
|||||||
#define BIN_SIZE (NUM_PARTITIONS * NUM_PARTITIONS * NUM_PARTITIONS)
|
#define BIN_SIZE (NUM_PARTITIONS * NUM_PARTITIONS * NUM_PARTITIONS)
|
||||||
|
|
||||||
static void HistogramClear(VP8LHistogram* const p) {
|
static void HistogramClear(VP8LHistogram* const p) {
|
||||||
memset(p->literal_, 0, sizeof(p->literal_));
|
int* const literal = p->literal_;
|
||||||
memset(p->red_, 0, sizeof(p->red_));
|
const int cache_bits = p->palette_code_bits_;
|
||||||
memset(p->blue_, 0, sizeof(p->blue_));
|
const uint64_t histo_size = VP8LGetHistogramSize(cache_bits);
|
||||||
memset(p->alpha_, 0, sizeof(p->alpha_));
|
memset(p, 0, histo_size);
|
||||||
memset(p->distance_, 0, sizeof(p->distance_));
|
p->palette_code_bits_ = cache_bits;
|
||||||
p->bit_cost_ = 0;
|
p->literal_ = literal;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HistogramCopy(const VP8LHistogram* const src,
|
||||||
|
VP8LHistogram* const dst) {
|
||||||
|
int* const dst_literal = dst->literal_;
|
||||||
|
const int dst_cache_bits = dst->palette_code_bits_;
|
||||||
|
const uint64_t histo_size = VP8LGetHistogramSize(dst_cache_bits);
|
||||||
|
assert(src->palette_code_bits_ == dst_cache_bits);
|
||||||
|
memcpy(dst, src, histo_size);
|
||||||
|
dst->literal_ = dst_literal;
|
||||||
|
}
|
||||||
|
|
||||||
|
int VP8LGetHistogramSize(int cache_bits) {
|
||||||
|
const uint64_t literal_size = VP8LHistogramNumCodes(cache_bits);
|
||||||
|
return sizeof(VP8LHistogram) + sizeof(int) * literal_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VP8LFreeHistogram(VP8LHistogram* const histo) {
|
||||||
|
WebPSafeFree(histo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VP8LFreeHistogramSet(VP8LHistogramSet* const histo) {
|
||||||
|
WebPSafeFree(histo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
|
void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
|
||||||
@ -60,13 +83,24 @@ void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits) {
|
|||||||
HistogramClear(p);
|
HistogramClear(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VP8LHistogram* VP8LAllocateHistogram(int cache_bits) {
|
||||||
|
VP8LHistogram* histo = NULL;
|
||||||
|
const uint64_t total_size = VP8LGetHistogramSize(cache_bits);
|
||||||
|
uint8_t* const memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory));
|
||||||
|
if (memory == NULL) return NULL;
|
||||||
|
histo = (VP8LHistogram*)memory;
|
||||||
|
// literal_ won't necessary be aligned.
|
||||||
|
histo->literal_ = (int*)(memory + sizeof(VP8LHistogram));
|
||||||
|
VP8LHistogramInit(histo, cache_bits);
|
||||||
|
return histo;
|
||||||
|
}
|
||||||
|
|
||||||
VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) {
|
VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) {
|
||||||
int i;
|
int i;
|
||||||
VP8LHistogramSet* set;
|
VP8LHistogramSet* set;
|
||||||
VP8LHistogram* bulk;
|
|
||||||
const uint64_t total_size = sizeof(*set)
|
const uint64_t total_size = sizeof(*set)
|
||||||
+ (uint64_t)size * sizeof(*set->histograms)
|
+ (uint64_t)size * sizeof(*set->histograms)
|
||||||
+ (uint64_t)size * sizeof(**set->histograms);
|
+ (uint64_t)size * VP8LGetHistogramSize(cache_bits);
|
||||||
uint8_t* memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory));
|
uint8_t* memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory));
|
||||||
if (memory == NULL) return NULL;
|
if (memory == NULL) return NULL;
|
||||||
|
|
||||||
@ -74,12 +108,15 @@ VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) {
|
|||||||
memory += sizeof(*set);
|
memory += sizeof(*set);
|
||||||
set->histograms = (VP8LHistogram**)memory;
|
set->histograms = (VP8LHistogram**)memory;
|
||||||
memory += size * sizeof(*set->histograms);
|
memory += size * sizeof(*set->histograms);
|
||||||
bulk = (VP8LHistogram*)memory;
|
|
||||||
set->max_size = size;
|
set->max_size = size;
|
||||||
set->size = size;
|
set->size = size;
|
||||||
for (i = 0; i < size; ++i) {
|
for (i = 0; i < size; ++i) {
|
||||||
set->histograms[i] = bulk + i;
|
set->histograms[i] = (VP8LHistogram*)memory;
|
||||||
|
// literal_ won't necessary be aligned.
|
||||||
|
set->histograms[i]->literal_ = (int*)(memory + sizeof(VP8LHistogram));
|
||||||
VP8LHistogramInit(set->histograms[i], cache_bits);
|
VP8LHistogramInit(set->histograms[i], cache_bits);
|
||||||
|
// There's no padding/alignment between successive histograms.
|
||||||
|
memory += VP8LGetHistogramSize(cache_bits);
|
||||||
}
|
}
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
@ -94,12 +131,13 @@ void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
|
|||||||
++histo->literal_[PixOrCopyLiteral(v, 1)];
|
++histo->literal_[PixOrCopyLiteral(v, 1)];
|
||||||
++histo->blue_[PixOrCopyLiteral(v, 0)];
|
++histo->blue_[PixOrCopyLiteral(v, 0)];
|
||||||
} else if (PixOrCopyIsCacheIdx(v)) {
|
} else if (PixOrCopyIsCacheIdx(v)) {
|
||||||
int literal_ix = 256 + NUM_LENGTH_CODES + PixOrCopyCacheIdx(v);
|
int literal_ix =
|
||||||
|
NUM_LITERAL_CODES + NUM_LENGTH_CODES + PixOrCopyCacheIdx(v);
|
||||||
++histo->literal_[literal_ix];
|
++histo->literal_[literal_ix];
|
||||||
} else {
|
} else {
|
||||||
int code, extra_bits;
|
int code, extra_bits;
|
||||||
VP8LPrefixEncodeBits(PixOrCopyLength(v), &code, &extra_bits);
|
VP8LPrefixEncodeBits(PixOrCopyLength(v), &code, &extra_bits);
|
||||||
++histo->literal_[256 + code];
|
++histo->literal_[NUM_LITERAL_CODES + code];
|
||||||
VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code, &extra_bits);
|
VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code, &extra_bits);
|
||||||
++histo->distance_[code];
|
++histo->distance_[code];
|
||||||
}
|
}
|
||||||
@ -224,22 +262,22 @@ static double GetCombinedEntropy(const int* const X, const int* const Y,
|
|||||||
double VP8LHistogramEstimateBits(const VP8LHistogram* const p) {
|
double VP8LHistogramEstimateBits(const VP8LHistogram* const p) {
|
||||||
return
|
return
|
||||||
PopulationCost(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_))
|
PopulationCost(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_))
|
||||||
+ PopulationCost(p->red_, 256)
|
+ PopulationCost(p->red_, NUM_LITERAL_CODES)
|
||||||
+ PopulationCost(p->blue_, 256)
|
+ PopulationCost(p->blue_, NUM_LITERAL_CODES)
|
||||||
+ PopulationCost(p->alpha_, 256)
|
+ PopulationCost(p->alpha_, NUM_LITERAL_CODES)
|
||||||
+ PopulationCost(p->distance_, NUM_DISTANCE_CODES)
|
+ PopulationCost(p->distance_, NUM_DISTANCE_CODES)
|
||||||
+ VP8LExtraCost(p->literal_ + 256, NUM_LENGTH_CODES)
|
+ VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES)
|
||||||
+ VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES);
|
+ VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES);
|
||||||
}
|
}
|
||||||
|
|
||||||
double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) {
|
double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) {
|
||||||
return
|
return
|
||||||
BitsEntropy(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_))
|
BitsEntropy(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_))
|
||||||
+ BitsEntropy(p->red_, 256)
|
+ BitsEntropy(p->red_, NUM_LITERAL_CODES)
|
||||||
+ BitsEntropy(p->blue_, 256)
|
+ BitsEntropy(p->blue_, NUM_LITERAL_CODES)
|
||||||
+ BitsEntropy(p->alpha_, 256)
|
+ BitsEntropy(p->alpha_, NUM_LITERAL_CODES)
|
||||||
+ BitsEntropy(p->distance_, NUM_DISTANCE_CODES)
|
+ BitsEntropy(p->distance_, NUM_DISTANCE_CODES)
|
||||||
+ VP8LExtraCost(p->literal_ + 256, NUM_LENGTH_CODES)
|
+ VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES)
|
||||||
+ VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES);
|
+ VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,13 +288,15 @@ double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) {
|
|||||||
static void HistogramAdd(const VP8LHistogram* const in,
|
static void HistogramAdd(const VP8LHistogram* const in,
|
||||||
VP8LHistogram* const out) {
|
VP8LHistogram* const out) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) {
|
int literal_size = VP8LHistogramNumCodes(out->palette_code_bits_);
|
||||||
|
assert(in->palette_code_bits_ == out->palette_code_bits_);
|
||||||
|
for (i = 0; i < literal_size; ++i) {
|
||||||
out->literal_[i] += in->literal_[i];
|
out->literal_[i] += in->literal_[i];
|
||||||
}
|
}
|
||||||
for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
|
for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
|
||||||
out->distance_[i] += in->distance_[i];
|
out->distance_[i] += in->distance_[i];
|
||||||
}
|
}
|
||||||
for (i = 0; i < 256; ++i) {
|
for (i = 0; i < NUM_LITERAL_CODES; ++i) {
|
||||||
out->red_[i] += in->red_[i];
|
out->red_[i] += in->red_[i];
|
||||||
out->blue_[i] += in->blue_[i];
|
out->blue_[i] += in->blue_[i];
|
||||||
out->alpha_[i] += in->alpha_[i];
|
out->alpha_[i] += in->alpha_[i];
|
||||||
@ -267,22 +307,22 @@ static int GetCombinedHistogramEntropy(const VP8LHistogram* const a,
|
|||||||
const VP8LHistogram* const b,
|
const VP8LHistogram* const b,
|
||||||
double cost_threshold,
|
double cost_threshold,
|
||||||
double* cost) {
|
double* cost) {
|
||||||
const int palette_code_bits =
|
const int palette_code_bits = a->palette_code_bits_;
|
||||||
(a->palette_code_bits_ > b->palette_code_bits_) ? a->palette_code_bits_ :
|
assert(a->palette_code_bits_ == b->palette_code_bits_);
|
||||||
b->palette_code_bits_;
|
|
||||||
*cost += GetCombinedEntropy(a->literal_, b->literal_,
|
*cost += GetCombinedEntropy(a->literal_, b->literal_,
|
||||||
VP8LHistogramNumCodes(palette_code_bits));
|
VP8LHistogramNumCodes(palette_code_bits));
|
||||||
*cost += VP8LExtraCostCombined(a->literal_ + 256, b->literal_ + 256,
|
*cost += VP8LExtraCostCombined(a->literal_ + NUM_LITERAL_CODES,
|
||||||
|
b->literal_ + NUM_LITERAL_CODES,
|
||||||
NUM_LENGTH_CODES);
|
NUM_LENGTH_CODES);
|
||||||
if (*cost > cost_threshold) return 0;
|
if (*cost > cost_threshold) return 0;
|
||||||
|
|
||||||
*cost += GetCombinedEntropy(a->red_, b->red_, 256);
|
*cost += GetCombinedEntropy(a->red_, b->red_, NUM_LITERAL_CODES);
|
||||||
if (*cost > cost_threshold) return 0;
|
if (*cost > cost_threshold) return 0;
|
||||||
|
|
||||||
*cost += GetCombinedEntropy(a->blue_, b->blue_, 256);
|
*cost += GetCombinedEntropy(a->blue_, b->blue_, NUM_LITERAL_CODES);
|
||||||
if (*cost > cost_threshold) return 0;
|
if (*cost > cost_threshold) return 0;
|
||||||
|
|
||||||
*cost += GetCombinedEntropy(a->alpha_, b->alpha_, 256);
|
*cost += GetCombinedEntropy(a->alpha_, b->alpha_, NUM_LITERAL_CODES);
|
||||||
if (*cost > cost_threshold) return 0;
|
if (*cost > cost_threshold) return 0;
|
||||||
|
|
||||||
*cost += GetCombinedEntropy(a->distance_, b->distance_, NUM_DISTANCE_CODES);
|
*cost += GetCombinedEntropy(a->distance_, b->distance_, NUM_DISTANCE_CODES);
|
||||||
@ -306,22 +346,24 @@ static double HistogramAddEval(const VP8LHistogram* const a,
|
|||||||
double cost = 0;
|
double cost = 0;
|
||||||
const double sum_cost = a->bit_cost_ + b->bit_cost_;
|
const double sum_cost = a->bit_cost_ + b->bit_cost_;
|
||||||
int i;
|
int i;
|
||||||
|
assert(a->palette_code_bits_ == b->palette_code_bits_);
|
||||||
cost_threshold += sum_cost;
|
cost_threshold += sum_cost;
|
||||||
|
|
||||||
if (GetCombinedHistogramEntropy(a, b, cost_threshold, &cost)) {
|
if (GetCombinedHistogramEntropy(a, b, cost_threshold, &cost)) {
|
||||||
for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) {
|
int literal_size;
|
||||||
|
out->palette_code_bits_ = a->palette_code_bits_;
|
||||||
|
literal_size = VP8LHistogramNumCodes(out->palette_code_bits_);
|
||||||
|
for (i = 0; i < literal_size; ++i) {
|
||||||
out->literal_[i] = a->literal_[i] + b->literal_[i];
|
out->literal_[i] = a->literal_[i] + b->literal_[i];
|
||||||
}
|
}
|
||||||
for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
|
for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
|
||||||
out->distance_[i] = a->distance_[i] + b->distance_[i];
|
out->distance_[i] = a->distance_[i] + b->distance_[i];
|
||||||
}
|
}
|
||||||
for (i = 0; i < 256; ++i) {
|
for (i = 0; i < NUM_LITERAL_CODES; ++i) {
|
||||||
out->red_[i] = a->red_[i] + b->red_[i];
|
out->red_[i] = a->red_[i] + b->red_[i];
|
||||||
out->blue_[i] = a->blue_[i] + b->blue_[i];
|
out->blue_[i] = a->blue_[i] + b->blue_[i];
|
||||||
out->alpha_[i] = a->alpha_[i] + b->alpha_[i];
|
out->alpha_[i] = a->alpha_[i] + b->alpha_[i];
|
||||||
}
|
}
|
||||||
out->palette_code_bits_ = (a->palette_code_bits_ > b->palette_code_bits_) ?
|
|
||||||
a->palette_code_bits_ : b->palette_code_bits_;
|
|
||||||
out->bit_cost_ = cost;
|
out->bit_cost_ = cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,15 +416,16 @@ static void UpdateDominantCostRange(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void UpdateHistogramCost(VP8LHistogram* const h) {
|
static void UpdateHistogramCost(VP8LHistogram* const h) {
|
||||||
const double alpha_cost = PopulationCost(h->alpha_, 256);
|
const double alpha_cost = PopulationCost(h->alpha_, NUM_LITERAL_CODES);
|
||||||
const double distance_cost =
|
const double distance_cost =
|
||||||
PopulationCost(h->distance_, NUM_DISTANCE_CODES) +
|
PopulationCost(h->distance_, NUM_DISTANCE_CODES) +
|
||||||
VP8LExtraCost(h->distance_, NUM_DISTANCE_CODES);
|
VP8LExtraCost(h->distance_, NUM_DISTANCE_CODES);
|
||||||
const int num_codes = VP8LHistogramNumCodes(h->palette_code_bits_);
|
const int num_codes = VP8LHistogramNumCodes(h->palette_code_bits_);
|
||||||
h->literal_cost_ = PopulationCost(h->literal_, num_codes) +
|
h->literal_cost_ = PopulationCost(h->literal_, num_codes) +
|
||||||
VP8LExtraCost(h->literal_ + 256, NUM_LENGTH_CODES);
|
VP8LExtraCost(h->literal_ + NUM_LITERAL_CODES,
|
||||||
h->red_cost_ = PopulationCost(h->red_, 256);
|
NUM_LENGTH_CODES);
|
||||||
h->blue_cost_ = PopulationCost(h->blue_, 256);
|
h->red_cost_ = PopulationCost(h->red_, NUM_LITERAL_CODES);
|
||||||
|
h->blue_cost_ = PopulationCost(h->blue_, NUM_LITERAL_CODES);
|
||||||
h->bit_cost_ = h->literal_cost_ + h->red_cost_ + h->blue_cost_ +
|
h->bit_cost_ = h->literal_cost_ + h->red_cost_ + h->blue_cost_ +
|
||||||
alpha_cost + distance_cost;
|
alpha_cost + distance_cost;
|
||||||
}
|
}
|
||||||
@ -439,7 +482,7 @@ static void HistogramAnalyze(
|
|||||||
VP8LHistogram* const histo = histograms[i];
|
VP8LHistogram* const histo = histograms[i];
|
||||||
histo->bit_cost_ = VP8LHistogramEstimateBits(histo);
|
histo->bit_cost_ = VP8LHistogramEstimateBits(histo);
|
||||||
// Copy histograms from init_histo[] to histo_image[].
|
// Copy histograms from init_histo[] to histo_image[].
|
||||||
*histo_image->histograms[i] = *histo;
|
HistogramCopy(histo, histo_image->histograms[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,7 +503,7 @@ static void HistogramAnalyzeBin(
|
|||||||
VP8LHistogram* const histo = histograms[i];
|
VP8LHistogram* const histo = histograms[i];
|
||||||
UpdateHistogramCost(histo);
|
UpdateHistogramCost(histo);
|
||||||
// Copy histograms from init_histo[] to histo_image[].
|
// Copy histograms from init_histo[] to histo_image[].
|
||||||
*histo_image->histograms[i] = *histo;
|
HistogramCopy(histo, histo_image->histograms[i]);
|
||||||
UpdateDominantCostRange(histo, &cost_range);
|
UpdateDominantCostRange(histo, &cost_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,7 +546,8 @@ static void HistogramCompactBins(VP8LHistogramSet* const histo_image) {
|
|||||||
if (start < end) {
|
if (start < end) {
|
||||||
assert(histo_image->histograms[start] != NULL);
|
assert(histo_image->histograms[start] != NULL);
|
||||||
assert(histo_image->histograms[end] != NULL);
|
assert(histo_image->histograms[end] != NULL);
|
||||||
*histo_image->histograms[start] = *histo_image->histograms[end];
|
HistogramCopy(histo_image->histograms[end],
|
||||||
|
histo_image->histograms[start]);
|
||||||
histo_image->histograms[end] = NULL;
|
histo_image->histograms[end] = NULL;
|
||||||
--end;
|
--end;
|
||||||
}
|
}
|
||||||
@ -533,7 +577,7 @@ static void HistogramCombineBin(VP8LHistogramSet* const histo_image,
|
|||||||
histo_image->histograms[idx2],
|
histo_image->histograms[idx2],
|
||||||
cur_combo, bit_cost_thresh);
|
cur_combo, bit_cost_thresh);
|
||||||
if (curr_cost_diff < bit_cost_thresh) {
|
if (curr_cost_diff < bit_cost_thresh) {
|
||||||
*histo_image->histograms[idx1] = *cur_combo;
|
HistogramCopy(cur_combo, histo_image->histograms[idx1]);
|
||||||
histo_image->histograms[idx2]->bit_cost_ = 0.;
|
histo_image->histograms[idx2]->bit_cost_ = 0.;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -551,7 +595,7 @@ static uint32_t MyRand(uint32_t *seed) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void HistogramCombine(VP8LHistogramSet* const histo_image,
|
static void HistogramCombine(VP8LHistogramSet* const histo_image,
|
||||||
VP8LHistogram* const histos, int quality) {
|
VP8LHistogramSet* const histos, int quality) {
|
||||||
int iter;
|
int iter;
|
||||||
uint32_t seed = 0;
|
uint32_t seed = 0;
|
||||||
int tries_with_no_success = 0;
|
int tries_with_no_success = 0;
|
||||||
@ -561,8 +605,8 @@ static void HistogramCombine(VP8LHistogramSet* const histo_image,
|
|||||||
const int num_pairs = histo_image_size / 2;
|
const int num_pairs = histo_image_size / 2;
|
||||||
const int num_tries_no_success = outer_iters / 2;
|
const int num_tries_no_success = outer_iters / 2;
|
||||||
const int min_cluster_size = 2;
|
const int min_cluster_size = 2;
|
||||||
VP8LHistogram* cur_combo = histos + 0; // trial merged histogram
|
VP8LHistogram* cur_combo = histos->histograms[0]; // trial histogram
|
||||||
VP8LHistogram* best_combo = histos + 1; // best merged histogram so far
|
VP8LHistogram* best_combo = histos->histograms[1]; // best histogram so far
|
||||||
|
|
||||||
// Collapse similar histograms in 'histo_image'.
|
// Collapse similar histograms in 'histo_image'.
|
||||||
for (iter = 0;
|
for (iter = 0;
|
||||||
@ -603,12 +647,12 @@ static void HistogramCombine(VP8LHistogramSet* const histo_image,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (best_idx1 >= 0) {
|
if (best_idx1 >= 0) {
|
||||||
*histo_image->histograms[best_idx1] = *best_combo;
|
HistogramCopy(best_combo, histo_image->histograms[best_idx1]);
|
||||||
// swap best_idx2 slot with last one (which is now unused)
|
// swap best_idx2 slot with last one (which is now unused)
|
||||||
--histo_image_size;
|
--histo_image_size;
|
||||||
if (best_idx2 != histo_image_size) {
|
if (best_idx2 != histo_image_size) {
|
||||||
histo_image->histograms[best_idx2] =
|
HistogramCopy(histo_image->histograms[histo_image_size],
|
||||||
histo_image->histograms[histo_image_size];
|
histo_image->histograms[best_idx2]);
|
||||||
histo_image->histograms[histo_image_size] = NULL;
|
histo_image->histograms[histo_image_size] = NULL;
|
||||||
}
|
}
|
||||||
tries_with_no_success = 0;
|
tries_with_no_success = 0;
|
||||||
@ -683,8 +727,7 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
|
|||||||
// bin_map[n][num_histo + 1] ... bin_map[n][bin_depth - 1] = un-used indices.
|
// bin_map[n][num_histo + 1] ... bin_map[n][bin_depth - 1] = un-used indices.
|
||||||
const int bin_depth = histo_image_raw_size + 1;
|
const int bin_depth = histo_image_raw_size + 1;
|
||||||
int16_t* bin_map = NULL;
|
int16_t* bin_map = NULL;
|
||||||
VP8LHistogram* const histos =
|
VP8LHistogramSet* const histos = VP8LAllocateHistogramSet(2, cache_bits);
|
||||||
(VP8LHistogram*)WebPSafeMalloc(2ULL, sizeof(*histos));
|
|
||||||
VP8LHistogramSet* const init_histo =
|
VP8LHistogramSet* const init_histo =
|
||||||
VP8LAllocateHistogramSet(histo_image_raw_size, cache_bits);
|
VP8LAllocateHistogramSet(histo_image_raw_size, cache_bits);
|
||||||
|
|
||||||
@ -709,8 +752,8 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
|
|||||||
const double combine_cost_factor =
|
const double combine_cost_factor =
|
||||||
GetCombineCostFactor(histo_image_raw_size, quality);
|
GetCombineCostFactor(histo_image_raw_size, quality);
|
||||||
HistogramAnalyzeBin(init_histo, histo_image, bin_map);
|
HistogramAnalyzeBin(init_histo, histo_image, bin_map);
|
||||||
HistogramCombineBin(histo_image, histos, bin_depth, combine_cost_factor,
|
HistogramCombineBin(histo_image, histos->histograms[0],
|
||||||
bin_map);
|
bin_depth, combine_cost_factor, bin_map);
|
||||||
} else {
|
} else {
|
||||||
HistogramAnalyze(init_histo, histo_image);
|
HistogramAnalyze(init_histo, histo_image);
|
||||||
}
|
}
|
||||||
@ -725,7 +768,7 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
|
|||||||
|
|
||||||
Error:
|
Error:
|
||||||
WebPSafeFree(bin_map);
|
WebPSafeFree(bin_map);
|
||||||
WebPSafeFree(init_histo);
|
VP8LFreeHistogramSet(init_histo);
|
||||||
WebPSafeFree(histos);
|
VP8LFreeHistogramSet(histos);
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ extern "C" {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
// literal_ contains green literal, palette-code and
|
// literal_ contains green literal, palette-code and
|
||||||
// copy-length-prefix histogram
|
// copy-length-prefix histogram
|
||||||
int literal_[PIX_OR_COPY_CODES_MAX];
|
int* literal_; // Pointer to the allocated buffer for literal.
|
||||||
int red_[256];
|
int red_[256];
|
||||||
int blue_[256];
|
int blue_[256];
|
||||||
int alpha_[256];
|
int alpha_[256];
|
||||||
@ -62,6 +62,9 @@ void VP8LHistogramCreate(VP8LHistogram* const p,
|
|||||||
const VP8LBackwardRefs* const refs,
|
const VP8LBackwardRefs* const refs,
|
||||||
int palette_code_bits);
|
int palette_code_bits);
|
||||||
|
|
||||||
|
// Return the size of the histogram for a given palette_code_bits.
|
||||||
|
int VP8LGetHistogramSize(int palette_code_bits);
|
||||||
|
|
||||||
// Set the palette_code_bits and reset the stats.
|
// Set the palette_code_bits and reset the stats.
|
||||||
void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits);
|
void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits);
|
||||||
|
|
||||||
@ -69,10 +72,21 @@ void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits);
|
|||||||
void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
|
void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
|
||||||
VP8LHistogram* const histo);
|
VP8LHistogram* const histo);
|
||||||
|
|
||||||
|
// Free the memory allocated for the histogram.
|
||||||
|
void VP8LFreeHistogram(VP8LHistogram* const histo);
|
||||||
|
|
||||||
|
// Free the memory allocated for the histogram set.
|
||||||
|
void VP8LFreeHistogramSet(VP8LHistogramSet* const histo);
|
||||||
|
|
||||||
// Allocate an array of pointer to histograms, allocated and initialized
|
// Allocate an array of pointer to histograms, allocated and initialized
|
||||||
// using 'cache_bits'. Return NULL in case of memory error.
|
// using 'cache_bits'. Return NULL in case of memory error.
|
||||||
VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits);
|
VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits);
|
||||||
|
|
||||||
|
// Allocate and initialize histogram object with specified 'cache_bits'.
|
||||||
|
// Returns NULL in case of memory error.
|
||||||
|
// Special case of VP8LAllocateHistogramSet, with size equals 1.
|
||||||
|
VP8LHistogram* VP8LAllocateHistogram(int cache_bits);
|
||||||
|
|
||||||
// Accumulate a token 'v' into a histogram.
|
// Accumulate a token 'v' into a histogram.
|
||||||
void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
|
void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
|
||||||
const PixOrCopy* const v);
|
const PixOrCopy* const v);
|
||||||
@ -86,7 +100,7 @@ double VP8LHistogramEstimateBits(const VP8LHistogram* const p);
|
|||||||
double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p);
|
double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p);
|
||||||
|
|
||||||
static WEBP_INLINE int VP8LHistogramNumCodes(int palette_code_bits) {
|
static WEBP_INLINE int VP8LHistogramNumCodes(int palette_code_bits) {
|
||||||
return 256 + NUM_LENGTH_CODES +
|
return NUM_LITERAL_CODES + NUM_LENGTH_CODES +
|
||||||
((palette_code_bits > 0) ? (1 << palette_code_bits) : 0);
|
((palette_code_bits > 0) ? (1 << palette_code_bits) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
238
src/enc/vp8l.c
238
src/enc/vp8l.c
@ -106,14 +106,9 @@ static int AnalyzeEntropy(const uint32_t* argb,
|
|||||||
const uint32_t* last_line = NULL;
|
const uint32_t* last_line = NULL;
|
||||||
uint32_t last_pix = argb[0]; // so we're sure that pix_diff == 0
|
uint32_t last_pix = argb[0]; // so we're sure that pix_diff == 0
|
||||||
|
|
||||||
VP8LHistogram* nonpredicted = NULL;
|
VP8LHistogramSet* const histo_set = VP8LAllocateHistogramSet(2, 0);
|
||||||
VP8LHistogram* predicted =
|
if (histo_set == NULL) return 0;
|
||||||
(VP8LHistogram*)WebPSafeMalloc(2ULL, sizeof(*predicted));
|
|
||||||
if (predicted == NULL) return 0;
|
|
||||||
nonpredicted = predicted + 1;
|
|
||||||
|
|
||||||
VP8LHistogramInit(predicted, 0);
|
|
||||||
VP8LHistogramInit(nonpredicted, 0);
|
|
||||||
for (y = 0; y < height; ++y) {
|
for (y = 0; y < height; ++y) {
|
||||||
for (x = 0; x < width; ++x) {
|
for (x = 0; x < width; ++x) {
|
||||||
const uint32_t pix = argb[x];
|
const uint32_t pix = argb[x];
|
||||||
@ -126,21 +121,25 @@ static int AnalyzeEntropy(const uint32_t* argb,
|
|||||||
{
|
{
|
||||||
const PixOrCopy pix_token = PixOrCopyCreateLiteral(pix);
|
const PixOrCopy pix_token = PixOrCopyCreateLiteral(pix);
|
||||||
const PixOrCopy pix_diff_token = PixOrCopyCreateLiteral(pix_diff);
|
const PixOrCopy pix_diff_token = PixOrCopyCreateLiteral(pix_diff);
|
||||||
VP8LHistogramAddSinglePixOrCopy(nonpredicted, &pix_token);
|
VP8LHistogramAddSinglePixOrCopy(histo_set->histograms[0], &pix_token);
|
||||||
VP8LHistogramAddSinglePixOrCopy(predicted, &pix_diff_token);
|
VP8LHistogramAddSinglePixOrCopy(histo_set->histograms[1],
|
||||||
|
&pix_diff_token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
last_line = argb;
|
last_line = argb;
|
||||||
argb += argb_stride;
|
argb += argb_stride;
|
||||||
}
|
}
|
||||||
*nonpredicted_bits = VP8LHistogramEstimateBitsBulk(nonpredicted);
|
*nonpredicted_bits = VP8LHistogramEstimateBitsBulk(histo_set->histograms[0]);
|
||||||
*predicted_bits = VP8LHistogramEstimateBitsBulk(predicted);
|
*predicted_bits = VP8LHistogramEstimateBitsBulk(histo_set->histograms[1]);
|
||||||
WebPSafeFree(predicted);
|
VP8LFreeHistogramSet(histo_set);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int VP8LEncAnalyze(VP8LEncoder* const enc, WebPImageHint image_hint) {
|
static int AnalyzeAndInit(VP8LEncoder* const enc, WebPImageHint image_hint) {
|
||||||
const WebPPicture* const pic = enc->pic_;
|
const WebPPicture* const pic = enc->pic_;
|
||||||
|
const int width = pic->width;
|
||||||
|
const int height = pic->height;
|
||||||
|
const int pix_cnt = width * height;
|
||||||
assert(pic != NULL && pic->argb != NULL);
|
assert(pic != NULL && pic->argb != NULL);
|
||||||
|
|
||||||
enc->use_palette_ =
|
enc->use_palette_ =
|
||||||
@ -158,7 +157,7 @@ static int VP8LEncAnalyze(VP8LEncoder* const enc, WebPImageHint image_hint) {
|
|||||||
enc->use_cross_color_ = 1;
|
enc->use_cross_color_ = 1;
|
||||||
} else {
|
} else {
|
||||||
double non_pred_entropy, pred_entropy;
|
double non_pred_entropy, pred_entropy;
|
||||||
if (!AnalyzeEntropy(pic->argb, pic->width, pic->height, pic->argb_stride,
|
if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride,
|
||||||
&non_pred_entropy, &pred_entropy)) {
|
&non_pred_entropy, &pred_entropy)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -168,6 +167,14 @@ static int VP8LEncAnalyze(VP8LEncoder* const enc, WebPImageHint image_hint) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
enc->hash_chain_ = VP8LHashChainNew(pix_cnt);
|
||||||
|
if (enc->hash_chain_ == NULL) return 0;
|
||||||
|
|
||||||
|
enc->refs_[0] = VP8LBackwardRefsNew(pix_cnt);
|
||||||
|
enc->refs_[1] = VP8LBackwardRefsNew(pix_cnt);
|
||||||
|
if (enc->refs_[0] == NULL || enc->refs_[1] == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -176,10 +183,13 @@ static int GetHuffBitLengthsAndCodes(
|
|||||||
const VP8LHistogramSet* const histogram_image,
|
const VP8LHistogramSet* const histogram_image,
|
||||||
HuffmanTreeCode* const huffman_codes) {
|
HuffmanTreeCode* const huffman_codes) {
|
||||||
int i, k;
|
int i, k;
|
||||||
int ok = 1;
|
int ok = 0;
|
||||||
uint64_t total_length_size = 0;
|
uint64_t total_length_size = 0;
|
||||||
uint8_t* mem_buf = NULL;
|
uint8_t* mem_buf = NULL;
|
||||||
const int histogram_image_size = histogram_image->size;
|
const int histogram_image_size = histogram_image->size;
|
||||||
|
int max_num_symbols = 0;
|
||||||
|
uint8_t* buf_rle = NULL;
|
||||||
|
HuffmanTree* huff_tree = NULL;
|
||||||
|
|
||||||
// Iterate over all histograms and get the aggregate number of codes used.
|
// Iterate over all histograms and get the aggregate number of codes used.
|
||||||
for (i = 0; i < histogram_image_size; ++i) {
|
for (i = 0; i < histogram_image_size; ++i) {
|
||||||
@ -200,10 +210,8 @@ static int GetHuffBitLengthsAndCodes(
|
|||||||
uint8_t* lengths;
|
uint8_t* lengths;
|
||||||
mem_buf = (uint8_t*)WebPSafeCalloc(total_length_size,
|
mem_buf = (uint8_t*)WebPSafeCalloc(total_length_size,
|
||||||
sizeof(*lengths) + sizeof(*codes));
|
sizeof(*lengths) + sizeof(*codes));
|
||||||
if (mem_buf == NULL) {
|
if (mem_buf == NULL) goto End;
|
||||||
ok = 0;
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
codes = (uint16_t*)mem_buf;
|
codes = (uint16_t*)mem_buf;
|
||||||
lengths = (uint8_t*)&codes[total_length_size];
|
lengths = (uint8_t*)&codes[total_length_size];
|
||||||
for (i = 0; i < 5 * histogram_image_size; ++i) {
|
for (i = 0; i < 5 * histogram_image_size; ++i) {
|
||||||
@ -212,24 +220,33 @@ static int GetHuffBitLengthsAndCodes(
|
|||||||
huffman_codes[i].code_lengths = lengths;
|
huffman_codes[i].code_lengths = lengths;
|
||||||
codes += bit_length;
|
codes += bit_length;
|
||||||
lengths += bit_length;
|
lengths += bit_length;
|
||||||
|
if (max_num_symbols < bit_length) {
|
||||||
|
max_num_symbols = bit_length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_rle = (uint8_t*)WebPSafeMalloc(1ULL, max_num_symbols);
|
||||||
|
huff_tree = (HuffmanTree*)WebPSafeMalloc(3ULL * max_num_symbols,
|
||||||
|
sizeof(*huff_tree));
|
||||||
|
if (buf_rle == NULL || huff_tree == NULL) goto End;
|
||||||
|
|
||||||
// Create Huffman trees.
|
// Create Huffman trees.
|
||||||
for (i = 0; ok && (i < histogram_image_size); ++i) {
|
for (i = 0; i < histogram_image_size; ++i) {
|
||||||
HuffmanTreeCode* const codes = &huffman_codes[5 * i];
|
HuffmanTreeCode* const codes = &huffman_codes[5 * i];
|
||||||
VP8LHistogram* const histo = histogram_image->histograms[i];
|
VP8LHistogram* const histo = histogram_image->histograms[i];
|
||||||
ok = ok && VP8LCreateHuffmanTree(histo->literal_, 15, codes + 0);
|
VP8LCreateHuffmanTree(histo->literal_, 15, buf_rle, huff_tree, codes + 0);
|
||||||
ok = ok && VP8LCreateHuffmanTree(histo->red_, 15, codes + 1);
|
VP8LCreateHuffmanTree(histo->red_, 15, buf_rle, huff_tree, codes + 1);
|
||||||
ok = ok && VP8LCreateHuffmanTree(histo->blue_, 15, codes + 2);
|
VP8LCreateHuffmanTree(histo->blue_, 15, buf_rle, huff_tree, codes + 2);
|
||||||
ok = ok && VP8LCreateHuffmanTree(histo->alpha_, 15, codes + 3);
|
VP8LCreateHuffmanTree(histo->alpha_, 15, buf_rle, huff_tree, codes + 3);
|
||||||
ok = ok && VP8LCreateHuffmanTree(histo->distance_, 15, codes + 4);
|
VP8LCreateHuffmanTree(histo->distance_, 15, buf_rle, huff_tree, codes + 4);
|
||||||
}
|
}
|
||||||
|
ok = 1;
|
||||||
End:
|
End:
|
||||||
|
WebPSafeFree(huff_tree);
|
||||||
|
WebPSafeFree(buf_rle);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
WebPSafeFree(mem_buf);
|
WebPSafeFree(mem_buf);
|
||||||
// If one VP8LCreateHuffmanTree() above fails, we need to clean up behind.
|
|
||||||
memset(huffman_codes, 0, 5 * histogram_image_size * sizeof(*huffman_codes));
|
memset(huffman_codes, 0, 5 * histogram_image_size * sizeof(*huffman_codes));
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
@ -296,18 +313,16 @@ static void StoreHuffmanTreeToBitMask(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int StoreFullHuffmanCode(VP8LBitWriter* const bw,
|
// 'huff_tree' and 'tokens' are pre-alloacted buffers.
|
||||||
|
static void StoreFullHuffmanCode(VP8LBitWriter* const bw,
|
||||||
|
HuffmanTree* const huff_tree,
|
||||||
|
HuffmanTreeToken* const tokens,
|
||||||
const HuffmanTreeCode* const tree) {
|
const HuffmanTreeCode* const tree) {
|
||||||
int ok = 0;
|
|
||||||
uint8_t code_length_bitdepth[CODE_LENGTH_CODES] = { 0 };
|
uint8_t code_length_bitdepth[CODE_LENGTH_CODES] = { 0 };
|
||||||
uint16_t code_length_bitdepth_symbols[CODE_LENGTH_CODES] = { 0 };
|
uint16_t code_length_bitdepth_symbols[CODE_LENGTH_CODES] = { 0 };
|
||||||
const int max_tokens = tree->num_symbols;
|
const int max_tokens = tree->num_symbols;
|
||||||
int num_tokens;
|
int num_tokens;
|
||||||
HuffmanTreeCode huffman_code;
|
HuffmanTreeCode huffman_code;
|
||||||
HuffmanTreeToken* const tokens =
|
|
||||||
(HuffmanTreeToken*)WebPSafeMalloc((uint64_t)max_tokens, sizeof(*tokens));
|
|
||||||
if (tokens == NULL) return 0;
|
|
||||||
|
|
||||||
huffman_code.num_symbols = CODE_LENGTH_CODES;
|
huffman_code.num_symbols = CODE_LENGTH_CODES;
|
||||||
huffman_code.code_lengths = code_length_bitdepth;
|
huffman_code.code_lengths = code_length_bitdepth;
|
||||||
huffman_code.codes = code_length_bitdepth_symbols;
|
huffman_code.codes = code_length_bitdepth_symbols;
|
||||||
@ -316,14 +331,13 @@ static int StoreFullHuffmanCode(VP8LBitWriter* const bw,
|
|||||||
num_tokens = VP8LCreateCompressedHuffmanTree(tree, tokens, max_tokens);
|
num_tokens = VP8LCreateCompressedHuffmanTree(tree, tokens, max_tokens);
|
||||||
{
|
{
|
||||||
int histogram[CODE_LENGTH_CODES] = { 0 };
|
int histogram[CODE_LENGTH_CODES] = { 0 };
|
||||||
|
uint8_t buf_rle[CODE_LENGTH_CODES] = { 0 };
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < num_tokens; ++i) {
|
for (i = 0; i < num_tokens; ++i) {
|
||||||
++histogram[tokens[i].code];
|
++histogram[tokens[i].code];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!VP8LCreateHuffmanTree(histogram, 7, &huffman_code)) {
|
VP8LCreateHuffmanTree(histogram, 7, buf_rle, huff_tree, &huffman_code);
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StoreHuffmanTreeOfHuffmanTreeToBitMask(bw, code_length_bitdepth);
|
StoreHuffmanTreeOfHuffmanTreeToBitMask(bw, code_length_bitdepth);
|
||||||
@ -360,13 +374,12 @@ static int StoreFullHuffmanCode(VP8LBitWriter* const bw,
|
|||||||
}
|
}
|
||||||
StoreHuffmanTreeToBitMask(bw, tokens, length, &huffman_code);
|
StoreHuffmanTreeToBitMask(bw, tokens, length, &huffman_code);
|
||||||
}
|
}
|
||||||
ok = 1;
|
|
||||||
End:
|
|
||||||
WebPSafeFree(tokens);
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int StoreHuffmanCode(VP8LBitWriter* const bw,
|
// 'huff_tree' and 'tokens' are pre-alloacted buffers.
|
||||||
|
static void StoreHuffmanCode(VP8LBitWriter* const bw,
|
||||||
|
HuffmanTree* const huff_tree,
|
||||||
|
HuffmanTreeToken* const tokens,
|
||||||
const HuffmanTreeCode* const huffman_code) {
|
const HuffmanTreeCode* const huffman_code) {
|
||||||
int i;
|
int i;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@ -385,7 +398,6 @@ static int StoreHuffmanCode(VP8LBitWriter* const bw,
|
|||||||
if (count == 0) { // emit minimal tree for empty cases
|
if (count == 0) { // emit minimal tree for empty cases
|
||||||
// bits: small tree marker: 1, count-1: 0, large 8-bit code: 0, code: 0
|
// bits: small tree marker: 1, count-1: 0, large 8-bit code: 0, code: 0
|
||||||
VP8LWriteBits(bw, 4, 0x01);
|
VP8LWriteBits(bw, 4, 0x01);
|
||||||
return 1;
|
|
||||||
} else if (count <= 2 && symbols[0] < kMaxSymbol && symbols[1] < kMaxSymbol) {
|
} else if (count <= 2 && symbols[0] < kMaxSymbol && symbols[1] < kMaxSymbol) {
|
||||||
VP8LWriteBits(bw, 1, 1); // Small tree marker to encode 1 or 2 symbols.
|
VP8LWriteBits(bw, 1, 1); // Small tree marker to encode 1 or 2 symbols.
|
||||||
VP8LWriteBits(bw, 1, count - 1);
|
VP8LWriteBits(bw, 1, count - 1);
|
||||||
@ -399,9 +411,8 @@ static int StoreHuffmanCode(VP8LBitWriter* const bw,
|
|||||||
if (count == 2) {
|
if (count == 2) {
|
||||||
VP8LWriteBits(bw, 8, symbols[1]);
|
VP8LWriteBits(bw, 8, symbols[1]);
|
||||||
}
|
}
|
||||||
return 1;
|
|
||||||
} else {
|
} else {
|
||||||
return StoreFullHuffmanCode(bw, huffman_code);
|
StoreFullHuffmanCode(bw, huff_tree, tokens, huffman_code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,21 +475,29 @@ static void StoreImageToBitMask(
|
|||||||
// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31
|
// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31
|
||||||
static int EncodeImageNoHuffman(VP8LBitWriter* const bw,
|
static int EncodeImageNoHuffman(VP8LBitWriter* const bw,
|
||||||
const uint32_t* const argb,
|
const uint32_t* const argb,
|
||||||
|
VP8LHashChain* const hash_chain,
|
||||||
|
VP8LBackwardRefs* const refs_array[2],
|
||||||
int width, int height, int quality) {
|
int width, int height, int quality) {
|
||||||
int i;
|
int i;
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
VP8LBackwardRefs refs;
|
int max_tokens = 0;
|
||||||
|
VP8LBackwardRefs* refs;
|
||||||
|
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);
|
VP8LHistogramSet* const histogram_image = VP8LAllocateHistogramSet(1, 0);
|
||||||
if (histogram_image == NULL) return 0;
|
HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc(
|
||||||
|
3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
|
||||||
|
if (histogram_image == NULL || huff_tree == NULL) goto Error;
|
||||||
|
|
||||||
// Calculate backward references from ARGB image.
|
// Calculate backward references from ARGB image.
|
||||||
if (!VP8LGetBackwardReferences(width, height, argb, quality, 0, 1, &refs)) {
|
refs = VP8LGetBackwardReferences(width, height, argb, quality, 0, 1,
|
||||||
|
hash_chain, refs_array);
|
||||||
|
if (refs == NULL) {
|
||||||
goto Error;
|
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]);
|
||||||
|
|
||||||
// Create Huffman bit lengths and codes for each histogram image.
|
// Create Huffman bit lengths and codes for each histogram image.
|
||||||
assert(histogram_image->size == 1);
|
assert(histogram_image->size == 1);
|
||||||
@ -489,28 +508,41 @@ static int EncodeImageNoHuffman(VP8LBitWriter* const bw,
|
|||||||
// No color cache, no Huffman image.
|
// No color cache, no Huffman image.
|
||||||
VP8LWriteBits(bw, 1, 0);
|
VP8LWriteBits(bw, 1, 0);
|
||||||
|
|
||||||
|
// Find maximum number of symbols for the huffman tree-set.
|
||||||
|
for (i = 0; i < 5; ++i) {
|
||||||
|
HuffmanTreeCode* const codes = &huffman_codes[i];
|
||||||
|
if (max_tokens < codes->num_symbols) {
|
||||||
|
max_tokens = codes->num_symbols;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens = (HuffmanTreeToken*)WebPSafeMalloc((uint64_t)max_tokens,
|
||||||
|
sizeof(*tokens));
|
||||||
|
if (tokens == NULL) goto Error;
|
||||||
|
|
||||||
// Store Huffman codes.
|
// Store Huffman codes.
|
||||||
for (i = 0; i < 5; ++i) {
|
for (i = 0; i < 5; ++i) {
|
||||||
HuffmanTreeCode* const codes = &huffman_codes[i];
|
HuffmanTreeCode* const codes = &huffman_codes[i];
|
||||||
if (!StoreHuffmanCode(bw, codes)) {
|
StoreHuffmanCode(bw, huff_tree, tokens, codes);
|
||||||
goto Error;
|
|
||||||
}
|
|
||||||
ClearHuffmanTreeIfOnlyOneSymbol(codes);
|
ClearHuffmanTreeIfOnlyOneSymbol(codes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store actual literals.
|
// Store actual literals.
|
||||||
StoreImageToBitMask(bw, width, 0, &refs, histogram_symbols, huffman_codes);
|
StoreImageToBitMask(bw, width, 0, refs, histogram_symbols, huffman_codes);
|
||||||
ok = 1;
|
ok = 1;
|
||||||
|
|
||||||
Error:
|
Error:
|
||||||
WebPSafeFree(histogram_image);
|
WebPSafeFree(tokens);
|
||||||
VP8LClearBackwardRefs(&refs);
|
WebPSafeFree(huff_tree);
|
||||||
|
VP8LFreeHistogramSet(histogram_image);
|
||||||
WebPSafeFree(huffman_codes[0].codes);
|
WebPSafeFree(huffman_codes[0].codes);
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int EncodeImageInternal(VP8LBitWriter* const bw,
|
static int EncodeImageInternal(VP8LBitWriter* const bw,
|
||||||
const uint32_t* const argb,
|
const uint32_t* const argb,
|
||||||
|
VP8LHashChain* const hash_chain,
|
||||||
|
VP8LBackwardRefs* const refs_array[2],
|
||||||
int width, int height, int quality,
|
int width, int height, int quality,
|
||||||
int cache_bits, int histogram_bits) {
|
int cache_bits, int histogram_bits) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
@ -520,11 +552,14 @@ static int EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
VP8LSubSampleSize(width, histogram_bits) *
|
VP8LSubSampleSize(width, histogram_bits) *
|
||||||
VP8LSubSampleSize(height, histogram_bits);
|
VP8LSubSampleSize(height, histogram_bits);
|
||||||
VP8LHistogramSet* histogram_image =
|
VP8LHistogramSet* histogram_image =
|
||||||
VP8LAllocateHistogramSet(histogram_image_xysize, 0);
|
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;
|
||||||
|
HuffmanTreeToken* tokens = NULL;
|
||||||
HuffmanTreeCode* huffman_codes = NULL;
|
HuffmanTreeCode* huffman_codes = NULL;
|
||||||
VP8LBackwardRefs refs;
|
VP8LBackwardRefs* refs = NULL;
|
||||||
|
VP8LBackwardRefs* best_refs;
|
||||||
uint16_t* const histogram_symbols =
|
uint16_t* const histogram_symbols =
|
||||||
(uint16_t*)WebPSafeMalloc((uint64_t)histogram_image_xysize,
|
(uint16_t*)WebPSafeMalloc((uint64_t)histogram_image_xysize,
|
||||||
sizeof(*histogram_symbols));
|
sizeof(*histogram_symbols));
|
||||||
@ -532,18 +567,27 @@ static int EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
assert(histogram_bits <= MAX_HUFFMAN_BITS);
|
assert(histogram_bits <= MAX_HUFFMAN_BITS);
|
||||||
|
|
||||||
if (histogram_image == NULL || histogram_symbols == NULL) {
|
if (histogram_image == NULL || histogram_symbols == NULL) {
|
||||||
WebPSafeFree(histogram_image);
|
VP8LFreeHistogramSet(histogram_image);
|
||||||
WebPSafeFree(histogram_symbols);
|
WebPSafeFree(histogram_symbols);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refs = VP8LBackwardRefsNew(refs_array[0]->max_size);
|
||||||
|
if (refs == NULL) {
|
||||||
|
goto Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'best_refs' is the reference to the best backward refs and points to one
|
||||||
|
// of refs_array[0] or refs_array[1].
|
||||||
// Calculate backward references from ARGB image.
|
// Calculate backward references from ARGB image.
|
||||||
if (!VP8LGetBackwardReferences(width, height, argb, quality, cache_bits,
|
best_refs = VP8LGetBackwardReferences(width, height, argb, quality,
|
||||||
use_2d_locality, &refs)) {
|
cache_bits, use_2d_locality,
|
||||||
|
hash_chain, refs_array);
|
||||||
|
if (best_refs == NULL || !VP8LBackwardRefsCopy(best_refs, refs)) {
|
||||||
goto Error;
|
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, cache_bits,
|
quality, histogram_bits, cache_bits,
|
||||||
histogram_image,
|
histogram_image,
|
||||||
histogram_symbols)) {
|
histogram_symbols)) {
|
||||||
@ -559,7 +603,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
// Free combined histograms.
|
// Free combined histograms.
|
||||||
WebPSafeFree(histogram_image);
|
VP8LFreeHistogramSet(histogram_image);
|
||||||
histogram_image = NULL;
|
histogram_image = NULL;
|
||||||
|
|
||||||
// Color Cache parameters.
|
// Color Cache parameters.
|
||||||
@ -589,7 +633,7 @@ static int EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
histogram_image_size = max_index;
|
histogram_image_size = max_index;
|
||||||
|
|
||||||
VP8LWriteBits(bw, 3, histogram_bits - 2);
|
VP8LWriteBits(bw, 3, histogram_bits - 2);
|
||||||
ok = EncodeImageNoHuffman(bw, histogram_argb,
|
ok = EncodeImageNoHuffman(bw, histogram_argb, hash_chain, refs_array,
|
||||||
VP8LSubSampleSize(width, histogram_bits),
|
VP8LSubSampleSize(width, histogram_bits),
|
||||||
VP8LSubSampleSize(height, histogram_bits),
|
VP8LSubSampleSize(height, histogram_bits),
|
||||||
quality);
|
quality);
|
||||||
@ -601,22 +645,37 @@ static int EncodeImageInternal(VP8LBitWriter* const bw,
|
|||||||
// Store Huffman codes.
|
// Store Huffman codes.
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
int max_tokens = 0;
|
||||||
|
huff_tree = (HuffmanTree*)WebPSafeMalloc(3ULL * CODE_LENGTH_CODES,
|
||||||
|
sizeof(*huff_tree));
|
||||||
|
if (huff_tree == NULL) goto Error;
|
||||||
|
// Find maximum number of symbols for the huffman tree-set.
|
||||||
for (i = 0; i < 5 * histogram_image_size; ++i) {
|
for (i = 0; i < 5 * histogram_image_size; ++i) {
|
||||||
HuffmanTreeCode* const codes = &huffman_codes[i];
|
HuffmanTreeCode* const codes = &huffman_codes[i];
|
||||||
if (!StoreHuffmanCode(bw, codes)) goto Error;
|
if (max_tokens < codes->num_symbols) {
|
||||||
|
max_tokens = codes->num_symbols;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokens = (HuffmanTreeToken*)WebPSafeMalloc((uint64_t)max_tokens,
|
||||||
|
sizeof(*tokens));
|
||||||
|
if (tokens == NULL) goto Error;
|
||||||
|
for (i = 0; i < 5 * histogram_image_size; ++i) {
|
||||||
|
HuffmanTreeCode* const codes = &huffman_codes[i];
|
||||||
|
StoreHuffmanCode(bw, huff_tree, tokens, codes);
|
||||||
ClearHuffmanTreeIfOnlyOneSymbol(codes);
|
ClearHuffmanTreeIfOnlyOneSymbol(codes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store actual literals.
|
// Store actual literals.
|
||||||
StoreImageToBitMask(bw, width, histogram_bits, &refs,
|
StoreImageToBitMask(bw, width, histogram_bits, refs,
|
||||||
histogram_symbols, huffman_codes);
|
histogram_symbols, huffman_codes);
|
||||||
ok = 1;
|
ok = 1;
|
||||||
|
|
||||||
Error:
|
Error:
|
||||||
WebPSafeFree(histogram_image);
|
WebPSafeFree(tokens);
|
||||||
|
WebPSafeFree(huff_tree);
|
||||||
VP8LClearBackwardRefs(&refs);
|
VP8LFreeHistogramSet(histogram_image);
|
||||||
|
VP8LBackwardRefsDelete(refs);
|
||||||
if (huffman_codes != NULL) {
|
if (huffman_codes != NULL) {
|
||||||
WebPSafeFree(huffman_codes->codes);
|
WebPSafeFree(huffman_codes->codes);
|
||||||
WebPSafeFree(huffman_codes);
|
WebPSafeFree(huffman_codes);
|
||||||
@ -637,11 +696,9 @@ static int EvalAndApplySubtractGreen(VP8LEncoder* const enc,
|
|||||||
int i;
|
int i;
|
||||||
const uint32_t* const argb = enc->argb_;
|
const uint32_t* const argb = enc->argb_;
|
||||||
double bit_cost_before, bit_cost_after;
|
double bit_cost_before, bit_cost_after;
|
||||||
VP8LHistogram* const histo =
|
// Allocate histogram with cache_bits = 1.
|
||||||
(VP8LHistogram*)WebPSafeMalloc(1ULL, sizeof(*histo));
|
VP8LHistogram* const histo = VP8LAllocateHistogram(1);
|
||||||
if (histo == NULL) return 0;
|
if (histo == NULL) return 0;
|
||||||
|
|
||||||
VP8LHistogramInit(histo, 1);
|
|
||||||
for (i = 0; i < width * height; ++i) {
|
for (i = 0; i < width * height; ++i) {
|
||||||
const uint32_t c = argb[i];
|
const uint32_t c = argb[i];
|
||||||
++histo->red_[(c >> 16) & 0xff];
|
++histo->red_[(c >> 16) & 0xff];
|
||||||
@ -657,7 +714,7 @@ static int EvalAndApplySubtractGreen(VP8LEncoder* const enc,
|
|||||||
++histo->blue_[((c >> 0) - green) & 0xff];
|
++histo->blue_[((c >> 0) - green) & 0xff];
|
||||||
}
|
}
|
||||||
bit_cost_after = VP8LHistogramEstimateBits(histo);
|
bit_cost_after = VP8LHistogramEstimateBits(histo);
|
||||||
WebPSafeFree(histo);
|
VP8LFreeHistogram(histo);
|
||||||
|
|
||||||
// Check if subtracting green yields low entropy.
|
// Check if subtracting green yields low entropy.
|
||||||
enc->use_subtract_green_ = (bit_cost_after < bit_cost_before);
|
enc->use_subtract_green_ = (bit_cost_after < bit_cost_before);
|
||||||
@ -683,8 +740,9 @@ static int ApplyPredictFilter(const VP8LEncoder* const enc,
|
|||||||
VP8LWriteBits(bw, 2, PREDICTOR_TRANSFORM);
|
VP8LWriteBits(bw, 2, PREDICTOR_TRANSFORM);
|
||||||
assert(pred_bits >= 2);
|
assert(pred_bits >= 2);
|
||||||
VP8LWriteBits(bw, 3, pred_bits - 2);
|
VP8LWriteBits(bw, 3, pred_bits - 2);
|
||||||
if (!EncodeImageNoHuffman(bw, enc->transform_data_,
|
if (!EncodeImageNoHuffman(bw, enc->transform_data_, enc->hash_chain_,
|
||||||
transform_width, transform_height, quality)) {
|
enc->refs_, transform_width, transform_height,
|
||||||
|
quality)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
@ -703,8 +761,9 @@ static int ApplyCrossColorFilter(const VP8LEncoder* const enc,
|
|||||||
VP8LWriteBits(bw, 2, CROSS_COLOR_TRANSFORM);
|
VP8LWriteBits(bw, 2, CROSS_COLOR_TRANSFORM);
|
||||||
assert(ccolor_transform_bits >= 2);
|
assert(ccolor_transform_bits >= 2);
|
||||||
VP8LWriteBits(bw, 3, ccolor_transform_bits - 2);
|
VP8LWriteBits(bw, 3, ccolor_transform_bits - 2);
|
||||||
if (!EncodeImageNoHuffman(bw, enc->transform_data_,
|
if (!EncodeImageNoHuffman(bw, enc->transform_data_, enc->hash_chain_,
|
||||||
transform_width, transform_height, quality)) {
|
enc->refs_, transform_width, transform_height,
|
||||||
|
quality)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
@ -902,7 +961,8 @@ static WebPEncodingError EncodePalette(VP8LBitWriter* const bw,
|
|||||||
for (i = palette_size - 1; i >= 1; --i) {
|
for (i = palette_size - 1; i >= 1; --i) {
|
||||||
palette[i] = VP8LSubPixels(palette[i], palette[i - 1]);
|
palette[i] = VP8LSubPixels(palette[i], palette[i - 1]);
|
||||||
}
|
}
|
||||||
if (!EncodeImageNoHuffman(bw, palette, palette_size, 1, quality)) {
|
if (!EncodeImageNoHuffman(bw, palette, enc->hash_chain_, enc->refs_,
|
||||||
|
palette_size, 1, quality)) {
|
||||||
err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
|
err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
@ -915,7 +975,7 @@ static WebPEncodingError EncodePalette(VP8LBitWriter* const bw,
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
static int GetHistoBits(int method, int use_palette, int width, int height) {
|
static int GetHistoBits(int method, int use_palette, int width, int height) {
|
||||||
const uint64_t hist_size = sizeof(VP8LHistogram);
|
const uint64_t hist_size = VP8LGetHistogramSize(MAX_COLOR_CACHE_BITS);
|
||||||
// Make tile size a function of encoding method (Range: 0 to 6).
|
// Make tile size a function of encoding method (Range: 0 to 6).
|
||||||
int histo_bits = (use_palette ? 9 : 7) - method;
|
int histo_bits = (use_palette ? 9 : 7) - method;
|
||||||
while (1) {
|
while (1) {
|
||||||
@ -969,6 +1029,9 @@ static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config,
|
|||||||
|
|
||||||
static void VP8LEncoderDelete(VP8LEncoder* enc) {
|
static void VP8LEncoderDelete(VP8LEncoder* enc) {
|
||||||
if (enc != NULL) {
|
if (enc != NULL) {
|
||||||
|
VP8LHashChainDelete(enc->hash_chain_);
|
||||||
|
VP8LBackwardRefsDelete(enc->refs_[0]);
|
||||||
|
VP8LBackwardRefsDelete(enc->refs_[1]);
|
||||||
WebPSafeFree(enc->argb_);
|
WebPSafeFree(enc->argb_);
|
||||||
WebPSafeFree(enc);
|
WebPSafeFree(enc);
|
||||||
}
|
}
|
||||||
@ -995,7 +1058,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Analyze image (entropy, num_palettes etc)
|
// Analyze image (entropy, num_palettes etc)
|
||||||
|
|
||||||
if (!VP8LEncAnalyze(enc, config->image_hint)) {
|
if (!AnalyzeAndInit(enc, config->image_hint)) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
@ -1051,8 +1114,8 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
|
|
||||||
if (enc->cache_bits_ > 0) {
|
if (enc->cache_bits_ > 0) {
|
||||||
if (!VP8LCalculateEstimateForCacheSize(enc->argb_, enc->current_width_,
|
if (!VP8LCalculateEstimateForCacheSize(enc->argb_, enc->current_width_,
|
||||||
height, quality,
|
height, quality, enc->hash_chain_,
|
||||||
&enc->cache_bits_)) {
|
enc->refs_[0], &enc->cache_bits_)) {
|
||||||
err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
|
err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
@ -1061,8 +1124,9 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Encode and write the transformed image.
|
// Encode and write the transformed image.
|
||||||
|
|
||||||
if (!EncodeImageInternal(bw, enc->argb_, enc->current_width_, height,
|
if (!EncodeImageInternal(bw, enc->argb_, enc->hash_chain_, enc->refs_,
|
||||||
quality, enc->cache_bits_, enc->histo_bits_)) {
|
enc->current_width_, height, quality,
|
||||||
|
enc->cache_bits_, enc->histo_bits_)) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
@ -1092,6 +1156,7 @@ int VP8LEncodeImage(const WebPConfig* const config,
|
|||||||
int has_alpha;
|
int has_alpha;
|
||||||
size_t coded_size;
|
size_t coded_size;
|
||||||
int percent = 0;
|
int percent = 0;
|
||||||
|
int initial_size;
|
||||||
WebPEncodingError err = VP8_ENC_OK;
|
WebPEncodingError err = VP8_ENC_OK;
|
||||||
VP8LBitWriter bw;
|
VP8LBitWriter bw;
|
||||||
|
|
||||||
@ -1105,8 +1170,11 @@ int VP8LEncodeImage(const WebPConfig* const config,
|
|||||||
|
|
||||||
width = picture->width;
|
width = picture->width;
|
||||||
height = picture->height;
|
height = picture->height;
|
||||||
// Initialize BitWriter with size corresponding to 8bpp.
|
// Initialize BitWriter with size corresponding to 16 bpp to photo images and
|
||||||
if (!VP8LBitWriterInit(&bw, width * height)) {
|
// 8 bpp for graphical images.
|
||||||
|
initial_size = (config->image_hint == WEBP_HINT_GRAPH) ?
|
||||||
|
width * height : width * height * 2;
|
||||||
|
if (!VP8LBitWriterInit(&bw, initial_size)) {
|
||||||
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct VP8LHashChain; // Defined in backward_references.h
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const WebPConfig* config_; // user configuration and parameters
|
const WebPConfig* config_; // user configuration and parameters
|
||||||
const WebPPicture* pic_; // input picture.
|
const WebPPicture* pic_; // input picture.
|
||||||
@ -33,6 +35,11 @@ typedef struct {
|
|||||||
uint32_t* transform_data_; // Scratch memory for transform data.
|
uint32_t* transform_data_; // Scratch memory for transform data.
|
||||||
int current_width_; // Corresponds to packed image width.
|
int current_width_; // Corresponds to packed image width.
|
||||||
|
|
||||||
|
struct VP8LHashChain* hash_chain_; // HashChain data for constructing
|
||||||
|
// backward references.
|
||||||
|
struct VP8LBackwardRefs* refs_[2]; // Backward Refs array corresponding to
|
||||||
|
// LZ77 & RLE coding.
|
||||||
|
|
||||||
// Encoding parameters derived from quality parameter.
|
// Encoding parameters derived from quality parameter.
|
||||||
int histo_bits_;
|
int histo_bits_;
|
||||||
int transform_bits_;
|
int transform_bits_;
|
||||||
|
@ -28,13 +28,13 @@ static int ValuesShouldBeCollapsedToStrideAverage(int a, int b) {
|
|||||||
|
|
||||||
// Change the population counts in a way that the consequent
|
// Change the population counts in a way that the consequent
|
||||||
// Huffman tree compression, especially its RLE-part, give smaller output.
|
// Huffman tree compression, especially its RLE-part, give smaller output.
|
||||||
static int OptimizeHuffmanForRle(int length, int* const counts) {
|
static void OptimizeHuffmanForRle(int length, uint8_t* const good_for_rle,
|
||||||
uint8_t* good_for_rle;
|
int* const counts) {
|
||||||
// 1) Let's make the Huffman code more compatible with rle encoding.
|
// 1) Let's make the Huffman code more compatible with rle encoding.
|
||||||
int i;
|
int i;
|
||||||
for (; length >= 0; --length) {
|
for (; length >= 0; --length) {
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
return 1; // All zeros.
|
return; // All zeros.
|
||||||
}
|
}
|
||||||
if (counts[length - 1] != 0) {
|
if (counts[length - 1] != 0) {
|
||||||
// Now counts[0..length - 1] does not have trailing zeros.
|
// Now counts[0..length - 1] does not have trailing zeros.
|
||||||
@ -43,10 +43,6 @@ static int OptimizeHuffmanForRle(int length, int* const counts) {
|
|||||||
}
|
}
|
||||||
// 2) Let's mark all population counts that already can be encoded
|
// 2) Let's mark all population counts that already can be encoded
|
||||||
// with an rle code.
|
// with an rle code.
|
||||||
good_for_rle = (uint8_t*)WebPSafeCalloc(1ULL, length);
|
|
||||||
if (good_for_rle == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
// Let's not spoil any of the existing good rle codes.
|
// Let's not spoil any of the existing good rle codes.
|
||||||
// Mark any seq of 0's that is longer as 5 as a good_for_rle.
|
// Mark any seq of 0's that is longer as 5 as a good_for_rle.
|
||||||
@ -119,17 +115,8 @@ static int OptimizeHuffmanForRle(int length, int* const counts) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WebPSafeFree(good_for_rle);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int total_count_;
|
|
||||||
int value_;
|
|
||||||
int pool_index_left_;
|
|
||||||
int pool_index_right_;
|
|
||||||
} HuffmanTree;
|
|
||||||
|
|
||||||
// A comparer function for two Huffman trees: sorts first by 'total count'
|
// A comparer function for two Huffman trees: sorts first by 'total count'
|
||||||
// (more comes first), and then by 'value' (more comes first).
|
// (more comes first), and then by 'value' (more comes first).
|
||||||
static int CompareHuffmanTrees(const void* ptr1, const void* ptr2) {
|
static int CompareHuffmanTrees(const void* ptr1, const void* ptr2) {
|
||||||
@ -175,12 +162,11 @@ static void SetBitDepths(const HuffmanTree* const tree,
|
|||||||
// we are not planning to use this with extremely long blocks.
|
// we are not planning to use this with extremely long blocks.
|
||||||
//
|
//
|
||||||
// See http://en.wikipedia.org/wiki/Huffman_coding
|
// See http://en.wikipedia.org/wiki/Huffman_coding
|
||||||
static int GenerateOptimalTree(const int* const histogram, int histogram_size,
|
static void GenerateOptimalTree(const int* const histogram, int histogram_size,
|
||||||
int tree_depth_limit,
|
HuffmanTree* tree, int tree_depth_limit,
|
||||||
uint8_t* const bit_depths) {
|
uint8_t* const bit_depths) {
|
||||||
int count_min;
|
int count_min;
|
||||||
HuffmanTree* tree_pool;
|
HuffmanTree* tree_pool;
|
||||||
HuffmanTree* tree;
|
|
||||||
int tree_size_orig = 0;
|
int tree_size_orig = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -191,15 +177,9 @@ static int GenerateOptimalTree(const int* const histogram, int histogram_size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tree_size_orig == 0) { // pretty optimal already!
|
if (tree_size_orig == 0) { // pretty optimal already!
|
||||||
return 1;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3 * tree_size is enough to cover all the nodes representing a
|
|
||||||
// population and all the inserted nodes combining two existing nodes.
|
|
||||||
// The tree pool needs 2 * (tree_size_orig - 1) entities, and the
|
|
||||||
// tree needs exactly tree_size_orig entities.
|
|
||||||
tree = (HuffmanTree*)WebPSafeMalloc(3ULL * tree_size_orig, sizeof(*tree));
|
|
||||||
if (tree == NULL) return 0;
|
|
||||||
tree_pool = tree + tree_size_orig;
|
tree_pool = tree + tree_size_orig;
|
||||||
|
|
||||||
// For block sizes with less than 64k symbols we never need to do a
|
// For block sizes with less than 64k symbols we never need to do a
|
||||||
@ -272,8 +252,6 @@ static int GenerateOptimalTree(const int* const histogram, int histogram_size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WebPSafeFree(tree);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@ -424,17 +402,15 @@ static void ConvertBitDepthsToSymbols(HuffmanTreeCode* const tree) {
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Main entry point
|
// Main entry point
|
||||||
|
|
||||||
int VP8LCreateHuffmanTree(int* const histogram, int tree_depth_limit,
|
void VP8LCreateHuffmanTree(int* const histogram, int tree_depth_limit,
|
||||||
HuffmanTreeCode* const tree) {
|
uint8_t* const buf_rle,
|
||||||
const int num_symbols = tree->num_symbols;
|
HuffmanTree* const huff_tree,
|
||||||
if (!OptimizeHuffmanForRle(num_symbols, histogram)) {
|
HuffmanTreeCode* const huff_code) {
|
||||||
return 0;
|
const int num_symbols = huff_code->num_symbols;
|
||||||
}
|
memset(buf_rle, 0, num_symbols * sizeof(*buf_rle));
|
||||||
if (!GenerateOptimalTree(histogram, num_symbols,
|
OptimizeHuffmanForRle(num_symbols, buf_rle, histogram);
|
||||||
tree_depth_limit, tree->code_lengths)) {
|
GenerateOptimalTree(histogram, num_symbols, huff_tree, tree_depth_limit,
|
||||||
return 0;
|
huff_code->code_lengths);
|
||||||
}
|
|
||||||
// Create the actual bit codes for the bit lengths.
|
// Create the actual bit codes for the bit lengths.
|
||||||
ConvertBitDepthsToSymbols(tree);
|
ConvertBitDepthsToSymbols(huff_code);
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
@ -33,13 +33,25 @@ typedef struct {
|
|||||||
uint16_t* codes; // Symbol Codes.
|
uint16_t* codes; // Symbol Codes.
|
||||||
} HuffmanTreeCode;
|
} HuffmanTreeCode;
|
||||||
|
|
||||||
|
// Struct to represent the Huffman tree.
|
||||||
|
// TODO(vikasa): Add comment for the fields of the Struct.
|
||||||
|
typedef struct {
|
||||||
|
int total_count_;
|
||||||
|
int value_;
|
||||||
|
int pool_index_left_; // Index for the left sub-tree.
|
||||||
|
int pool_index_right_; // Index for the right sub-tree.
|
||||||
|
} HuffmanTree;
|
||||||
|
|
||||||
// Turn the Huffman tree into a token sequence.
|
// Turn the Huffman tree into a token sequence.
|
||||||
// Returns the number of tokens used.
|
// Returns the number of tokens used.
|
||||||
int VP8LCreateCompressedHuffmanTree(const HuffmanTreeCode* const tree,
|
int VP8LCreateCompressedHuffmanTree(const HuffmanTreeCode* const tree,
|
||||||
HuffmanTreeToken* tokens, int max_tokens);
|
HuffmanTreeToken* tokens, int max_tokens);
|
||||||
|
|
||||||
// Create an optimized tree, and tokenize it.
|
// Create an optimized tree, and tokenize it.
|
||||||
int VP8LCreateHuffmanTree(int* const histogram, int tree_depth_limit,
|
// 'buf_rle' and 'huff_tree' are pre-allocated and the 'tree' is the constructed
|
||||||
|
// huffman code tree.
|
||||||
|
void VP8LCreateHuffmanTree(int* const histogram, int tree_depth_limit,
|
||||||
|
uint8_t* const buf_rle, HuffmanTree* const huff_tree,
|
||||||
HuffmanTreeCode* const tree);
|
HuffmanTreeCode* const tree);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
Loading…
Reference in New Issue
Block a user