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:
Pascal Massimino 2012-05-10 09:11:47 -07:00
parent c579a71012
commit 9f566d1d36
3 changed files with 134 additions and 142 deletions

View File

@ -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;
} }

View File

@ -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];
int depth_count[MAX_BITS] = { 0 };
for (i = 0; i < len; ++i) { for (i = 0; i < len; ++i) {
++bl_count[depth[i]]; assert(depth[i] < MAX_BITS);
} ++depth_count[depth[i]];
bl_count[0] = 0;
} }
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

View File

@ -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)
} }