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

@ -7,11 +7,14 @@
//
// 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
#include "./huffman_encode.h"
#define MAX_BITS 16 // maximum allowed length for the codes
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
@ -57,9 +60,7 @@ static void SetBitDepths(const HuffmanTree* const tree,
// This function will create a Huffman tree.
//
// 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."
// The catch here is that the tree cannot be arbitrarily deep
//
// 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
@ -165,149 +166,144 @@ int VP8LCreateHuffmanTree(const int* const histogram, int histogram_size,
return 1;
}
static void WriteHuffmanTreeRepetitions(
const int value,
const int prev_value,
int repetitions,
int* num_symbols,
uint8_t* tree,
uint8_t* extra_bits_data) {
// -----------------------------------------------------------------------------
// Coding of the Huffman tree values
static HuffmanTreeToken* CodeRepeatedValues(int repetitions,
HuffmanTreeToken* tokens,
int value, int prev_value) {
assert(value < MAX_BITS);
if (value != prev_value) {
tree[*num_symbols] = value;
extra_bits_data[*num_symbols] = 0;
++(*num_symbols);
tokens->code = value;
tokens->extra_bits = 0;
++tokens;
--repetitions;
}
while (repetitions >= 1) {
if (repetitions < 3) {
int i;
for (i = 0; i < repetitions; ++i) {
tree[*num_symbols] = value;
extra_bits_data[*num_symbols] = 0;
++(*num_symbols);
tokens->code = value;
tokens->extra_bits = 0;
++tokens;
}
return;
break;
} else if (repetitions < 7) {
// 3 to 6 left
tree[*num_symbols] = 16;
extra_bits_data[*num_symbols] = repetitions - 3;
++(*num_symbols);
return;
tokens->code = 16;
tokens->extra_bits = repetitions - 3;
++tokens;
break;
} else {
tree[*num_symbols] = 16;
extra_bits_data[*num_symbols] = 3;
++(*num_symbols);
tokens->code = 16;
tokens->extra_bits = 3;
++tokens;
repetitions -= 6;
}
}
return tokens;
}
static void WriteHuffmanTreeRepetitionsZeros(
const int value,
int repetitions,
int* num_symbols,
uint8_t* tree,
uint8_t* extra_bits_data) {
static HuffmanTreeToken* CodeRepeatedZeros(int repetitions,
HuffmanTreeToken* tokens) {
while (repetitions >= 1) {
if (repetitions < 3) {
int i;
for (i = 0; i < repetitions; ++i) {
tree[*num_symbols] = value;
extra_bits_data[*num_symbols] = 0;
++(*num_symbols);
tokens->code = 0; // 0-value
tokens->extra_bits = 0;
++tokens;
}
return;
break;
} else if (repetitions < 11) {
tree[*num_symbols] = 17;
extra_bits_data[*num_symbols] = repetitions - 3;
++(*num_symbols);
return;
tokens->code = 17;
tokens->extra_bits = repetitions - 3;
++tokens;
break;
} else if (repetitions < 139) {
tree[*num_symbols] = 18;
extra_bits_data[*num_symbols] = repetitions - 11;
++(*num_symbols);
return;
tokens->code = 18;
tokens->extra_bits = repetitions - 11;
++tokens;
break;
} else {
tree[*num_symbols] = 18;
extra_bits_data[*num_symbols] = 0x7f; // 138 repeated 0s
++(*num_symbols);
tokens->code = 18;
tokens->extra_bits = 0x7f; // 138 repeated 0s
++tokens;
repetitions -= 138;
}
}
return tokens;
}
void VP8LCreateCompressedHuffmanTree(const uint8_t* const depth,
int depth_size,
int* num_symbols,
uint8_t* tree,
uint8_t* extra_bits_data) {
int VP8LCreateCompressedHuffmanTree(const uint8_t* const depth,
int depth_size,
HuffmanTreeToken* tokens,
int max_tokens) {
HuffmanTreeToken* const starting_token = tokens;
HuffmanTreeToken* const ending_token = tokens + max_tokens;
int prev_value = 8; // 8 is the initial value for rle.
int i;
for (i = 0; i < depth_size;) {
int i = 0;
while (i < depth_size) {
const int value = depth[i];
int reps = 1;
int k;
for (k = i + 1; k < depth_size && depth[k] == value; ++k) {
++reps;
}
int k = i + 1;
int runs;
while (k < depth_size && depth[k] == value) ++k;
runs = k - i;
if (value == 0) {
WriteHuffmanTreeRepetitionsZeros(value, reps,
num_symbols,
tree, extra_bits_data);
tokens = CodeRepeatedZeros(runs, tokens);
} else {
WriteHuffmanTreeRepetitions(value, prev_value, reps,
num_symbols,
tree, extra_bits_data);
tokens = CodeRepeatedValues(runs, tokens, value, prev_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) {
uint32_t retval = 0;
int i;
for (i = 0; i < num_bits; ++i) {
retval <<= 1;
retval |= bits & 1;
bits >>= 1;
int i = 0;
while (i < num_bits) {
i += 4;
retval |= kReversedBits[bits & 0xf] << (MAX_BITS - i);
bits >>= 4;
}
retval >>= (MAX_BITS - num_bits);
return retval;
}
void VP8LConvertBitDepthsToSymbols(const uint8_t* depth, int len,
uint16_t* bits) {
// This function is based on RFC 1951.
//
// 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 };
void VP8LConvertBitDepthsToSymbols(const uint8_t* const depth,
int len,
uint16_t* const bits) {
// 0 bit-depth means that the symbol does not exist.
int i;
{
for (i = 0; i < len; ++i) {
++bl_count[depth[i]];
}
bl_count[0] = 0;
uint32_t next_code[MAX_BITS];
int depth_count[MAX_BITS] = { 0 };
for (i = 0; i < len; ++i) {
assert(depth[i] < MAX_BITS);
++depth_count[depth[i]];
}
depth_count[0] = 0; // ignore unused symbol
next_code[0] = 0;
{
int code = 0;
int bits;
for (bits = 1; bits < MAX_BITS; ++bits) {
code = (code + bl_count[bits - 1]) << 1;
next_code[bits] = code;
uint32_t code = 0;
for (i = 1; i < MAX_BITS; ++i) {
code = (code + depth_count[i - 1]) << 1;
next_code[i] = code;
}
}
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

View File

@ -7,7 +7,7 @@
//
// 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_
#define WEBP_UTILS_HUFFMAN_ENCODE_H_
@ -20,27 +20,29 @@
extern "C" {
#endif
// This function will create a Huffman tree.
// Create a Huffman tree.
//
// 'histogram' contains the population counts.
// 'tree_depth_limit' is the maximum bit depth of the Huffman codes.
// The created tree is returned as 'bit_depths', which stores how many bits are
// used for each symbol.
// See http://en.wikipedia.org/wiki/Huffman_coding
// Returns 0 on memory error.
int VP8LCreateHuffmanTree(const int* const histogram, int histogram_size,
int tree_depth_limit, uint8_t* const bit_depths);
// (data,length): population counts.
// tree_limit: maximum bit depth (inclusive) of the codes.
// bit_depths[]: how many bits are used for the symbol.
//
// Returns 0 when an error has occured.
int VP8LCreateHuffmanTree(const int* data, const int length,
const int tree_limit, uint8_t* bit_depths);
// Write a huffman tree from bit depths. The generated Huffman tree is
// compressed once more using a Huffman tree.
void VP8LCreateCompressedHuffmanTree(const uint8_t* const depth, int len,
int* num_symbols,
uint8_t* tree,
uint8_t* extra_bits_data);
// Turn the Huffman tree into a token sequence.
// Returns the number of tokens used.
typedef struct {
uint8_t code; // value (0..15) or escape code (16,17,18)
uint8_t extra_bits; // extra bits for escape codes
} 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.
void VP8LConvertBitDepthsToSymbols(const uint8_t* depth, int len,
uint16_t* bits);
void VP8LConvertBitDepthsToSymbols(const uint8_t* const depth, int len,
uint16_t* const bits);
#if defined(__cplusplus) || defined(c_plusplus)
}