mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-28 14:38:21 +01:00
clean-up around Huffman-encode
* refine doc * use LUT for bit-reversal * added assert * Introduce HuffmanTreeToken instead of separate code/extra_bits arrays Change-Id: I0fe0b50b55eb43a4be9f730b1abe40632a6fa7f0
This commit is contained in:
parent
c579a71012
commit
9f566d1d36
@ -387,25 +387,25 @@ static void StoreHuffmanTreeOfHuffmanTreeToBitMask(
|
|||||||
|
|
||||||
static void StoreHuffmanTreeToBitMask(
|
static void StoreHuffmanTreeToBitMask(
|
||||||
VP8LBitWriter* const bw,
|
VP8LBitWriter* const bw,
|
||||||
const uint8_t* huffman_tree,
|
const HuffmanTreeToken* const tokens,
|
||||||
const uint8_t* huffman_tree_extra_bits,
|
const int num_tokens,
|
||||||
const int num_symbols,
|
|
||||||
const uint8_t* code_length_bitdepth,
|
const uint8_t* code_length_bitdepth,
|
||||||
const uint16_t* code_length_bitdepth_symbols) {
|
const uint16_t* code_length_bitdepth_symbols) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < num_symbols; ++i) {
|
for (i = 0; i < num_tokens; ++i) {
|
||||||
const int ix = huffman_tree[i];
|
const int ix = tokens[i].code;
|
||||||
|
const int extra_bits = tokens[i].extra_bits;
|
||||||
VP8LWriteBits(bw, code_length_bitdepth[ix],
|
VP8LWriteBits(bw, code_length_bitdepth[ix],
|
||||||
code_length_bitdepth_symbols[ix]);
|
code_length_bitdepth_symbols[ix]);
|
||||||
switch (ix) {
|
switch (ix) {
|
||||||
case 16:
|
case 16:
|
||||||
VP8LWriteBits(bw, 2, huffman_tree_extra_bits[i]);
|
VP8LWriteBits(bw, 2, extra_bits);
|
||||||
break;
|
break;
|
||||||
case 17:
|
case 17:
|
||||||
VP8LWriteBits(bw, 3, huffman_tree_extra_bits[i]);
|
VP8LWriteBits(bw, 3, extra_bits);
|
||||||
break;
|
break;
|
||||||
case 18:
|
case 18:
|
||||||
VP8LWriteBits(bw, 7, huffman_tree_extra_bits[i]);
|
VP8LWriteBits(bw, 7, extra_bits);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -415,27 +415,21 @@ static int StoreFullHuffmanCode(VP8LBitWriter* const bw,
|
|||||||
const uint8_t* const bit_lengths,
|
const uint8_t* const bit_lengths,
|
||||||
int bit_lengths_size) {
|
int bit_lengths_size) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
int huffman_tree_size = 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 };
|
||||||
uint8_t* huffman_tree_extra_bits;
|
int num_tokens;
|
||||||
uint8_t* const huffman_tree =
|
HuffmanTreeToken* const tokens =
|
||||||
(uint8_t*)malloc(bit_lengths_size * sizeof(*huffman_tree) +
|
(HuffmanTreeToken*)malloc(bit_lengths_size * sizeof(*tokens));
|
||||||
bit_lengths_size * sizeof(*huffman_tree_extra_bits));
|
if (tokens == NULL) return 0;
|
||||||
|
|
||||||
if (huffman_tree == NULL) return 0;
|
|
||||||
huffman_tree_extra_bits =
|
|
||||||
huffman_tree + bit_lengths_size * sizeof(*huffman_tree);
|
|
||||||
|
|
||||||
VP8LWriteBits(bw, 1, 0);
|
VP8LWriteBits(bw, 1, 0);
|
||||||
VP8LCreateCompressedHuffmanTree(bit_lengths, bit_lengths_size,
|
num_tokens = VP8LCreateCompressedHuffmanTree(bit_lengths, bit_lengths_size,
|
||||||
&huffman_tree_size, huffman_tree,
|
tokens, bit_lengths_size);
|
||||||
huffman_tree_extra_bits);
|
|
||||||
{
|
{
|
||||||
int histogram[CODE_LENGTH_CODES] = { 0 };
|
int histogram[CODE_LENGTH_CODES] = { 0 };
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < huffman_tree_size; ++i) {
|
for (i = 0; i < num_tokens; ++i) {
|
||||||
++histogram[huffman_tree[i]];
|
++histogram[tokens[i].code];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!VP8LCreateHuffmanTree(histogram, CODE_LENGTH_CODES,
|
if (!VP8LCreateHuffmanTree(histogram, CODE_LENGTH_CODES,
|
||||||
@ -451,12 +445,12 @@ static int StoreFullHuffmanCode(VP8LBitWriter* const bw,
|
|||||||
code_length_bitdepth_symbols);
|
code_length_bitdepth_symbols);
|
||||||
{
|
{
|
||||||
int trailing_zero_bits = 0;
|
int trailing_zero_bits = 0;
|
||||||
int trimmed_length = huffman_tree_size;
|
int trimmed_length = num_tokens;
|
||||||
int write_trimmed_length;
|
int write_trimmed_length;
|
||||||
int length;
|
int length;
|
||||||
int i = huffman_tree_size;
|
int i = num_tokens;
|
||||||
while (i-- > 0) {
|
while (i-- > 0) {
|
||||||
const int ix = huffman_tree[i];
|
const int ix = tokens[i].code;
|
||||||
if (ix == 0 || ix == 17 || ix == 18) {
|
if (ix == 0 || ix == 17 || ix == 18) {
|
||||||
--trimmed_length; // discount trailing zeros
|
--trimmed_length; // discount trailing zeros
|
||||||
trailing_zero_bits += code_length_bitdepth[ix];
|
trailing_zero_bits += code_length_bitdepth[ix];
|
||||||
@ -470,7 +464,7 @@ static int StoreFullHuffmanCode(VP8LBitWriter* const bw,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
write_trimmed_length = (trimmed_length > 1 && trailing_zero_bits > 12);
|
write_trimmed_length = (trimmed_length > 1 && trailing_zero_bits > 12);
|
||||||
length = write_trimmed_length ? trimmed_length : huffman_tree_size;
|
length = write_trimmed_length ? trimmed_length : num_tokens;
|
||||||
VP8LWriteBits(bw, 1, write_trimmed_length);
|
VP8LWriteBits(bw, 1, write_trimmed_length);
|
||||||
if (write_trimmed_length) {
|
if (write_trimmed_length) {
|
||||||
const int nbits = VP8LBitsLog2Ceiling(trimmed_length - 1);
|
const int nbits = VP8LBitsLog2Ceiling(trimmed_length - 1);
|
||||||
@ -478,13 +472,13 @@ static int StoreFullHuffmanCode(VP8LBitWriter* const bw,
|
|||||||
VP8LWriteBits(bw, 3, nbitpairs - 1);
|
VP8LWriteBits(bw, 3, nbitpairs - 1);
|
||||||
VP8LWriteBits(bw, nbitpairs * 2, trimmed_length - 2);
|
VP8LWriteBits(bw, nbitpairs * 2, trimmed_length - 2);
|
||||||
}
|
}
|
||||||
StoreHuffmanTreeToBitMask(bw, huffman_tree, huffman_tree_extra_bits,
|
StoreHuffmanTreeToBitMask(bw, tokens,
|
||||||
length, code_length_bitdepth,
|
length, code_length_bitdepth,
|
||||||
code_length_bitdepth_symbols);
|
code_length_bitdepth_symbols);
|
||||||
}
|
}
|
||||||
ok = 1;
|
ok = 1;
|
||||||
End:
|
End:
|
||||||
free(huffman_tree);
|
free(tokens);
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,11 +7,14 @@
|
|||||||
//
|
//
|
||||||
// Author: jyrki@google.com (Jyrki Alakuijala)
|
// Author: jyrki@google.com (Jyrki Alakuijala)
|
||||||
//
|
//
|
||||||
// Flate like entropy encoding (Huffman) for webp lossless.
|
// Flate-like entropy encoding (Huffman) for webp lossless.
|
||||||
|
|
||||||
#ifdef USE_LOSSLESS_ENCODER
|
#ifdef USE_LOSSLESS_ENCODER
|
||||||
|
|
||||||
#include "./huffman_encode.h"
|
#include "./huffman_encode.h"
|
||||||
|
|
||||||
|
#define MAX_BITS 16 // maximum allowed length for the codes
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -57,9 +60,7 @@ static void SetBitDepths(const HuffmanTree* const tree,
|
|||||||
|
|
||||||
// This function will create a Huffman tree.
|
// This function will create a Huffman tree.
|
||||||
//
|
//
|
||||||
// The catch here is that the tree cannot be arbitrarily deep.
|
// The catch here is that the tree cannot be arbitrarily deep
|
||||||
// Deflate specifies a maximum depth of 15 bits for "code trees"
|
|
||||||
// and 7 bits for "code length code trees."
|
|
||||||
//
|
//
|
||||||
// count_limit is the value that is to be faked as the minimum value
|
// count_limit is the value that is to be faked as the minimum value
|
||||||
// and this minimum value is raised until the tree matches the
|
// and this minimum value is raised until the tree matches the
|
||||||
@ -165,149 +166,144 @@ int VP8LCreateHuffmanTree(const int* const histogram, int histogram_size,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WriteHuffmanTreeRepetitions(
|
// -----------------------------------------------------------------------------
|
||||||
const int value,
|
// Coding of the Huffman tree values
|
||||||
const int prev_value,
|
|
||||||
int repetitions,
|
static HuffmanTreeToken* CodeRepeatedValues(int repetitions,
|
||||||
int* num_symbols,
|
HuffmanTreeToken* tokens,
|
||||||
uint8_t* tree,
|
int value, int prev_value) {
|
||||||
uint8_t* extra_bits_data) {
|
assert(value < MAX_BITS);
|
||||||
if (value != prev_value) {
|
if (value != prev_value) {
|
||||||
tree[*num_symbols] = value;
|
tokens->code = value;
|
||||||
extra_bits_data[*num_symbols] = 0;
|
tokens->extra_bits = 0;
|
||||||
++(*num_symbols);
|
++tokens;
|
||||||
--repetitions;
|
--repetitions;
|
||||||
}
|
}
|
||||||
while (repetitions >= 1) {
|
while (repetitions >= 1) {
|
||||||
if (repetitions < 3) {
|
if (repetitions < 3) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < repetitions; ++i) {
|
for (i = 0; i < repetitions; ++i) {
|
||||||
tree[*num_symbols] = value;
|
tokens->code = value;
|
||||||
extra_bits_data[*num_symbols] = 0;
|
tokens->extra_bits = 0;
|
||||||
++(*num_symbols);
|
++tokens;
|
||||||
}
|
}
|
||||||
return;
|
break;
|
||||||
} else if (repetitions < 7) {
|
} else if (repetitions < 7) {
|
||||||
// 3 to 6 left
|
tokens->code = 16;
|
||||||
tree[*num_symbols] = 16;
|
tokens->extra_bits = repetitions - 3;
|
||||||
extra_bits_data[*num_symbols] = repetitions - 3;
|
++tokens;
|
||||||
++(*num_symbols);
|
break;
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
tree[*num_symbols] = 16;
|
tokens->code = 16;
|
||||||
extra_bits_data[*num_symbols] = 3;
|
tokens->extra_bits = 3;
|
||||||
++(*num_symbols);
|
++tokens;
|
||||||
repetitions -= 6;
|
repetitions -= 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WriteHuffmanTreeRepetitionsZeros(
|
static HuffmanTreeToken* CodeRepeatedZeros(int repetitions,
|
||||||
const int value,
|
HuffmanTreeToken* tokens) {
|
||||||
int repetitions,
|
|
||||||
int* num_symbols,
|
|
||||||
uint8_t* tree,
|
|
||||||
uint8_t* extra_bits_data) {
|
|
||||||
while (repetitions >= 1) {
|
while (repetitions >= 1) {
|
||||||
if (repetitions < 3) {
|
if (repetitions < 3) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < repetitions; ++i) {
|
for (i = 0; i < repetitions; ++i) {
|
||||||
tree[*num_symbols] = value;
|
tokens->code = 0; // 0-value
|
||||||
extra_bits_data[*num_symbols] = 0;
|
tokens->extra_bits = 0;
|
||||||
++(*num_symbols);
|
++tokens;
|
||||||
}
|
}
|
||||||
return;
|
break;
|
||||||
} else if (repetitions < 11) {
|
} else if (repetitions < 11) {
|
||||||
tree[*num_symbols] = 17;
|
tokens->code = 17;
|
||||||
extra_bits_data[*num_symbols] = repetitions - 3;
|
tokens->extra_bits = repetitions - 3;
|
||||||
++(*num_symbols);
|
++tokens;
|
||||||
return;
|
break;
|
||||||
} else if (repetitions < 139) {
|
} else if (repetitions < 139) {
|
||||||
tree[*num_symbols] = 18;
|
tokens->code = 18;
|
||||||
extra_bits_data[*num_symbols] = repetitions - 11;
|
tokens->extra_bits = repetitions - 11;
|
||||||
++(*num_symbols);
|
++tokens;
|
||||||
return;
|
break;
|
||||||
} else {
|
} else {
|
||||||
tree[*num_symbols] = 18;
|
tokens->code = 18;
|
||||||
extra_bits_data[*num_symbols] = 0x7f; // 138 repeated 0s
|
tokens->extra_bits = 0x7f; // 138 repeated 0s
|
||||||
++(*num_symbols);
|
++tokens;
|
||||||
repetitions -= 138;
|
repetitions -= 138;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VP8LCreateCompressedHuffmanTree(const uint8_t* const depth,
|
int VP8LCreateCompressedHuffmanTree(const uint8_t* const depth,
|
||||||
int depth_size,
|
int depth_size,
|
||||||
int* num_symbols,
|
HuffmanTreeToken* tokens,
|
||||||
uint8_t* tree,
|
int max_tokens) {
|
||||||
uint8_t* extra_bits_data) {
|
HuffmanTreeToken* const starting_token = tokens;
|
||||||
|
HuffmanTreeToken* const ending_token = tokens + max_tokens;
|
||||||
int prev_value = 8; // 8 is the initial value for rle.
|
int prev_value = 8; // 8 is the initial value for rle.
|
||||||
int i;
|
int i = 0;
|
||||||
for (i = 0; i < depth_size;) {
|
while (i < depth_size) {
|
||||||
const int value = depth[i];
|
const int value = depth[i];
|
||||||
int reps = 1;
|
int k = i + 1;
|
||||||
int k;
|
int runs;
|
||||||
for (k = i + 1; k < depth_size && depth[k] == value; ++k) {
|
while (k < depth_size && depth[k] == value) ++k;
|
||||||
++reps;
|
runs = k - i;
|
||||||
}
|
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
WriteHuffmanTreeRepetitionsZeros(value, reps,
|
tokens = CodeRepeatedZeros(runs, tokens);
|
||||||
num_symbols,
|
|
||||||
tree, extra_bits_data);
|
|
||||||
} else {
|
} else {
|
||||||
WriteHuffmanTreeRepetitions(value, prev_value, reps,
|
tokens = CodeRepeatedValues(runs, tokens, value, prev_value);
|
||||||
num_symbols,
|
|
||||||
tree, extra_bits_data);
|
|
||||||
prev_value = value;
|
prev_value = value;
|
||||||
}
|
}
|
||||||
i += reps;
|
i += runs;
|
||||||
|
assert(tokens <= ending_token);
|
||||||
}
|
}
|
||||||
|
(void)ending_token; // suppress 'unused variable' warning
|
||||||
|
return tokens - starting_token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Pre-reversed 4-bit values.
|
||||||
|
static const uint8_t kReversedBits[16] = {
|
||||||
|
0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
|
||||||
|
0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf
|
||||||
|
};
|
||||||
|
|
||||||
static uint32_t ReverseBits(int num_bits, uint32_t bits) {
|
static uint32_t ReverseBits(int num_bits, uint32_t bits) {
|
||||||
uint32_t retval = 0;
|
uint32_t retval = 0;
|
||||||
int i;
|
int i = 0;
|
||||||
for (i = 0; i < num_bits; ++i) {
|
while (i < num_bits) {
|
||||||
retval <<= 1;
|
i += 4;
|
||||||
retval |= bits & 1;
|
retval |= kReversedBits[bits & 0xf] << (MAX_BITS - i);
|
||||||
bits >>= 1;
|
bits >>= 4;
|
||||||
}
|
}
|
||||||
|
retval >>= (MAX_BITS - num_bits);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VP8LConvertBitDepthsToSymbols(const uint8_t* depth, int len,
|
void VP8LConvertBitDepthsToSymbols(const uint8_t* const depth,
|
||||||
uint16_t* bits) {
|
int len,
|
||||||
// This function is based on RFC 1951.
|
uint16_t* const bits) {
|
||||||
//
|
// 0 bit-depth means that the symbol does not exist.
|
||||||
// In deflate, all bit depths are [1..15]
|
|
||||||
// 0 bit depth means that the symbol does not exist.
|
|
||||||
|
|
||||||
// 0..15 are values for bits
|
|
||||||
#define MAX_BITS 16
|
|
||||||
uint32_t next_code[MAX_BITS];
|
|
||||||
uint32_t bl_count[MAX_BITS] = { 0 };
|
|
||||||
int i;
|
int i;
|
||||||
{
|
uint32_t next_code[MAX_BITS];
|
||||||
for (i = 0; i < len; ++i) {
|
int depth_count[MAX_BITS] = { 0 };
|
||||||
++bl_count[depth[i]];
|
for (i = 0; i < len; ++i) {
|
||||||
}
|
assert(depth[i] < MAX_BITS);
|
||||||
bl_count[0] = 0;
|
++depth_count[depth[i]];
|
||||||
}
|
}
|
||||||
|
depth_count[0] = 0; // ignore unused symbol
|
||||||
next_code[0] = 0;
|
next_code[0] = 0;
|
||||||
{
|
{
|
||||||
int code = 0;
|
uint32_t code = 0;
|
||||||
int bits;
|
for (i = 1; i < MAX_BITS; ++i) {
|
||||||
for (bits = 1; bits < MAX_BITS; ++bits) {
|
code = (code + depth_count[i - 1]) << 1;
|
||||||
code = (code + bl_count[bits - 1]) << 1;
|
next_code[i] = code;
|
||||||
next_code[bits] = code;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i = 0; i < len; ++i) {
|
for (i = 0; i < len; ++i) {
|
||||||
if (depth[i]) {
|
bits[i] = ReverseBits(depth[i], next_code[depth[i]]++);
|
||||||
bits[i] = ReverseBits(depth[i], next_code[depth[i]]++);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#undef MAX_BITS
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
//
|
//
|
||||||
// Author: jyrki@google.com (Jyrki Alakuijala)
|
// Author: jyrki@google.com (Jyrki Alakuijala)
|
||||||
//
|
//
|
||||||
// Flate like entropy encoding (Huffman) for webp lossless
|
// Flate-like entropy encoding (Huffman) for webp lossless
|
||||||
|
|
||||||
#ifndef WEBP_UTILS_HUFFMAN_ENCODE_H_
|
#ifndef WEBP_UTILS_HUFFMAN_ENCODE_H_
|
||||||
#define WEBP_UTILS_HUFFMAN_ENCODE_H_
|
#define WEBP_UTILS_HUFFMAN_ENCODE_H_
|
||||||
@ -20,27 +20,29 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// This function will create a Huffman tree.
|
// Create a Huffman tree.
|
||||||
//
|
//
|
||||||
// 'histogram' contains the population counts.
|
// (data,length): population counts.
|
||||||
// 'tree_depth_limit' is the maximum bit depth of the Huffman codes.
|
// tree_limit: maximum bit depth (inclusive) of the codes.
|
||||||
// The created tree is returned as 'bit_depths', which stores how many bits are
|
// bit_depths[]: how many bits are used for the symbol.
|
||||||
// used for each symbol.
|
//
|
||||||
// See http://en.wikipedia.org/wiki/Huffman_coding
|
// Returns 0 when an error has occured.
|
||||||
// Returns 0 on memory error.
|
int VP8LCreateHuffmanTree(const int* data, const int length,
|
||||||
int VP8LCreateHuffmanTree(const int* const histogram, int histogram_size,
|
const int tree_limit, uint8_t* bit_depths);
|
||||||
int tree_depth_limit, uint8_t* const bit_depths);
|
|
||||||
|
|
||||||
// Write a huffman tree from bit depths. The generated Huffman tree is
|
// Turn the Huffman tree into a token sequence.
|
||||||
// compressed once more using a Huffman tree.
|
// Returns the number of tokens used.
|
||||||
void VP8LCreateCompressedHuffmanTree(const uint8_t* const depth, int len,
|
typedef struct {
|
||||||
int* num_symbols,
|
uint8_t code; // value (0..15) or escape code (16,17,18)
|
||||||
uint8_t* tree,
|
uint8_t extra_bits; // extra bits for escape codes
|
||||||
uint8_t* extra_bits_data);
|
} HuffmanTreeToken;
|
||||||
|
|
||||||
|
int VP8LCreateCompressedHuffmanTree(const uint8_t* const depth, int len,
|
||||||
|
HuffmanTreeToken* tokens, int max_tokens);
|
||||||
|
|
||||||
// Get the actual bit values for a tree of bit depths.
|
// Get the actual bit values for a tree of bit depths.
|
||||||
void VP8LConvertBitDepthsToSymbols(const uint8_t* depth, int len,
|
void VP8LConvertBitDepthsToSymbols(const uint8_t* const depth, int len,
|
||||||
uint16_t* bits);
|
uint16_t* const bits);
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user