Table of Contents

NAME

QccENTArithmeticEncodeStart, QccENTArithmeticEncodeEnd, QccENTArithmeticEncode, QccENTArithmeticEncodeFlush, QccENTArithmeticDecodeStart, QccENTArithmeticDecodeRestart, QccENTArithmeticDecode - arithmetic encoding and decoding

SYNOPSIS

#include "libQccPack.h"

QccENTArithmeticModel *QccENTArithmeticEncodeStart(const int *num_symbols, int num_contexts, QccENTArithmeticGetContext context_function, int target_num_bits);
int QccENTArithmeticEncodeEnd(QccENTArithmeticModel *model, int final_context, QccBitBuffer *output_buffer);
int QccENTArithmeticEncode(const int *symbol_stream, int symbol_stream_length, QccENTArithmeticModel *model, QccBitBuffer *output_buffer);
int QccENTArithmeticEncodeFlush(QccENTArithmeticModel *model, QccBitBuffer *output_buffer);
QccENTArithmeticModel *QccENTArithmeticDecodeStart(QccBitBuffer *input_buffer, const int *num_symbols, int num_contexts, QccENTArithmeticGetContext context_function, int target_num_bits);
int QccENTArithmeticDecodeRestart(QccBitBuffer *input_buffer, QccENTArithmeticModel *model, int target_num_bits);
int QccENTArithmeticDecode(QccBitBuffer *input_buffer, QccENTArithmeticModel *model, int *symbol_stream, int symbol_stream_length);

DESCRIPTION

QccPack provides multiple-context adaptive and nonadaptive arithmetic coding with an arbitrary number of symbols. For both encoding and decoding, an arithmetic coding model of type QccENTArithmeticModel(3) is allocated. This model contains the probability models for the different contexts which are, by default, adapted as coding and decoding progresses. The QccENTArithmeticModel(3) structure can also contain a pointer to a callback function which is used to obtain the context of the current symbol during encoding and decoding, and the adaptiveness of the model can be enabled and disabled via calls to QccENTArithmeticSetModelAdaption(3) .

In general, in both encoding and decoding, a num_contexts value is specified at the time of arithmetic-coding model creation, and this value indicates the number of contexts to be used for coding and decoding. num_contexts must be 1 or greater. Additionally, an array, num_symbols, is also passed at the time of model creation. num_symbols is an array of num_context integers which indicates the number of symbols for each context. Finally, during encoding and decoding, valid symbols for context i are integers greater than or equal to 0, and less than or equal num_symbols[i] - 1. The contexts themselves are assumed to be numbered from 0 up to, and including, num_contexts - 1.

Encoding

QccENTArithmeticEncodeStart() should be the first function called in the arithmetic-encoding process. QccENTArithmeticEncodeStart() allocates a QccENTArithmeticModel(3) structure, initializes the probability models for each context in the model, and returns a pointer to the model. num_contexts gives the number of contexts for encoding, num_symbols gives the number of symbols for each context, and context_function is a pointer to a callback function of type QccENTArithmeticGetContext(3) which is called to obtain the current context. context_function can be the NULL pointer, in which case the current_context field of the arithmetic model is used to set the current context directly by a calling routine. If target_num_bits is different from QCCENT_ANYNUMBITS, then the arithmetic coder can be forced to stop outputting bits when the total number of bits output to the bitstream (i.e., output_buffer->bit_cnt) is equal to target_num_bits. Note that this is a global stopping point; i.e., any bits output before the call to QccENTArithmeticEncodeStart() are included when comparing the bits output to target_num_bits. For example, when a binary header via calls to functions like QccBitBufferPutChar(3) is output before starting the arithmetic coder, these header bits are included in the total number of bits counted. If target_num_bits is set to QCCENT_ANYNUMBITS, then the arithmetic encoded output can have any number of bits output and should be explicitly terminated via a call to QccENTArithmeticEncodeEnd().

QccENTArithmeticEncode() performs arithmetic encoding of the stream of symbols, symbol_stream, which is an array of valid symbols. symbol_stream_length gives the length of the stream to be encoded, while model is the pointer to the QccENTArithmeticModel structure previously returned by the call to QccENTArithmeticEncodeStart(). Finally, output_buffer is a pointer to a QccBitBuffer(3) structure; this output_buffer must already be opened for writing via a prior call to QccBitBufferStart(3) . If target_num_bits, as set during the initial call to QccENTArithmeticEncodeStart(), is not equal to QCCENT_ANYNUMBITS, then QccENTArithmeticEncode() will stop outputting bits and return prematurely when output_buffer->bit_cnt is equal to target_num_bits, regardless of whether all symbols in symbol_stream have been processed or not. If target_num_bits is equal to QCCENT_ANYNUMBITS, then QccENTArithmeticEncode() will return only when all symbols in symbol_stream have been processed. If QccENTArithmeticEncode() returns prematurely due to target_num_bits being output, then QccENTArithmeticEncode() returns with a value of 2; otherwise, it returns a 0 on success, or a 1 on failure.

QccENTArithmeticEncodeEnd() terminates arithmetic encoding by encoding a special EOF symbol to the bitstream, and then calling QccENTArithmeticEncodeFlush(). final_context is the context that is to be used for the EOF symbol. If the num_contexts in the arithmetic model is 1 (i.e., single-context coding), the value of final_context is ignored.

QccENTArithmeticEncodeFlush() flushes the state of the arithmetic encoder. Usually, after the last symbol is processed by QccENTArithmeticEncode(), there are several bits that remain to be output from the arithmetic encoder. QccENTArithmeticEncodeFlush() forces these bits to be output, and then calls QccBitBufferFlush(3) to guarantee that the bits are actually written to the output file. After a call to QccENTArithmeticEncodeFlush(), subsequent bits output will start on the next byte boundary of the output file; consequently, one can follow a call to QccENTArithmeticEncodeFlush() with a subsequent call to QccENTArithmeticEncode() to force encoded "blocks" to be "byte-aligned." Note: if such byte-alignment is done during encoding, during decoding, the decoder must be "restart" at the start of each block with a call to QccENTArithmeticDecodeRestart() (see below).

Decoding

QccENTArithmeticDecodeStart() should be the first function called in the arithmetic-decoding process. For the decoding to make sense, the values of num_contexts, and num_symbols should be the same as used in encoding, and context_function should be the same function used in encoding. target_num_bits can be used to force QccENTArithmeticDecode() to return prematurely when input_buffer->bit_cnt meets or exceeds target_num_bits.

QccENTArithmeticDecode() attempts to decode symbol_stream_length symbols from input_buffer. The decoded symbols are placed in the array symbol_stream which must be allocated with space sufficient for symbol_stream_length symbols prior to the call to QccENTArithmeticDecode(). If target_num_bits was not equal to QCCENT_ANYNUMBITS with the immediately preceding call to QccENTArithmeticDecodeStart() or QccENTArithmeticDecodeRestart(), then QccENTArithmeticDecode() will return prematurely when the input_buffer->bit_cnt meets or exceeds target_num_bits. That is, QccENTArithmeticDecode() will not read past the target_num_bits bit in the bitstream, even if symbol_stream_length symbols have not been decoded. If, on the other hand, target_num_bits is equal to QCCENT_ANYNUMBITS, QccENTArithmeticDecode() will return when symbol_stream_length symbols are decoded. QccENTArithmeticDecode() returns 2 if the special EOF symbol is encountered while decoding; 1 if the end of the bitstream is reached while decoding; 1 if the target_num_bits bit limit is reached; or 0 if no error occurs.

QccENTArithmeticDecodeRestart() can be used to start the arithmetic decoder on any byte boundary. That is, if, during encoding, QccENTArithmeticEncodeFlush() is used to force byte alignment of "blocks" of bits, then one can use fseek(3) to position the bitstream to the byte at the start of the block, and then call QccENTArithmeticDecodeRestart() to restart the decoder at the byte-aligned boundary. In this case, target_num_bits can be set to input_buffer -> bit_cnt plus the length (in bits) of the block to stop the decoder again after the block has been decoded.

MODEL ADAPTION

Arithmetic coding can be either adaptive, in which the frequency-count information in the arithmetic model is updated after each symbol is encoded, or nonadaptive, in which the frequency counts stay static. By default, when a model is created, it is set to be an adaptive model, but one can enable or disable adaption at any time via calls to QccENTArithmeticSetModelAdaption(3) .

For a nonadaptive model, one can explicitly ("manually") change the frequency-count information in the model by passing a probability distribution to the model via a call to QccENTArithmeticSetModelProbabilities(3) .

RETURN VALUE

QccENTArithmeticEncode() returns 0 on success, 1 on failure, or 2 in the case that encoding is terminated prematurely due to target_num_bits being output.

QccENTArithmeticDecode() returns 0 on success, 1 on failure to read all requested symbols, or 2 if the special EOF symbol is encountered.

QccENTArithmeticEncodeStart() and QccENTArithmeticDecodeStart() return NULL on error, or a pointer to a QccENTArithmeticModel(3) structure on success.

QccENTArithmeticDecodeRestart() and QccENTArithmeticEncodeFlush() return 0 on success, and 1 on failure.

EXAMPLES

Single-Context Adaptive Arithmetic Coding

Straightforward single-context adaptive encoding.

Encoder:

/* ENCODER */

#include "QccPack.h"

main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer output_buffer;
QccENTArithmeticModel *model = NULL;

QccInit(argc, argv);
QccBitBufferInitialize(&output_buffer);

/* obtain symbols here */

output_buffer.type = QCCBITBUFFER_OUTPUT;
if (QccBitBufferStart(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}

if ((model = QccENTArithmeticEncodeStart(&NUM_SYMBOLS,
1,
NULL,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeStart()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticEncode(symbol_stream,
SYMBOL_STREAM_LENGTH,
model,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncode()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticEncodeEnd(model,
0,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeEnd()");
QccErrorExit();
}

if (QccBitBufferEnd(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}

QccENTArithmeticFreeModel(model);

QccExit;
}

Decoder:

/* DECODER */

#include "QccPack.h"

main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer input_buffer;
QccENTArithmeticModel *model = NULL;

QccInit(argc, argv);
QccBitBufferInitialize(&input_buffer);

input_buffer.type = QCCBITBUFFER_INPUT;
if (QccBitBufferStart(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}

if ((model = QccENTArithmeticDecodeStart(&input_buffer,
&NUM_SYMBOLS,
1,
NULL,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecodeStart()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticDecode(&input_buffer,
model,
symbol_stream,
SYMBOL_STREAM_LENGTH))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecode()",
argv[0]);
QccErrorExit();
}

if (QccBitBufferEnd(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}

QccENTArithmeticFreeModel(model);

/* output symbols stream here */

QccExit;
}

Simple Multiple-Context Adaptive Arithmetic Coding

Multiple-context adaptive arithmetic coding in which there are two contexts, each with the same number of symbols. In this example, the symbols are coded in two blocks; the first block is encoded with the first context, and the second block is encoded with the second context. The trailing EOF symbol is output in the second context.

Encoder:

/* ENCODER */

#include "QccPack.h"

#define FIRST_CONTEXT 0
#define SECOND_CONTEXT 1

main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer output_buffer;
QccENTArithmeticModel *model = NULL;

int num_contexts = 2;
int num_symbols[2];

QccInit(argc, argv);
QccBitBufferInitialize(&output_buffer);

for (context = 0; context < 2; context++)
num_symbols[context] = NUM_SYMBOLS;

/* obtain symbols here */

output_buffer.type = QCCBITBUFFER_OUTPUT;
if (QccBitBufferStart(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}

if ((model = QccENTArithmeticEncodeStart(num_symbols,
2,
NULL,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeStart()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticSetModelContext(model, FIRST_CONTEXT))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticSetModelContext()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticEncode(symbol_stream,
FIRST_BLOCK_LENGTH,
model,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncode()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticSetModelContext(model, SECOND_CONTEXT))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticSetModelContext()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticEncode(&symbol_stream[FIRST_BLOCK_LENGTH],
SECOND_BLOCK_LENGTH,
model,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncode()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticEncodeEnd(model,
SECOND_CONTEXT,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeEnd()");
QccErrorExit();
}

if (QccBitBufferEnd(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}

QccENTArithmeticFreeModel(model);

QccExit;
}

Decoder:

/* DECODER */

#include "QccPack.h"

#define FIRST_CONTEXT 0
#define SECOND_CONTEXT 1

main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer input_buffer;
QccENTArithmeticModel *model = NULL;

int num_contexts = 2;
int num_symbols[2];

QccInit(argc, argv);
QccBitBufferInitialize(&input_buffer);

for (context = 0; context < 2; context++)
num_symbols[context] = NUM_SYMBOLS;

input_buffer.type = QCCBITBUFFER_INPUT;
if (QccBitBufferStart(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}

if ((model = QccENTArithmeticDecodeStart(&input_buffer,
num_symbols,
2,
NULL,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecodeStart()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticSetModelContext(model, FIRST_CONTEXT))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticSetModelContext()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticDecode(&input_buffer,
model,
symbol_stream,
FIRST_BLOCK_LENGTH))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecode()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticSetModelContext(model, SECOND_CONTEXT))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticSetModelContext()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticDecode(&input_buffer,
model,
&symbol_stream[FIRST_BLOCK_LENGTH],
SECOND_BLOCK_LENGTH))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecode()",
argv[0]);
QccErrorExit();
}

if (QccBitBufferEnd(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}

QccENTArithmeticFreeModel(model);

/* output symbols stream here */

QccExit;
}

Multiple-Context Adaptive Arithmetic Coding

Multiple-context adaptive arithmetic coding in which the previous symbol is used as the context of the current symbol. This coder would be a reasonable choice for a Markov process. Note that the number of contexts is equal to the number of symbols.

Encoder:

/* ENCODER */

#include "QccPack.h"

int get_current_context(const int *symbol_stream,
int current_symbol)
{
if ((symbol_stream == NULL) || (current_symbol <= 0))
return(0);

return(symbol_stream[current_symbol - 1]);
}

main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer output_buffer;
QccENTArithmeticModel *model = NULL;

int num_contexts = NUM_SYMBOLS;
int num_symbols[NUM_SYMBOLS];

QccInit(argc, argv);
QccBitBufferInitialize(&output_buffer);

for (context = 0; context < NUM_SYMBOLS; context++)
num_symbols[context] = NUM_SYMBOLS;

/* obtain symbols here */

output_buffer.type = QCCBITBUFFER_OUTPUT;
if (QccBitBufferStart(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}

if ((model = QccENTArithmeticEncodeStart(num_symbols,
num_contexts,
get_current_context,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeStart()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticEncode(symbol_stream,
SYMBOL_STREAM_LENGTH,
model,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncode()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticEncodeEnd(model,
0,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeEnd()");
QccErrorExit();
}

if (QccBitBufferEnd(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}

QccENTArithmeticFreeModel(model);

QccExit;
}

Decoder:

/* DECODER */

#include "QccPack.h"

int get_current_context(const int *symbol_stream,
int current_symbol)
{
if ((symbol_stream == NULL) || (current_symbol <= 0))
return(0);

return(symbol_stream[current_symbol - 1]);
}

main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer input_buffer;
QccENTArithmeticModel *model = NULL;

int num_contexts = NUM_SYMBOLS;
int num_symbols[NUM_SYMBOLS];

QccInit(argc, argv);
QccBitBufferInitialize(&input_buffer);

for (context = 0; context < NUM_SYMBOLS; context++)
num_symbols[context] = NUM_SYMBOLS;

input_buffer.type = QCCBITBUFFER_INPUT;
if (QccBitBufferStart(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}

if ((model = QccENTArithmeticDecodeStart(&input_buffer,
num_symbols,
num_contexts,
get_current_context,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecodeStart()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticDecode(&input_buffer,
model,
symbol_stream,
SYMBOL_STREAM_LENGTH))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecode()",
argv[0]);
QccErrorExit();
}

if (QccBitBufferEnd(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}

QccENTArithmeticFreeModel(model);

/* output symbols stream here */

QccExit;
}

Single-Context Nonadaptive Arithmetic Coding

Straightforward single-context nonadaptive encoding. Note that adaptive coding is used by default, so we merely need to disable adaption before coding begins.

Encoder:

/* ENCODER */

#include "QccPack.h"

main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer output_buffer;
QccENTArithmeticModel *model = NULL;
double probabilities[NUM_SYMBOLS];

QccInit(argc, argv);
QccBitBufferInitialize(&output_buffer);

/* obtain symbols and their probabilities somehow here */

output_buffer.type = QCCBITBUFFER_OUTPUT;
if (QccBitBufferStart(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}

if ((model = QccENTArithmeticEncodeStart(&NUM_SYMBOLS,
1,
NULL,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeStart()",
argv[0]);
QccErrorExit();
}

QccENTArithmeticSetModelAdaption(model, QCCENT_NONADAPTIVE);
if (QccENTArithmeticSetModelProbabilities(model,
probabilities,
0))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticSetModelProbabilities()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticEncode(symbol_stream,
SYMBOL_STREAM_LENGTH,
model,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncode()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticEncodeEnd(model,
0,
&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticEncodeEnd()");
QccErrorExit();
}

if (QccBitBufferEnd(&output_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}

QccENTArithmeticFreeModel(model);

QccExit;
}

Decoder:

/* DECODER */

#include "QccPack.h"

main(int argc, char *argv[])
{
int symbol_stream[SYMBOL_STREAM_LENGTH];
QccBitBuffer input_buffer;
QccENTArithmeticModel *model = NULL;
double probabilities[NUM_SYMBOLS];

QccInit(argc, argv);
QccBitBufferInitialize(&input_buffer);

/* obtain symbol probabilities somehow here */

input_buffer.type = QCCBITBUFFER_INPUT;
if (QccBitBufferStart(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferStart()",
argv[0]);
QccErrorExit();
}

if ((model = QccENTArithmeticDecodeStart(&input_buffer,
&NUM_SYMBOLS,
1,
NULL,
QCCENT_ANYNUMBITS)) == NULL)
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecodeStart()",
argv[0]);
QccErrorExit();
}

QccENTArithmeticSetModelAdaption(model, QCCENT_NONADAPTIVE);
if (QccENTArithmeticSetModelProbabilities(model,
probabilities,
0))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticSetModelProbabilities()",
argv[0]);
QccErrorExit();
}

if (QccENTArithmeticDecode(&input_buffer,
model,
symbol_stream,
SYMBOL_STREAM_LENGTH))
{
QccErrorAddMessage("%s: Error calling QccENTArithmeticDecode()",
argv[0]);
QccErrorExit();
}

if (QccBitBufferEnd(&input_buffer))
{
QccErrorAddMessage("%s: Error calling QccBitBufferEnd()",
argv[0]);
QccErrorExit();
}

QccENTArithmeticFreeModel(model);

/* output symbols stream here */

QccExit;
}

NOTES

If QccENTArithmetocEncodeStart() is called using target_num_bits equal to other than QCCENT_ANYNUMBITS in order to output exactly target_num_bits (in which case, QccENTArithmeticEncodeEnd() is usually not called at the end of the bitstream), then the last few symbols decoded by QccENTArithmeticDecode() as it nears the end of the bitstream will be valid symbols, but may not be exactly the same as the last few symbols passed to QccENTArithmeticEncode(). This discrepancy is due to the fact that the arithmetic encoder probably had not completely flushed its state to the bitstream when the target_num_bits threshold was reached. Thus, the decoder may decode a few symbols erroneously before it realizes that it has encountered the end of the bitstream, since the decoder needs the complete encoder state (which is unknown because it was not flushed to the bitstream) to decode accurately. Usually, this is not a problem in applications since the target_num_bits capability is most likely only of use in lossy compression, and the erroneously decoded symbols at the end of the bitstream can be viewed as contributing negligible additional distortion to the lossy representation.

SEE ALSO

QccENTArithmeticModel(3) , QccENTArithmeticGetContext(3) , QccBitBuffer(3) , QccPackENT(3) , QccPack(3)

I. H. Witten, R. M. Neal, and J. G. Cleary, "Arithmetic Coding for Data Compression," Communications of the ACM, vol. 30, no. 6, pp. 520-540, June 1987.

AUTHOR

Copyright (C) 1997-2021 James E. Fowler


Table of Contents



Get QccPack at SourceForge.net. Fast, secure and Free Open Source software downloads