Skip to content

Commit

Permalink
T1: fix BYPASS/LAZY, TERMALL/RESTART and PTERM/ERTERM encoding modes. (
Browse files Browse the repository at this point in the history
…uclouvain#674)

There were a number of defects regarding when and how the termination of
passes had to done and the computation of their rate.
  • Loading branch information
rouault committed Jun 9, 2017
1 parent 53d46fc commit 81c5311
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 80 deletions.
111 changes: 85 additions & 26 deletions src/lib/openjp2/mqc.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,10 @@ static opj_mqc_state_t mqc_states[47 * 2] = {

static void opj_mqc_byteout(opj_mqc_t *mqc)
{
/* avoid accessing uninitialized memory*/
if (mqc->bp == mqc->start - 1) {
mqc->bp++;
*mqc->bp = (OPJ_BYTE)(mqc->c >> 19);
mqc->c &= 0x7ffff;
mqc->ct = 8;
} else if (*mqc->bp == 0xff) {
/* bp is initialized to start - 1 in opj_mqc_init_enc() */
/* but this is safe, see opj_tcd_code_block_enc_allocate_data() */
assert(mqc->bp >= mqc->start - 1);
if (*mqc->bp == 0xff) {
mqc->bp++;
*mqc->bp = (OPJ_BYTE)(mqc->c >> 20);
mqc->c &= 0xfffff;
Expand Down Expand Up @@ -296,12 +293,23 @@ OPJ_UINT32 opj_mqc_numbytes(opj_mqc_t *mqc)

void opj_mqc_init_enc(opj_mqc_t *mqc, OPJ_BYTE *bp)
{
/* TODO MSD: need to take a look to the v2 version */
/* To avoid the curctx pointer to be dangling, but not strictly */
/* required as the current context is always set before encoding */
opj_mqc_setcurctx(mqc, 0);

/* As specified in Figure C.10 - Initialization of the encoder */
/* (C.2.8 Initialization of the encoder (INITENC)) */
mqc->a = 0x8000;
mqc->c = 0;
/* Yes, we point before the start of the buffer, but this is safe */
/* given opj_tcd_code_block_enc_allocate_data() */
mqc->bp = bp - 1;
mqc->ct = 12;
/* At this point we should test *(mqc->bp) against 0xFF, but this is not */
/* necessary, as this is only used at the beginning of the code block */
/* and our initial fake byte is set at 0 */
assert(*(mqc->bp) != 0xff);

mqc->start = bp;
}

Expand All @@ -316,60 +324,102 @@ void opj_mqc_encode(opj_mqc_t *mqc, OPJ_UINT32 d)

void opj_mqc_flush(opj_mqc_t *mqc)
{
/* C.2.9 Termination of coding (FLUSH) */
/* Figure C.11 – FLUSH procedure */
opj_mqc_setbits(mqc);
mqc->c <<= mqc->ct;
opj_mqc_byteout(mqc);
mqc->c <<= mqc->ct;
opj_mqc_byteout(mqc);

/* It is forbidden that a coding pass ends with 0xff */
if (*mqc->bp != 0xff) {
/* Advance pointer so that opj_mqc_numbytes() returns a valid value */
mqc->bp++;
}
}

#define BYPASS_CT_INIT 0xDEADBEEF

void opj_mqc_bypass_init_enc(opj_mqc_t *mqc)
{
/* This function is normally called after at least one opj_mqc_flush() */
/* which will have advance mqc->bp by at least 2 bytes beyond its */
/* initial position */
assert(mqc->bp >= mqc->start);
mqc->c = 0;
mqc->ct = 8;
/*if (*mqc->bp == 0xff) {
mqc->ct = 7;
} */
/* in theory we should initialize to 8, but use this special value */
/* as a hint that opj_mqc_bypass_enc() has never been called, so */
/* as to avoid the 0xff 0x7f elimination trick in opj_mqc_bypass_flush_enc() */
/* to trigger when we don't have output any bit during this bypass sequence */
/* Any value > 8 will do */
mqc->ct = BYPASS_CT_INIT;
/* Given that we are called after opj_mqc_flush(), the previous byte */
/* cannot be 0xff. */
assert(mqc->bp[-1] != 0xff);
}

void opj_mqc_bypass_enc(opj_mqc_t *mqc, OPJ_UINT32 d)
{
if (mqc->ct == BYPASS_CT_INIT) {
mqc->ct = 8;
}
mqc->ct--;
mqc->c = mqc->c + (d << mqc->ct);
if (mqc->ct == 0) {
mqc->bp++;
*mqc->bp = (OPJ_BYTE)mqc->c;
mqc->ct = 8;
/* If the previous byte was 0xff, make sure that the next msb is 0 */
if (*mqc->bp == 0xff) {
mqc->ct = 7;
}
mqc->bp++;
mqc->c = 0;
}
}

OPJ_UINT32 opj_mqc_bypass_flush_enc(opj_mqc_t *mqc)
OPJ_UINT32 opj_mqc_bypass_get_extra_bytes(opj_mqc_t *mqc, OPJ_BOOL erterm)
{
OPJ_BYTE bit_padding;

bit_padding = 0;
return (mqc->ct < 7 ||
(mqc->ct == 7 && (erterm || mqc->bp[-1] != 0xff))) ? 1 : 0;
}

if (mqc->ct != 0) {
void opj_mqc_bypass_flush_enc(opj_mqc_t *mqc, OPJ_BOOL erterm)
{
/* Is there any bit remaining to be flushed ? */
/* If the last output byte is 0xff, we can discard it, unless */
/* erterm is required (I'm not completely sure why in erterm */
/* we must output 0xff 0x2a if the last byte was 0xff instead of */
/* discarding it, but Kakadu requires it when decoding */
/* in -fussy mode) */
if (mqc->ct < 7 || (mqc->ct == 7 && (erterm || mqc->bp[-1] != 0xff))) {
OPJ_BYTE bit_value = 0;
/* If so, fill the remaining lsbs with an alternating sequence of */
/* 0,1,... */
/* Note: it seems the standard only requires that for a ERTERM flush */
/* and doesn't specify what to do for a regular BYPASS flush */
while (mqc->ct > 0) {
mqc->ct--;
mqc->c += (OPJ_UINT32)(bit_padding << mqc->ct);
bit_padding = (bit_padding + 1) & 0x01;
mqc->c += (OPJ_UINT32)(bit_value << mqc->ct);
bit_value = (OPJ_BYTE)(1U - bit_value);
}
mqc->bp++;
*mqc->bp = (OPJ_BYTE)mqc->c;
mqc->ct = 8;
mqc->c = 0;
/* Advance pointer so that opj_mqc_numbytes() returns a valid value */
mqc->bp++;
} else if (mqc->ct == 7 && mqc->bp[-1] == 0xff) {
/* Discard last 0xff */
assert(!erterm);
mqc->bp --;
} else if (mqc->ct == 8 && !erterm &&
mqc->bp[-1] == 0x7f && mqc->bp[-2] == 0xff) {
/* Tiny optimization: discard terminating 0xff 0x7f since it is */
/* interpreted as 0xff 0x7f [0xff 0xff] by the decoder, and given */
/* the bit stuffing, in fact as 0xff 0xff [0xff ..] */
/* Happens once on opj_compress -i ../MAPA.tif -o MAPA.j2k -M 1 */
mqc->bp -= 2;
}

return 1;
assert(mqc->bp[-1] != 0xff);
}

void opj_mqc_reset_enc(opj_mqc_t *mqc)
Expand All @@ -380,6 +430,7 @@ void opj_mqc_reset_enc(opj_mqc_t *mqc)
opj_mqc_setstate(mqc, T1_CTXNO_ZC, 0, 4);
}

#ifdef notdef
OPJ_UINT32 opj_mqc_restart_enc(opj_mqc_t *mqc)
{
OPJ_UINT32 correction = 1;
Expand All @@ -396,15 +447,23 @@ OPJ_UINT32 opj_mqc_restart_enc(opj_mqc_t *mqc)

return correction;
}
#endif

void opj_mqc_restart_init_enc(opj_mqc_t *mqc)
{
/* <Re-init part> */
opj_mqc_setcurctx(mqc, 0);

/* As specified in Figure C.10 - Initialization of the encoder */
/* (C.2.8 Initialization of the encoder (INITENC)) */
mqc->a = 0x8000;
mqc->c = 0;
mqc->ct = 12;
mqc->bp--;
/* This function is normally called after at least one opj_mqc_flush() */
/* which will have advance mqc->bp by at least 2 bytes beyond its */
/* initial position */
mqc->bp --;
assert(mqc->bp >= mqc->start - 1);
assert(*mqc->bp != 0xff);
if (*mqc->bp == 0xff) {
mqc->ct = 13;
}
Expand Down
19 changes: 14 additions & 5 deletions src/lib/openjp2/mqc.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,36 +142,45 @@ void opj_mqc_flush(opj_mqc_t *mqc);
/**
BYPASS mode switch, initialization operation.
JPEG 2000 p 505.
<h2>Not fully implemented and tested !!</h2>
@param mqc MQC handle
*/
void opj_mqc_bypass_init_enc(opj_mqc_t *mqc);

/** Return number of extra bytes to add to opj_mqc_numbytes() for the²
size of a non-terminating BYPASS pass
@param mqc MQC handle
@param erterm 1 if ERTERM is enabled, 0 otherwise
*/
OPJ_UINT32 opj_mqc_bypass_get_extra_bytes(opj_mqc_t *mqc, OPJ_BOOL erterm);

/**
BYPASS mode switch, coding operation.
JPEG 2000 p 505.
<h2>Not fully implemented and tested !!</h2>
@param mqc MQC handle
@param d The symbol to be encoded (0 or 1)
*/
void opj_mqc_bypass_enc(opj_mqc_t *mqc, OPJ_UINT32 d);
/**
BYPASS mode switch, flush operation
<h2>Not fully implemented and tested !!</h2>
@param mqc MQC handle
@return Returns 1 (always)
@param erterm 1 if ERTERM is enabled, 0 otherwise
*/
OPJ_UINT32 opj_mqc_bypass_flush_enc(opj_mqc_t *mqc);
void opj_mqc_bypass_flush_enc(opj_mqc_t *mqc, OPJ_BOOL erterm);
/**
RESET mode switch
@param mqc MQC handle
*/
void opj_mqc_reset_enc(opj_mqc_t *mqc);

#ifdef notdef
/**
RESTART mode switch (TERMALL)
@param mqc MQC handle
@return Returns 1 (always)
*/
OPJ_UINT32 opj_mqc_restart_enc(opj_mqc_t *mqc);
#endif

/**
RESTART mode switch (TERMALL) reinitialisation
@param mqc MQC handle
Expand Down
Loading

0 comments on commit 81c5311

Please sign in to comment.