1091 lines
32 KiB
C
1091 lines
32 KiB
C
/*
|
|
* T.85 "light" version of the portable JBIG image compression library
|
|
*
|
|
* Copyright 1995-2014 -- Markus Kuhn -- http://www.cl.cam.ac.uk/~mgk25/
|
|
*
|
|
* This module implements a portable standard C encoder and decoder
|
|
* using the JBIG1 lossless bi-level image compression algorithm
|
|
* specified in International Standard ISO 11544:1993 and
|
|
* ITU-T Recommendation T.82. See the file jbig.txt for usage
|
|
* instructions and application examples.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
* If you want to use this program under different license conditions,
|
|
* then contact the author for an arrangement.
|
|
*/
|
|
|
|
#ifdef DEBUG
|
|
#include <stdio.h>
|
|
#else
|
|
#define NDEBUG
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "jbig85.h"
|
|
|
|
#define TPB2CX 0x195 /* contexts for TP special pixels */
|
|
#define TPB3CX 0x0e5
|
|
|
|
/* marker codes */
|
|
#define MARKER_STUFF 0x00
|
|
#define MARKER_RESERVE 0x01
|
|
#define MARKER_SDNORM 0x02
|
|
#define MARKER_SDRST 0x03
|
|
#define MARKER_ABORT 0x04
|
|
#define MARKER_NEWLEN 0x05
|
|
#define MARKER_ATMOVE 0x06
|
|
#define MARKER_COMMENT 0x07
|
|
#define MARKER_ESC 0xff
|
|
|
|
/* object code version id */
|
|
|
|
const char jbg85_version[] =
|
|
"JBIG-KIT " JBG85_VERSION " (T.85 version) -- (c) 1995-2014 Markus Kuhn -- "
|
|
"Licence: " JBG85_LICENCE "\n";
|
|
|
|
#define _(String) String /* to mark translatable string for GNU gettext */
|
|
|
|
/*
|
|
* Array with English ASCII error messages that correspond
|
|
* to return values from public functions in this library.
|
|
*/
|
|
static const char *errmsg[] = {
|
|
_("All OK"), /* JBG_EOK */
|
|
_("Reached specified image size"), /* JBG_EOK_INTR */
|
|
_("Unexpected end of input data stream"), /* JBG_EAGAIN */
|
|
_("Not enough memory available"), /* JBG_ENOMEM */
|
|
_("ABORT marker segment encountered"), /* JBG_EABORT */
|
|
_("Unknown marker segment encountered"), /* JBG_EMARKER */
|
|
_("Input data stream contains invalid data"), /* JBG_EINVAL */
|
|
_("Input data stream uses unimplemented JBIG features") /* JBG_EIMPL */
|
|
};
|
|
|
|
|
|
/*
|
|
* Callback adapter function for arithmetic encoder
|
|
*/
|
|
static void enc_byte_out(int byte, void *s)
|
|
{
|
|
unsigned char c = byte;
|
|
((struct jbg85_enc_state *)s)->data_out(&c, sizeof(unsigned char),
|
|
((struct jbg85_enc_state *)s)->file);
|
|
}
|
|
|
|
|
|
/*
|
|
* Initialize the status struct for the encoder.
|
|
*/
|
|
void jbg85_enc_init(struct jbg85_enc_state *s,
|
|
unsigned long x0, unsigned long y0,
|
|
void (*data_out)(unsigned char *start, size_t len,
|
|
void *file),
|
|
void *file)
|
|
{
|
|
assert(x0 > 0 && y0 > 0);
|
|
s->x0 = x0;
|
|
s->y0 = y0;
|
|
s->newlen = 0; /* no NEWLEN pending or output */
|
|
s->data_out = data_out;
|
|
s->file = file;
|
|
|
|
s->l0 = s->y0 / 35; /* 35 stripes/image suggested default */
|
|
if (s->l0 > 128) s->l0 = 128;
|
|
else if (s->l0 < 2) s->l0 = 2;
|
|
#if 1
|
|
s->l0 = 128; /* T.85 BASIC setting */
|
|
#endif
|
|
s->mx = 127;
|
|
s->new_tx = -1; /* no ATMOVE pending */
|
|
s->tx = 0;
|
|
s->options = JBG_TPBON | JBG_VLENGTH;
|
|
s->comment = NULL; /* no COMMENT pending */
|
|
s->y = 0;
|
|
s->i = 0;
|
|
s->ltp_old = 0;
|
|
|
|
/* initialize arithmetic encoder */
|
|
arith_encode_init(&s->s, 0);
|
|
s->s.byte_out = &enc_byte_out;
|
|
s->s.file = s;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* The following function allows to specify the bits describing the
|
|
* options of the format as well as the maximum AT movement window and
|
|
* the number of layer 0 lines per stripes.
|
|
*/
|
|
void jbg85_enc_options(struct jbg85_enc_state *s, int options,
|
|
unsigned long l0, int mx)
|
|
{
|
|
if (s->y > 0) return; /* too late to change anything now */
|
|
|
|
if (options >= 0) s->options = options;
|
|
if (l0 > 0) s->l0 = l0;
|
|
if (mx >= 0 && mx < 128) s->mx = mx;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* auxiliary routine to write out NEWLEN */
|
|
static void output_newlen(struct jbg85_enc_state *s)
|
|
{
|
|
unsigned char buf[6];
|
|
|
|
assert(s->i == 0);
|
|
if (s->newlen != 1)
|
|
return;
|
|
buf[0] = MARKER_ESC;
|
|
buf[1] = MARKER_NEWLEN;
|
|
buf[2] = s->y0 >> 24;
|
|
buf[3] = (s->y0 >> 16) & 0xff;
|
|
buf[4] = (s->y0 >> 8) & 0xff;
|
|
buf[5] = s->y0 & 0xff;
|
|
s->data_out(buf, 6, s->file);
|
|
s->newlen = 2;
|
|
if (s->y == s->y0) {
|
|
/* if newlen refers to a line in the preceeding stripe, ITU-T T.82
|
|
* section 6.2.6.2 requires us to append another SDNORM */
|
|
buf[1] = MARKER_SDNORM;
|
|
s->data_out(buf, 2, s->file);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Encode one full BIE and pass the generated data to the specified
|
|
* call-back function
|
|
*/
|
|
void jbg85_enc_lineout(struct jbg85_enc_state *s, unsigned char *line,
|
|
unsigned char *prevline, unsigned char *prevprevline)
|
|
{
|
|
unsigned char buf[20];
|
|
unsigned long bpl;
|
|
unsigned char *hp1, *hp2, *hp3, *p1, *q1;
|
|
unsigned long line_h1 = 0, line_h2, line_h3;
|
|
unsigned long j; /* loop variable for pixel column */
|
|
long o;
|
|
unsigned a, p, t;
|
|
int ltp;
|
|
unsigned long cmin, cmax, clmin, clmax;
|
|
int tmax;
|
|
#ifdef DEBUG
|
|
static long tp_lines;
|
|
static long encoded_pixels;
|
|
#endif
|
|
|
|
if (s->y >= s->y0) {
|
|
/* we have already output the full image, go away */
|
|
return;
|
|
}
|
|
|
|
/* line 0 has no previous line */
|
|
if (s->y < 1)
|
|
prevline = NULL;
|
|
if (s->y < 2)
|
|
prevprevline = NULL;
|
|
|
|
/* things that need to be done before the first line is encoded */
|
|
if (s->y == 0) {
|
|
/* prepare BIH */
|
|
buf[0] = 0; /* DL = initial layer to be transmitted */
|
|
buf[1] = 0; /* D = number of differential layers */
|
|
buf[2] = 1; /* P = number of bit planes */
|
|
buf[3] = 0;
|
|
buf[4] = s->x0 >> 24;
|
|
buf[5] = (s->x0 >> 16) & 0xff;
|
|
buf[6] = (s->x0 >> 8) & 0xff;
|
|
buf[7] = s->x0 & 0xff;
|
|
buf[8] = s->y0 >> 24;
|
|
buf[9] = (s->y0 >> 16) & 0xff;
|
|
buf[10] = (s->y0 >> 8) & 0xff;
|
|
buf[11] = s->y0 & 0xff;
|
|
buf[12] = s->l0 >> 24;
|
|
buf[13] = (s->l0 >> 16) & 0xff;
|
|
buf[14] = (s->l0 >> 8) & 0xff;
|
|
buf[15] = s->l0 & 0xff;
|
|
buf[16] = s->mx;
|
|
buf[17] = 0; /* MY = maximum vertical offset allowed for AT pixel */
|
|
buf[18] = 0; /* order: HITOLO = SEQ = ILEAVE = SMID = 0 */
|
|
buf[19] = s->options & (JBG_LRLTWO | JBG_VLENGTH | JBG_TPBON);
|
|
|
|
/* output BIH */
|
|
s->data_out(buf, 20, s->file);
|
|
}
|
|
|
|
/* things that need to be done before the next SDE is encoded */
|
|
if (s->i == 0) {
|
|
|
|
/* output NEWLEN if there is any pending */
|
|
output_newlen(s);
|
|
|
|
/* output comment marker segment if there is any pending */
|
|
if (s->comment) {
|
|
buf[0] = MARKER_ESC;
|
|
buf[1] = MARKER_COMMENT;
|
|
buf[2] = s->comment_len >> 24;
|
|
buf[3] = (s->comment_len >> 16) & 0xff;
|
|
buf[4] = (s->comment_len >> 8) & 0xff;
|
|
buf[5] = s->comment_len & 0xff;
|
|
s->data_out(buf, 6, s->file);
|
|
s->data_out(s->comment, s->comment_len, s->file);
|
|
s->comment = NULL;
|
|
}
|
|
|
|
/* output ATMOVE if there is any pending */
|
|
if (s->new_tx != -1 && s->new_tx != s->tx) {
|
|
s->tx = s->new_tx;
|
|
buf[0] = MARKER_ESC;
|
|
buf[1] = MARKER_ATMOVE;
|
|
buf[2] = 0;
|
|
buf[3] = 0;
|
|
buf[4] = 0;
|
|
buf[5] = 0;
|
|
buf[6] = s->tx;
|
|
buf[7] = 0;
|
|
s->data_out(buf, 8, s->file);
|
|
}
|
|
|
|
/* initialize adaptive template movement algorithm */
|
|
if (s->mx == 0) {
|
|
s->new_tx = 0; /* ATMOVE has been disabled */
|
|
} else {
|
|
s->c_all = 0;
|
|
for (t = 0; t <= s->mx; t++)
|
|
s->c[t] = 0;
|
|
s->new_tx = -1; /* we have yet to determine ATMOVE ... */
|
|
}
|
|
|
|
/* restart arithmetic encoder */
|
|
arith_encode_init(&s->s, 1);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (s->y == 0)
|
|
tp_lines = encoded_pixels = 0;
|
|
fprintf(stderr, "encode line %lu (%2lu of stripe)\n", s->y, s->i);
|
|
#endif
|
|
|
|
/* bytes per line */
|
|
bpl = (s->x0 >> 3) + !!(s->x0 & 7);
|
|
/* ensure correct zero padding of bitmap at the final byte of each line */
|
|
if (s->x0 & 7) {
|
|
line[bpl - 1] &= ~((1 << (8 - (s->x0 & 7))) - 1);
|
|
}
|
|
|
|
/* typical prediction */
|
|
ltp = 0;
|
|
if (s->options & JBG_TPBON) {
|
|
p1 = line;
|
|
q1 = prevline;
|
|
ltp = 1;
|
|
if (q1)
|
|
while (p1 < line + bpl && (ltp = (*p1++ == *q1++)) != 0) ;
|
|
else
|
|
while (p1 < line + bpl && (ltp = (*p1++ == 0 )) != 0) ;
|
|
arith_encode(&s->s, (s->options & JBG_LRLTWO) ? TPB2CX : TPB3CX,
|
|
ltp == s->ltp_old);
|
|
#ifdef DEBUG
|
|
tp_lines += ltp;
|
|
#endif
|
|
s->ltp_old = ltp;
|
|
}
|
|
|
|
if (!ltp) {
|
|
|
|
/*
|
|
* Layout of the variables line_h1, line_h2, line_h3, which contain
|
|
* as bits the neighbour pixels of the currently coded pixel X:
|
|
*
|
|
* 76543210765432107654321076543210 line_h3
|
|
* 76543210765432107654321076543210 line_h2
|
|
* 76543210765432107654321X76543210 line_h1
|
|
*/
|
|
|
|
/* pointer to first image byte of the three lines of interest */
|
|
hp3 = prevprevline;
|
|
hp2 = prevline;
|
|
hp1 = line;
|
|
|
|
line_h1 = line_h2 = line_h3 = 0;
|
|
if (hp2) line_h2 = (long)*hp2 << 8;
|
|
if (hp3) line_h3 = (long)*hp3 << 8;
|
|
|
|
/* encode line */
|
|
for (j = 0; j < s->x0;) {
|
|
line_h1 |= *hp1;
|
|
if (j < bpl * 8 - 8 && hp2) {
|
|
line_h2 |= *(hp2 + 1);
|
|
if (hp3)
|
|
line_h3 |= *(hp3 + 1);
|
|
}
|
|
if (s->options & JBG_LRLTWO) {
|
|
/* two line template */
|
|
do {
|
|
line_h1 <<= 1; line_h2 <<= 1; line_h3 <<= 1;
|
|
if (s->tx) {
|
|
if ((unsigned) s->tx > j)
|
|
a = 0;
|
|
else {
|
|
o = (j - s->tx) - (j & ~7L);
|
|
a = (hp1[o >> 3] >> (7 - (o & 7))) & 1;
|
|
a <<= 4;
|
|
}
|
|
assert(s->tx > 23 ||
|
|
a == ((line_h1 >> (4 + s->tx)) & 0x010));
|
|
arith_encode(&s->s, (((line_h2 >> 10) & 0x3e0) | a |
|
|
((line_h1 >> 9) & 0x00f)),
|
|
(line_h1 >> 8) & 1);
|
|
}
|
|
else
|
|
arith_encode(&s->s, (((line_h2 >> 10) & 0x3f0) |
|
|
((line_h1 >> 9) & 0x00f)),
|
|
(line_h1 >> 8) & 1);
|
|
#ifdef DEBUG
|
|
encoded_pixels++;
|
|
#endif
|
|
/* statistics for adaptive template changes */
|
|
if (s->new_tx == -1 && j >= s->mx && j < s->x0 - 2) {
|
|
p = (line_h1 & 0x100) != 0; /* current pixel value */
|
|
s->c[0] += ((line_h2 & 0x4000) != 0) == p; /* default position */
|
|
assert(!(((line_h2 >> 6) ^ line_h1) & 0x100) ==
|
|
(((line_h2 & 0x4000) != 0) == p));
|
|
for (t = 5; t <= s->mx && t <= j; t++) {
|
|
o = (j - t) - (j & ~7L);
|
|
a = (hp1[o >> 3] >> (7 - (o & 7))) & 1;
|
|
assert(t > 23 ||
|
|
(a == p) == !(((line_h1 >> t) ^ line_h1) & 0x100));
|
|
s->c[t] += a == p;
|
|
}
|
|
for (; t <= s->mx; t++) {
|
|
s->c[t] += 0 == p;
|
|
}
|
|
++s->c_all;
|
|
}
|
|
} while (++j & 7 && j < s->x0);
|
|
} else {
|
|
/* three line template */
|
|
do {
|
|
line_h1 <<= 1; line_h2 <<= 1; line_h3 <<= 1;
|
|
if (s->tx) {
|
|
if ((unsigned) s->tx > j)
|
|
a = 0;
|
|
else {
|
|
o = (j - s->tx) - (j & ~7L);
|
|
a = (hp1[o >> 3] >> (7 - (o & 7))) & 1;
|
|
a <<= 2;
|
|
}
|
|
assert(s->tx > 23 ||
|
|
a == ((line_h1 >> (6 + s->tx)) & 0x004));
|
|
arith_encode(&s->s, (((line_h3 >> 8) & 0x380) |
|
|
((line_h2 >> 12) & 0x078) | a |
|
|
((line_h1 >> 9) & 0x003)),
|
|
(line_h1 >> 8) & 1);
|
|
} else
|
|
arith_encode(&s->s, (((line_h3 >> 8) & 0x380) |
|
|
((line_h2 >> 12) & 0x07c) |
|
|
((line_h1 >> 9) & 0x003)),
|
|
(line_h1 >> 8) & 1);
|
|
#ifdef DEBUG
|
|
encoded_pixels++;
|
|
#endif
|
|
/* statistics for adaptive template changes */
|
|
if (s->new_tx == -1 && j >= s->mx && j < s->x0 - 2) {
|
|
p = (line_h1 & 0x100) != 0; /* current pixel value */
|
|
s->c[0] += ((line_h2 & 0x4000) != 0) == p; /* default position */
|
|
assert(!(((line_h2 >> 6) ^ line_h1) & 0x100) ==
|
|
(((line_h2 & 0x4000) != 0) == p));
|
|
for (t = 3; t <= s->mx && t <= j; t++) {
|
|
o = (j - t) - (j & ~7L);
|
|
a = (hp1[o >> 3] >> (7 - (o & 7))) & 1;
|
|
assert(t > 23 ||
|
|
(a == p) == !(((line_h1 >> t) ^ line_h1) & 0x100));
|
|
s->c[t] += a == p;
|
|
}
|
|
for (; t <= s->mx; t++) {
|
|
s->c[t] += 0 == p;
|
|
}
|
|
++s->c_all;
|
|
}
|
|
} while (++j & 7 && j < s->x0);
|
|
} /* if (s->options & JBG_LRLTWO) */
|
|
hp1++;
|
|
if (hp2) hp2++;
|
|
if (hp3) hp3++;
|
|
} /* for (j = ...) */
|
|
} /* if (!ltp) */
|
|
|
|
/* line is complete now, deal with end of stripe */
|
|
s->i++; s->y++;
|
|
if (s->i == s->l0 || s->y == s->y0) {
|
|
/* end of stripe reached */
|
|
arith_encode_flush(&s->s);
|
|
buf[0] = MARKER_ESC;
|
|
buf[1] = MARKER_SDNORM;
|
|
s->data_out(buf, 2, s->file);
|
|
s->i = 0;
|
|
|
|
/* output NEWLEN if there is any pending */
|
|
output_newlen(s);
|
|
}
|
|
|
|
/* check whether it is worth to perform an ATMOVE */
|
|
if (s->new_tx == -1 && s->c_all > 2048) {
|
|
cmin = clmin = 0xffffffffL;
|
|
cmax = clmax = 0;
|
|
tmax = 0;
|
|
for (t = (s->options & JBG_LRLTWO) ? 5 : 3; t <= s->mx; t++) {
|
|
if (s->c[t] > cmax) cmax = s->c[t];
|
|
if (s->c[t] < cmin) cmin = s->c[t];
|
|
if (s->c[t] > s->c[tmax]) tmax = t;
|
|
}
|
|
clmin = (s->c[0] < cmin) ? s->c[0] : cmin;
|
|
clmax = (s->c[0] > cmax) ? s->c[0] : cmax;
|
|
if (s->c_all - cmax < (s->c_all >> 3) &&
|
|
cmax - s->c[s->tx] > s->c_all - cmax &&
|
|
cmax - s->c[s->tx] > (s->c_all >> 4) &&
|
|
/* ^ T.82 said < here, fixed in Cor.1/25 */
|
|
cmax - (s->c_all - s->c[s->tx]) > s->c_all - cmax &&
|
|
cmax - (s->c_all - s->c[s->tx]) > (s->c_all >> 4) &&
|
|
cmax - cmin > (s->c_all >> 2) &&
|
|
(s->tx || clmax - clmin > (s->c_all >> 3))) {
|
|
/* we have decided to perform an ATMOVE */
|
|
s->new_tx = tmax;
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "ATMOVE: tx=%d, c_all=%lu\n",
|
|
s->new_tx, s->c_all);
|
|
#endif
|
|
} else {
|
|
s->new_tx = s->tx; /* we have decided not to perform an ATMOVE */
|
|
}
|
|
}
|
|
assert(s->tx >= 0); /* i.e., tx can safely be cast to unsigned */
|
|
|
|
#ifdef DEBUG
|
|
if (s->y == s->y0)
|
|
fprintf(stderr, "tp_lines = %ld, encoded_pixels = %ld\n",
|
|
tp_lines, encoded_pixels);
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Inform encoder about new (reduced) height of image
|
|
*/
|
|
void jbg85_enc_newlen(struct jbg85_enc_state *s, unsigned long newlen)
|
|
{
|
|
unsigned char buf[6];
|
|
|
|
if (s->newlen == 2 || newlen >= s->y0 || newlen < 1 ||
|
|
!(s->options & JBG_VLENGTH)) {
|
|
/* invalid invocation or parameter */
|
|
return;
|
|
}
|
|
if (newlen < s->y) {
|
|
/* we are already beyond the new end, therefore move the new end */
|
|
newlen = s->y;
|
|
}
|
|
if (s->y > 0 && s->y0 != newlen)
|
|
s->newlen = 1;
|
|
s->y0 = newlen;
|
|
if (s->y == s->y0) {
|
|
/* we are already at the end; finish the current stripe if necessary */
|
|
if (s->i > 0) {
|
|
arith_encode_flush(&s->s);
|
|
buf[0] = MARKER_ESC;
|
|
buf[1] = MARKER_SDNORM;
|
|
s->data_out(buf, 2, s->file);
|
|
s->i = 0;
|
|
}
|
|
/* output NEWLEN if there is any pending */
|
|
output_newlen(s);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Abort encoding process immediately by outputting an ABORT marker segment
|
|
*/
|
|
void jbg85_enc_abort(struct jbg85_enc_state *s)
|
|
{
|
|
unsigned char buf[2];
|
|
|
|
buf[0] = MARKER_ESC;
|
|
buf[1] = MARKER_ABORT;
|
|
s->data_out(buf, 2, s->file);
|
|
s->y = s->y0; /* just to prevent further calls to jbg85_enc_lineout() */
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert the error codes used by jbg85_dec_in() into an English ASCII string
|
|
*/
|
|
const char *jbg85_strerror(int errnum)
|
|
{
|
|
errnum >>= 4;
|
|
if (errnum < 0 || (unsigned) errnum >= sizeof(errmsg)/sizeof(errmsg[0]))
|
|
return "Unknown error code passed to jbg85_strerror()";
|
|
|
|
return errmsg[errnum];
|
|
}
|
|
|
|
|
|
/*
|
|
* The constructor for a decoder
|
|
*/
|
|
void jbg85_dec_init(struct jbg85_dec_state *s,
|
|
unsigned char *buf, size_t buflen,
|
|
int (*line_out)(const struct jbg85_dec_state *s,
|
|
unsigned char *start, size_t len,
|
|
unsigned long y, void *file),
|
|
void *file)
|
|
{
|
|
s->x0 = 0;
|
|
s->y0 = 0;
|
|
s->linebuf = buf;
|
|
s->linebuf_len = buflen;
|
|
s->line_out = line_out;
|
|
s->file = file;
|
|
s->bie_len = 0;
|
|
s->end_of_bie = 0;
|
|
s->x = 0;
|
|
s->y = 0;
|
|
s->i = 0;
|
|
s->comment_skip = 0;
|
|
s->buf_len = 0;
|
|
s->pseudo = 1;
|
|
s->at_moves = 0;
|
|
s->tx = 0;
|
|
s->lntp = 1;
|
|
s->p[0] = 0;
|
|
s->p[1] = -1;
|
|
s->p[2] = -1;
|
|
arith_decode_init(&s->s, 0);
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Decode the new len PSCD bytes to which data points and output
|
|
* decoded lines as they are completed. Return the number of bytes
|
|
* which have actually been read. This will be less than len if a
|
|
* marker segment was part of the data or if the final byte was
|
|
* 0xff, meaning that this code can not determine whether we have a
|
|
* marker segment.
|
|
*/
|
|
static size_t decode_pscd(struct jbg85_dec_state *s, unsigned char *data,
|
|
size_t len)
|
|
{
|
|
unsigned char *hp1, *hp2, *hp3, *p1;
|
|
register unsigned long line_h1, line_h2, line_h3;
|
|
unsigned long x;
|
|
long o;
|
|
unsigned a;
|
|
int n;
|
|
int pix, slntp;
|
|
int buflines = 3 - !!(s->options & JBG_LRLTWO);
|
|
|
|
/* forward data to arithmetic decoder */
|
|
s->s.pscd_ptr = data;
|
|
s->s.pscd_end = data + len;
|
|
|
|
/* restore a few local variables */
|
|
line_h1 = s->line_h1;
|
|
line_h2 = s->line_h2;
|
|
line_h3 = s->line_h3;
|
|
x = s->x;
|
|
|
|
#ifdef DEBUG
|
|
if (x == 0 && s->i == 0 && s->pseudo)
|
|
fprintf(stderr, "decode_pscd(%p, %p, %ld)\n",
|
|
(void *) s, (void *) data, (long) len);
|
|
#endif
|
|
|
|
s->intr = 0;
|
|
for (; s->i < s->l0 && s->y < s->y0 && !s->intr; s->i++, s->y++) {
|
|
|
|
/* pointer to image byte */
|
|
hp1 = s->linebuf + s->p[0] * s->bpl + (x >> 3);
|
|
hp2 = s->linebuf + s->p[1] * s->bpl + (x >> 3);
|
|
hp3 = s->linebuf + s->p[2] * s->bpl + (x >> 3);
|
|
|
|
/* adaptive template changes */
|
|
if (x == 0 && s->pseudo)
|
|
for (n = 0; n < s->at_moves; n++)
|
|
if (s->at_line[n] == s->i) {
|
|
s->tx = s->at_tx[n];
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "ATMOVE: line=%lu, tx=%d.\n", s->i, s->tx);
|
|
#endif
|
|
}
|
|
assert(s->tx >= 0); /* i.e., tx can safely be cast to unsigned */
|
|
|
|
/* typical prediction */
|
|
if (s->options & JBG_TPBON && s->pseudo) {
|
|
slntp = arith_decode(&s->s, (s->options & JBG_LRLTWO) ? TPB2CX : TPB3CX);
|
|
if (slntp < 0)
|
|
goto leave;
|
|
s->lntp =
|
|
!(slntp ^ s->lntp);
|
|
if (!s->lntp) {
|
|
/* this line is 'typical' (i.e. identical to the previous one) */
|
|
if (s->p[1] < 0) {
|
|
/* first line of page or (following SDRST) of stripe */
|
|
for (p1 = hp1; p1 < hp1 + s->bpl; *p1++ = 0) ;
|
|
s->intr = s->line_out(s, hp1, s->bpl, s->y, s->file);
|
|
/* rotate the ring buffer that holds the last three lines */
|
|
s->p[2] = s->p[1];
|
|
s->p[1] = s->p[0];
|
|
if (++(s->p[0]) >= buflines) s->p[0] = 0;
|
|
} else {
|
|
s->intr = s->line_out(s, hp2, s->bpl, s->y, s->file);
|
|
/* duplicate the last line in the ring buffer */
|
|
s->p[2] = s->p[1];
|
|
}
|
|
continue;
|
|
}
|
|
/* this line is 'not typical' and has to be coded completely */
|
|
}
|
|
s->pseudo = 0;
|
|
|
|
/*
|
|
* Layout of the variables line_h1, line_h2, line_h3, which contain
|
|
* as bits the neighbour pixels of the currently decoded pixel X:
|
|
*
|
|
* 76543210 76543210 76543210 76543210 line_h3
|
|
* 76543210 76543210 76543210 76543210 line_h2
|
|
* 76543210 76543210 76543210 76543210 X line_h1
|
|
*/
|
|
|
|
if (x == 0) {
|
|
line_h1 = line_h2 = line_h3 = 0;
|
|
if (s->p[1] >= 0)
|
|
line_h2 = (long)*hp2 << 8;
|
|
if (s->p[2] >= 0)
|
|
line_h3 = (long)*hp3 << 8;
|
|
}
|
|
|
|
/* decode line */
|
|
while (x < s->x0) {
|
|
if ((x & 7) == 0) {
|
|
if (x < (s->bpl - 1) * 8 && s->p[1] >= 0) {
|
|
line_h2 |= *(hp2 + 1);
|
|
if (s->p[2] >= 0)
|
|
line_h3 |= *(hp3 + 1);
|
|
}
|
|
}
|
|
if (s->options & JBG_LRLTWO) {
|
|
/* two line template */
|
|
do {
|
|
if (s->tx) {
|
|
if ((unsigned) s->tx > x)
|
|
a = 0;
|
|
else if (s->tx < 8)
|
|
a = ((line_h1 >> (s->tx - 5)) & 0x010);
|
|
else {
|
|
o = (x - s->tx) - (x & ~7L);
|
|
a = (hp1[o >> 3] >> (7 - (o & 7))) & 1;
|
|
a <<= 4;
|
|
}
|
|
assert(s->tx > 31 ||
|
|
a == ((line_h1 >> (s->tx - 5)) & 0x010));
|
|
pix = arith_decode(&s->s, (((line_h2 >> 9) & 0x3e0) | a |
|
|
(line_h1 & 0x00f)));
|
|
} else
|
|
pix = arith_decode(&s->s, (((line_h2 >> 9) & 0x3f0) |
|
|
(line_h1 & 0x00f)));
|
|
if (pix < 0)
|
|
goto leave;
|
|
line_h1 = (line_h1 << 1) | pix;
|
|
line_h2 <<= 1;
|
|
} while ((++x & 7) && x < s->x0);
|
|
} else {
|
|
/* three line template */
|
|
do {
|
|
if (s->tx) {
|
|
if ((unsigned) s->tx > x)
|
|
a = 0;
|
|
else if (s->tx < 8)
|
|
a = ((line_h1 >> (s->tx - 3)) & 0x004);
|
|
else {
|
|
o = (x - s->tx) - (x & ~7L);
|
|
a = (hp1[o >> 3] >> (7 - (o & 7))) & 1;
|
|
a <<= 2;
|
|
}
|
|
assert(s->tx > 31 ||
|
|
a == ((line_h1 >> (s->tx - 3)) & 0x004));
|
|
pix = arith_decode(&s->s, (((line_h3 >> 7) & 0x380) |
|
|
((line_h2 >> 11) & 0x078) | a |
|
|
(line_h1 & 0x003)));
|
|
} else
|
|
pix = arith_decode(&s->s, (((line_h3 >> 7) & 0x380) |
|
|
((line_h2 >> 11) & 0x07c) |
|
|
(line_h1 & 0x003)));
|
|
if (pix < 0)
|
|
goto leave;
|
|
line_h1 = (line_h1 << 1) | pix;
|
|
line_h2 <<= 1;
|
|
line_h3 <<= 1;
|
|
} while ((++x & 7) && x < s->x0);
|
|
} /* if (s->options & JBG_LRLTWO) */
|
|
*hp1++ = line_h1;
|
|
hp2++;
|
|
hp3++;
|
|
} /* while (x < s->x0) */
|
|
*(hp1 - 1) <<= s->bpl * 8 - s->x0;
|
|
s->intr = s->line_out(s, s->linebuf + s->p[0] * s->bpl,
|
|
s->bpl, s->y, s->file);
|
|
x = 0;
|
|
s->pseudo = 1;
|
|
/* rotate the ring buffer that holds the last three lines */
|
|
s->p[2] = s->p[1];
|
|
s->p[1] = s->p[0];
|
|
if (++(s->p[0]) >= buflines) s->p[0] = 0;
|
|
} /* for (i = ...) */
|
|
|
|
leave:
|
|
|
|
/* save a few local variables */
|
|
s->line_h1 = line_h1;
|
|
s->line_h2 = line_h2;
|
|
s->line_h3 = line_h3;
|
|
s->x = x;
|
|
|
|
return s->s.pscd_ptr - data;
|
|
}
|
|
|
|
/*
|
|
* Helper routine for processing SDNORM/SDRST marker segment
|
|
* (which is found in s->buffer[0..1])
|
|
*/
|
|
static int finish_sde(struct jbg85_dec_state *s)
|
|
{
|
|
/* decode final pixels based on trailing zero bytes */
|
|
s->s.nopadding = 0;
|
|
if (decode_pscd(s, s->buffer, 2) != 2 && s->intr)
|
|
return 1;
|
|
|
|
/* prepare decoder for next SDE */
|
|
arith_decode_init(&s->s, s->buffer[1] == MARKER_SDNORM);
|
|
s->s.nopadding = s->options & JBG_VLENGTH;
|
|
|
|
s->x = 0;
|
|
s->i = 0;
|
|
s->pseudo = 1;
|
|
s->at_moves = 0;
|
|
if (s->buffer[1] == MARKER_SDRST) {
|
|
s->tx = 0;
|
|
s->lntp = 1;
|
|
s->p[0] = 0;
|
|
s->p[1] = -1;
|
|
s->p[2] = -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Provide to the decoder a new BIE fragment of len bytes starting at data.
|
|
*
|
|
* Unless cnt is NULL, *cnt will contain the number of actually read bytes
|
|
* on return.
|
|
*
|
|
* Normal return values:
|
|
*
|
|
* JBG_EAGAIN All data bytes provided so far have been processed
|
|
* (*cnt == len) but the end of the data stream has
|
|
* not yet been recognized. Call the function again
|
|
* with additional BIE bytes.
|
|
* JBG_EOK The function has reached the end of the BIE and
|
|
* the full image has been decoded.
|
|
* JBG_EOK_INTR Parsing the BIE has been interrupted as had been
|
|
* requested by a non-zero return value of line_out().
|
|
* This function can be called again with the
|
|
* rest of the BIE to continue the decoding process.
|
|
* The remaining len - *cnt bytes of the previous
|
|
* data block will then have to be passed to this
|
|
* function again (only if len > *cnt).
|
|
*
|
|
* Any other return value indicates that the decoding process was
|
|
* aborted by a serious problem and the only function you can then
|
|
* still call is jbg85_strerror() to find out what to tell the user.
|
|
* (Looking at the least significant bits of the return value will
|
|
* provide additional information by identifying which test exactly
|
|
* has failed.)
|
|
*/
|
|
int jbg85_dec_in(struct jbg85_dec_state *s, unsigned char *data, size_t len,
|
|
size_t *cnt)
|
|
{
|
|
int required_length;
|
|
unsigned long y;
|
|
size_t dummy_cnt;
|
|
|
|
if (!cnt) cnt = &dummy_cnt;
|
|
*cnt = 0;
|
|
|
|
/* read in 20-byte BIH */
|
|
if (s->bie_len < 20) {
|
|
while (s->bie_len < 20 && *cnt < len)
|
|
s->buffer[s->bie_len++] = data[(*cnt)++];
|
|
if (s->bie_len < 20)
|
|
return JBG_EAGAIN;
|
|
/* parse header parameters */
|
|
s->x0 = (((long) s->buffer[ 4] << 24) | ((long) s->buffer[ 5] << 16) |
|
|
((long) s->buffer[ 6] << 8) | (long) s->buffer[ 7]);
|
|
s->y0 = (((long) s->buffer[ 8] << 24) | ((long) s->buffer[ 9] << 16) |
|
|
((long) s->buffer[10] << 8) | (long) s->buffer[11]);
|
|
s->l0 = (((long) s->buffer[12] << 24) | ((long) s->buffer[13] << 16) |
|
|
((long) s->buffer[14] << 8) | (long) s->buffer[15]);
|
|
s->bpl = (s->x0 >> 3) + !!(s->x0 & 7); /* bytes per line */
|
|
s->mx = s->buffer[16];
|
|
s->options = s->buffer[19];
|
|
s->s.nopadding = s->options & JBG_VLENGTH;
|
|
/* test whether this looks like a valid JBIG header at all */
|
|
if (s->buffer[1] < s->buffer[0]) return JBG_EINVAL | 1;
|
|
/* are padding bits zero as required? */
|
|
if (s->buffer[3] != 0) return JBG_EINVAL | 2; /* padding != 0 */
|
|
if ((s->buffer[18] & 0xf0) != 0) return JBG_EINVAL | 3; /* padding != 0 */
|
|
if ((s->buffer[19] & 0x80) != 0) return JBG_EINVAL | 4; /* padding != 0 */
|
|
if (!s->buffer[2]) return JBG_EINVAL | 5;
|
|
if (!s->x0) return JBG_EINVAL | 6;
|
|
if (!s->y0) return JBG_EINVAL | 7;
|
|
if (!s->l0) return JBG_EINVAL | 8;
|
|
if (s->mx > 127)
|
|
return JBG_EINVAL | 9;
|
|
if (s->buffer[ 0] != 0) return JBG_EIMPL | 8; /* parameter outside T.85 */
|
|
if (s->buffer[ 1] != 0) return JBG_EIMPL | 9; /* parameter outside T.85 */
|
|
if (s->buffer[ 2] != 1) return JBG_EIMPL |10; /* parameter outside T.85 */
|
|
if (s->buffer[17] != 0) return JBG_EIMPL |11; /* parameter outside T.85 */
|
|
#if JBG85_STRICT_ORDER_BITS
|
|
if (s->buffer[18] != 0) return JBG_EIMPL |12; /* parameter outside T.85 */
|
|
#endif
|
|
if (s->options & 0x17) return JBG_EIMPL |13; /* parameter outside T.85 */
|
|
if (s->x0 > (s->linebuf_len / ((s->options & JBG_LRLTWO) ? 2 : 3)) * 8)
|
|
return JBG_ENOMEM; /* provided line buffer is too short */
|
|
}
|
|
|
|
/*
|
|
* BID processing loop
|
|
*/
|
|
|
|
while (*cnt < len || s->end_of_bie == 1) {
|
|
if (s->end_of_bie == 1) s->end_of_bie = 2;
|
|
|
|
/* process floating marker segments */
|
|
|
|
/* skip COMMENT contents */
|
|
if (s->comment_skip) {
|
|
if (s->comment_skip <= len - *cnt) {
|
|
*cnt += s->comment_skip;
|
|
s->comment_skip = 0;
|
|
} else {
|
|
s->comment_skip -= len - *cnt;
|
|
*cnt = len;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* load marker segments into s->buffer for processing */
|
|
if (s->buf_len > 0) {
|
|
assert(s->buffer[0] == MARKER_ESC);
|
|
/* load enough bytes to determine length of marker segment */
|
|
while (s->buf_len < 2 && *cnt < len)
|
|
s->buffer[s->buf_len++] = data[(*cnt)++];
|
|
if (s->buf_len < 2) continue;
|
|
switch (s->buffer[1]) {
|
|
case MARKER_COMMENT: required_length = 6; break;
|
|
case MARKER_ATMOVE: required_length = 8; break;
|
|
case MARKER_NEWLEN: required_length = 6; break;
|
|
case MARKER_SDNORM:
|
|
case MARKER_SDRST:
|
|
if ((s->options & JBG_VLENGTH) && !s->end_of_bie) {
|
|
/* peek ahead whether a NEWLEN marker segment follows */
|
|
required_length = 2 + 1;
|
|
if (s->buf_len == 2 + 1 && s->buffer[2] == MARKER_ESC)
|
|
required_length = 2 + 2; /* SDNORM + 2 marker sequence bytes */
|
|
else if (s->buf_len >= 2 + 2 && s->buffer[3] == MARKER_NEWLEN)
|
|
required_length = 2 + 6; /* SDNORM + NEWLEN */
|
|
} else {
|
|
/* no further NEWLEN allowed or end of BIE reached */
|
|
required_length = 2;
|
|
}
|
|
break;
|
|
case MARKER_ABORT:
|
|
s->buf_len = 0;
|
|
return JBG_EABORT;
|
|
case MARKER_STUFF:
|
|
/* forward stuffed 0xff to arithmetic decoder */
|
|
if (decode_pscd(s, s->buffer, 2) == 2 || !s->intr)
|
|
s->buf_len = 0;
|
|
if (s->intr)
|
|
return JBG_EOK_INTR; /* line_out() requested interrupt */
|
|
continue;
|
|
default:
|
|
return JBG_EMARKER;
|
|
}
|
|
/* load minimal number of additional bytes required for processing */
|
|
while (s->buf_len < required_length && *cnt < len)
|
|
s->buffer[s->buf_len++] = data[(*cnt)++];
|
|
if (s->buf_len < required_length) continue;
|
|
/* now the buffer is filled with exactly one marker segment
|
|
* (or in the case of SDNORM/SDRST sometimes also with
|
|
* two additional peek-ahead bytes) */
|
|
switch (s->buffer[1]) {
|
|
case MARKER_COMMENT:
|
|
s->comment_skip =
|
|
(((long) s->buffer[2] << 24) | ((long) s->buffer[3] << 16) |
|
|
((long) s->buffer[4] << 8) | (long) s->buffer[5]);
|
|
break;
|
|
case MARKER_ATMOVE:
|
|
if (s->at_moves < JBG85_ATMOVES_MAX) {
|
|
s->at_line[s->at_moves] =
|
|
(((long) s->buffer[2] << 24) | ((long) s->buffer[3] << 16) |
|
|
((long) s->buffer[4] << 8) | (long) s->buffer[5]);
|
|
s->at_tx[s->at_moves] = (signed char) s->buffer[6];
|
|
if (s->at_tx[s->at_moves] > (int) s->mx ||
|
|
(s->at_tx[s->at_moves] < ((s->options & JBG_LRLTWO) ? 5 : 3) &&
|
|
s->at_tx[s->at_moves] != 0) ||
|
|
s->buffer[7] != 0)
|
|
return JBG_EINVAL | 11;
|
|
s->at_moves++;
|
|
} else
|
|
return JBG_EIMPL | 14; /* more than JBG85_ATMOVES_MAX ATMOVES */
|
|
break;
|
|
case MARKER_NEWLEN:
|
|
y = (((long) s->buffer[2] << 24) | ((long) s->buffer[3] << 16) |
|
|
((long) s->buffer[4] << 8) | (long) s->buffer[5]);
|
|
if (y > s->y0) return JBG_EINVAL | 12;
|
|
#ifndef JBG85_TOLERATE_MULTIPLE_NEWLEN
|
|
if (!(s->options & JBG_VLENGTH)) return JBG_EINVAL | 13;
|
|
s->options &= ~JBG_VLENGTH;
|
|
#endif
|
|
s->y0 = y;
|
|
break;
|
|
|
|
case MARKER_SDNORM:
|
|
case MARKER_SDRST:
|
|
|
|
switch (s->buf_len) {
|
|
case 2:
|
|
/* process regular SDNORM/SDRST without peek-ahead bytes */
|
|
if (finish_sde(s))
|
|
return JBG_EOK_INTR; /* line_out() requested interrupt */
|
|
/* check whether this was the last SDE */
|
|
if (s->y >= s->y0) return JBG_EOK;
|
|
break;
|
|
case 2+1:
|
|
/* process single peek-ahead byte */
|
|
if (s->buffer[2] == MARKER_ESC)
|
|
continue; /* next time we'll have s->buf_len == 2 + 2 */
|
|
else {
|
|
/* push back the single peek-ahead PCSD byte */
|
|
assert(*cnt > 0);
|
|
(*cnt)--;
|
|
s->buf_len--;
|
|
if (finish_sde(s)) {
|
|
return JBG_EOK_INTR; /* line_out() requested interrupt */
|
|
}
|
|
}
|
|
break;
|
|
case 2+2:
|
|
/* process 2-byte peek-ahead marker sequence */
|
|
if (s->buffer[2] == MARKER_ESC && s->buffer[3] == MARKER_NEWLEN)
|
|
continue; /* next time we'll have s->buf_len == 2 + 6 */
|
|
else {
|
|
if (finish_sde(s))
|
|
return JBG_EOK_INTR; /* line_out() requested interrupt */
|
|
/* recycle the two peek-ahead marker sequence bytes */
|
|
s->buffer[0] = s->buffer[2];
|
|
s->buffer[1] = s->buffer[3];
|
|
s->buf_len = 2;
|
|
if (s->intr)
|
|
return JBG_EOK_INTR; /* line_out() requested interrupt */
|
|
continue;
|
|
}
|
|
case 2+6:
|
|
/* process peek-ahead NEWLEN marker sequence */
|
|
y = (((long) s->buffer[4] << 24) | ((long) s->buffer[5] << 16) |
|
|
((long) s->buffer[6] << 8) | (long) s->buffer[7]);
|
|
if (y > s->y0) return JBG_EINVAL | 12;
|
|
if (!(s->options & JBG_VLENGTH)) return JBG_EINVAL | 13;
|
|
s->y0 = y;
|
|
if (finish_sde(s))
|
|
return JBG_EOK_INTR; /* line_out() requested interrupt */
|
|
s->buf_len = 0;
|
|
s->options &= ~JBG_VLENGTH;
|
|
/* we leave returning JBG_EOK to the following SDNORM/RST */
|
|
break;
|
|
}
|
|
|
|
if (s->intr) {
|
|
s->buf_len = 0;
|
|
return JBG_EOK_INTR; /* line_out() requested interrupt */
|
|
}
|
|
|
|
} /* switch (s->buffer[1]) */
|
|
s->buf_len = 0;
|
|
|
|
} else if (*cnt < len && data[*cnt] == MARKER_ESC)
|
|
s->buffer[s->buf_len++] = data[(*cnt)++];
|
|
|
|
else {
|
|
|
|
/* we have found PSCD bytes */
|
|
*cnt += decode_pscd(s, data + *cnt, len - *cnt);
|
|
if (s->intr)
|
|
return JBG_EOK_INTR; /* line_out() requested interrupt */
|
|
if (*cnt < len && data[*cnt] != MARKER_ESC) {
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "PSCD was longer than expected, unread bytes "
|
|
"%02x %02x %02x %02x ...\n", data[*cnt], data[*cnt+1],
|
|
data[*cnt+2], data[*cnt+3]);
|
|
#endif
|
|
return JBG_EINVAL | 14; /* PSCD was longer than expected */
|
|
}
|
|
|
|
}
|
|
} /* of BID processing loop 'while (*cnt < len) ...' */
|
|
|
|
return JBG_EAGAIN;
|
|
}
|
|
|
|
|
|
/*
|
|
* After the final BIE byte has been delivered to jbg85_dec_in(), it
|
|
* may still return with JBG_EAGAIN in case the VLENGTH=1 option was
|
|
* used and no NEWLEN marker section has appeared yet. This is because
|
|
* such a BIE is not self-terminating (i.e., there could still be a
|
|
* NEWLEN followed by an SDNORM or SDRST lurk after the final stripe,
|
|
* which needs to be processed before the final line is output, see
|
|
* ITU-T Recommendation T.85, Appendix I). Therefore, after the last
|
|
* byte has been delivered, call this routine to signal the end of the
|
|
* BIE. This is necessary to allow the routine to finish processing
|
|
* BIEs with option VLENGTH=1 that do not actually contain any NEWLEN
|
|
* marker section.
|
|
*/
|
|
int jbg85_dec_end(struct jbg85_dec_state *s)
|
|
{
|
|
s->end_of_bie = 1;
|
|
return jbg85_dec_in(s, NULL, 0, NULL);
|
|
}
|