You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3413 lines
86 KiB
3413 lines
86 KiB
//========================================================================
|
|
//
|
|
// JBIG2Stream.cc
|
|
//
|
|
// Copyright 2002-2003 Glyph & Cog, LLC
|
|
//
|
|
//========================================================================
|
|
|
|
#include <aconf.h>
|
|
|
|
#ifdef USE_GCC_PRAGMAS
|
|
#pragma implementation
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include "GList.h"
|
|
#include "Error.h"
|
|
#include "JArithmeticDecoder.h"
|
|
#include "JBIG2Stream.h"
|
|
|
|
//~ share these tables
|
|
#include "Stream-CCITT.h"
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
static int contextSize[4] = { 16, 13, 10, 10 };
|
|
static int refContextSize[2] = { 13, 10 };
|
|
|
|
//------------------------------------------------------------------------
|
|
// JBIG2HuffmanTable
|
|
//------------------------------------------------------------------------
|
|
|
|
#define jbig2HuffmanLOW 0xfffffffd
|
|
#define jbig2HuffmanOOB 0xfffffffe
|
|
#define jbig2HuffmanEOT 0xffffffff
|
|
|
|
struct JBIG2HuffmanTable {
|
|
int val;
|
|
Guint prefixLen;
|
|
Guint rangeLen; // can also be LOW, OOB, or EOT
|
|
Guint prefix;
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableA[] = {
|
|
{ 0, 1, 4, 0x000 },
|
|
{ 16, 2, 8, 0x002 },
|
|
{ 272, 3, 16, 0x006 },
|
|
{ 65808, 3, 32, 0x007 },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableB[] = {
|
|
{ 0, 1, 0, 0x000 },
|
|
{ 1, 2, 0, 0x002 },
|
|
{ 2, 3, 0, 0x006 },
|
|
{ 3, 4, 3, 0x00e },
|
|
{ 11, 5, 6, 0x01e },
|
|
{ 75, 6, 32, 0x03e },
|
|
{ 0, 6, jbig2HuffmanOOB, 0x03f },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableC[] = {
|
|
{ 0, 1, 0, 0x000 },
|
|
{ 1, 2, 0, 0x002 },
|
|
{ 2, 3, 0, 0x006 },
|
|
{ 3, 4, 3, 0x00e },
|
|
{ 11, 5, 6, 0x01e },
|
|
{ 0, 6, jbig2HuffmanOOB, 0x03e },
|
|
{ 75, 7, 32, 0x0fe },
|
|
{ -256, 8, 8, 0x0fe },
|
|
{ -257, 8, jbig2HuffmanLOW, 0x0ff },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableD[] = {
|
|
{ 1, 1, 0, 0x000 },
|
|
{ 2, 2, 0, 0x002 },
|
|
{ 3, 3, 0, 0x006 },
|
|
{ 4, 4, 3, 0x00e },
|
|
{ 12, 5, 6, 0x01e },
|
|
{ 76, 5, 32, 0x01f },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableE[] = {
|
|
{ 1, 1, 0, 0x000 },
|
|
{ 2, 2, 0, 0x002 },
|
|
{ 3, 3, 0, 0x006 },
|
|
{ 4, 4, 3, 0x00e },
|
|
{ 12, 5, 6, 0x01e },
|
|
{ 76, 6, 32, 0x03e },
|
|
{ -255, 7, 8, 0x07e },
|
|
{ -256, 7, jbig2HuffmanLOW, 0x07f },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableF[] = {
|
|
{ 0, 2, 7, 0x000 },
|
|
{ 128, 3, 7, 0x002 },
|
|
{ 256, 3, 8, 0x003 },
|
|
{ -1024, 4, 9, 0x008 },
|
|
{ -512, 4, 8, 0x009 },
|
|
{ -256, 4, 7, 0x00a },
|
|
{ -32, 4, 5, 0x00b },
|
|
{ 512, 4, 9, 0x00c },
|
|
{ 1024, 4, 10, 0x00d },
|
|
{ -2048, 5, 10, 0x01c },
|
|
{ -128, 5, 6, 0x01d },
|
|
{ -64, 5, 5, 0x01e },
|
|
{ -2049, 6, jbig2HuffmanLOW, 0x03e },
|
|
{ 2048, 6, 32, 0x03f },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableG[] = {
|
|
{ -512, 3, 8, 0x000 },
|
|
{ 256, 3, 8, 0x001 },
|
|
{ 512, 3, 9, 0x002 },
|
|
{ 1024, 3, 10, 0x003 },
|
|
{ -1024, 4, 9, 0x008 },
|
|
{ -256, 4, 7, 0x009 },
|
|
{ -32, 4, 5, 0x00a },
|
|
{ 0, 4, 5, 0x00b },
|
|
{ 128, 4, 7, 0x00c },
|
|
{ -128, 5, 6, 0x01a },
|
|
{ -64, 5, 5, 0x01b },
|
|
{ 32, 5, 5, 0x01c },
|
|
{ 64, 5, 6, 0x01d },
|
|
{ -1025, 5, jbig2HuffmanLOW, 0x01e },
|
|
{ 2048, 5, 32, 0x01f },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableH[] = {
|
|
{ 0, 2, 1, 0x000 },
|
|
{ 0, 2, jbig2HuffmanOOB, 0x001 },
|
|
{ 4, 3, 4, 0x004 },
|
|
{ -1, 4, 0, 0x00a },
|
|
{ 22, 4, 4, 0x00b },
|
|
{ 38, 4, 5, 0x00c },
|
|
{ 2, 5, 0, 0x01a },
|
|
{ 70, 5, 6, 0x01b },
|
|
{ 134, 5, 7, 0x01c },
|
|
{ 3, 6, 0, 0x03a },
|
|
{ 20, 6, 1, 0x03b },
|
|
{ 262, 6, 7, 0x03c },
|
|
{ 646, 6, 10, 0x03d },
|
|
{ -2, 7, 0, 0x07c },
|
|
{ 390, 7, 8, 0x07d },
|
|
{ -15, 8, 3, 0x0fc },
|
|
{ -5, 8, 1, 0x0fd },
|
|
{ -7, 9, 1, 0x1fc },
|
|
{ -3, 9, 0, 0x1fd },
|
|
{ -16, 9, jbig2HuffmanLOW, 0x1fe },
|
|
{ 1670, 9, 32, 0x1ff },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableI[] = {
|
|
{ 0, 2, jbig2HuffmanOOB, 0x000 },
|
|
{ -1, 3, 1, 0x002 },
|
|
{ 1, 3, 1, 0x003 },
|
|
{ 7, 3, 5, 0x004 },
|
|
{ -3, 4, 1, 0x00a },
|
|
{ 43, 4, 5, 0x00b },
|
|
{ 75, 4, 6, 0x00c },
|
|
{ 3, 5, 1, 0x01a },
|
|
{ 139, 5, 7, 0x01b },
|
|
{ 267, 5, 8, 0x01c },
|
|
{ 5, 6, 1, 0x03a },
|
|
{ 39, 6, 2, 0x03b },
|
|
{ 523, 6, 8, 0x03c },
|
|
{ 1291, 6, 11, 0x03d },
|
|
{ -5, 7, 1, 0x07c },
|
|
{ 779, 7, 9, 0x07d },
|
|
{ -31, 8, 4, 0x0fc },
|
|
{ -11, 8, 2, 0x0fd },
|
|
{ -15, 9, 2, 0x1fc },
|
|
{ -7, 9, 1, 0x1fd },
|
|
{ -32, 9, jbig2HuffmanLOW, 0x1fe },
|
|
{ 3339, 9, 32, 0x1ff },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableJ[] = {
|
|
{ -2, 2, 2, 0x000 },
|
|
{ 6, 2, 6, 0x001 },
|
|
{ 0, 2, jbig2HuffmanOOB, 0x002 },
|
|
{ -3, 5, 0, 0x018 },
|
|
{ 2, 5, 0, 0x019 },
|
|
{ 70, 5, 5, 0x01a },
|
|
{ 3, 6, 0, 0x036 },
|
|
{ 102, 6, 5, 0x037 },
|
|
{ 134, 6, 6, 0x038 },
|
|
{ 198, 6, 7, 0x039 },
|
|
{ 326, 6, 8, 0x03a },
|
|
{ 582, 6, 9, 0x03b },
|
|
{ 1094, 6, 10, 0x03c },
|
|
{ -21, 7, 4, 0x07a },
|
|
{ -4, 7, 0, 0x07b },
|
|
{ 4, 7, 0, 0x07c },
|
|
{ 2118, 7, 11, 0x07d },
|
|
{ -5, 8, 0, 0x0fc },
|
|
{ 5, 8, 0, 0x0fd },
|
|
{ -22, 8, jbig2HuffmanLOW, 0x0fe },
|
|
{ 4166, 8, 32, 0x0ff },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableK[] = {
|
|
{ 1, 1, 0, 0x000 },
|
|
{ 2, 2, 1, 0x002 },
|
|
{ 4, 4, 0, 0x00c },
|
|
{ 5, 4, 1, 0x00d },
|
|
{ 7, 5, 1, 0x01c },
|
|
{ 9, 5, 2, 0x01d },
|
|
{ 13, 6, 2, 0x03c },
|
|
{ 17, 7, 2, 0x07a },
|
|
{ 21, 7, 3, 0x07b },
|
|
{ 29, 7, 4, 0x07c },
|
|
{ 45, 7, 5, 0x07d },
|
|
{ 77, 7, 6, 0x07e },
|
|
{ 141, 7, 32, 0x07f },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableL[] = {
|
|
{ 1, 1, 0, 0x000 },
|
|
{ 2, 2, 0, 0x002 },
|
|
{ 3, 3, 1, 0x006 },
|
|
{ 5, 5, 0, 0x01c },
|
|
{ 6, 5, 1, 0x01d },
|
|
{ 8, 6, 1, 0x03c },
|
|
{ 10, 7, 0, 0x07a },
|
|
{ 11, 7, 1, 0x07b },
|
|
{ 13, 7, 2, 0x07c },
|
|
{ 17, 7, 3, 0x07d },
|
|
{ 25, 7, 4, 0x07e },
|
|
{ 41, 8, 5, 0x0fe },
|
|
{ 73, 8, 32, 0x0ff },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableM[] = {
|
|
{ 1, 1, 0, 0x000 },
|
|
{ 2, 3, 0, 0x004 },
|
|
{ 7, 3, 3, 0x005 },
|
|
{ 3, 4, 0, 0x00c },
|
|
{ 5, 4, 1, 0x00d },
|
|
{ 4, 5, 0, 0x01c },
|
|
{ 15, 6, 1, 0x03a },
|
|
{ 17, 6, 2, 0x03b },
|
|
{ 21, 6, 3, 0x03c },
|
|
{ 29, 6, 4, 0x03d },
|
|
{ 45, 6, 5, 0x03e },
|
|
{ 77, 7, 6, 0x07e },
|
|
{ 141, 7, 32, 0x07f },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableN[] = {
|
|
{ 0, 1, 0, 0x000 },
|
|
{ -2, 3, 0, 0x004 },
|
|
{ -1, 3, 0, 0x005 },
|
|
{ 1, 3, 0, 0x006 },
|
|
{ 2, 3, 0, 0x007 },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
JBIG2HuffmanTable huffTableO[] = {
|
|
{ 0, 1, 0, 0x000 },
|
|
{ -1, 3, 0, 0x004 },
|
|
{ 1, 3, 0, 0x005 },
|
|
{ -2, 4, 0, 0x00c },
|
|
{ 2, 4, 0, 0x00d },
|
|
{ -4, 5, 1, 0x01c },
|
|
{ 3, 5, 1, 0x01d },
|
|
{ -8, 6, 2, 0x03c },
|
|
{ 5, 6, 2, 0x03d },
|
|
{ -24, 7, 4, 0x07c },
|
|
{ 9, 7, 4, 0x07d },
|
|
{ -25, 7, jbig2HuffmanLOW, 0x07e },
|
|
{ 25, 7, 32, 0x07f },
|
|
{ 0, 0, jbig2HuffmanEOT, 0 }
|
|
};
|
|
|
|
//------------------------------------------------------------------------
|
|
// JBIG2HuffmanDecoder
|
|
//------------------------------------------------------------------------
|
|
|
|
class JBIG2HuffmanDecoder {
|
|
public:
|
|
|
|
JBIG2HuffmanDecoder();
|
|
~JBIG2HuffmanDecoder();
|
|
void setStream(Stream *strA) { str = strA; }
|
|
|
|
void reset();
|
|
|
|
// Returns false for OOB, otherwise sets *<x> and returns true.
|
|
GBool decodeInt(int *x, JBIG2HuffmanTable *table);
|
|
|
|
Guint readBits(Guint n);
|
|
Guint readBit();
|
|
|
|
// Sort the table by prefix length and assign prefix values.
|
|
void buildTable(JBIG2HuffmanTable *table, Guint len);
|
|
|
|
private:
|
|
|
|
Stream *str;
|
|
Guint buf;
|
|
Guint bufLen;
|
|
};
|
|
|
|
JBIG2HuffmanDecoder::JBIG2HuffmanDecoder() {
|
|
str = NULL;
|
|
reset();
|
|
}
|
|
|
|
JBIG2HuffmanDecoder::~JBIG2HuffmanDecoder() {
|
|
}
|
|
|
|
void JBIG2HuffmanDecoder::reset() {
|
|
buf = 0;
|
|
bufLen = 0;
|
|
}
|
|
|
|
//~ optimize this
|
|
GBool JBIG2HuffmanDecoder::decodeInt(int *x, JBIG2HuffmanTable *table) {
|
|
Guint i, len, prefix;
|
|
|
|
i = 0;
|
|
len = 0;
|
|
prefix = 0;
|
|
while (table[i].rangeLen != jbig2HuffmanEOT) {
|
|
while (len < table[i].prefixLen) {
|
|
prefix = (prefix << 1) | readBit();
|
|
++len;
|
|
}
|
|
if (prefix == table[i].prefix) {
|
|
if (table[i].rangeLen == jbig2HuffmanOOB) {
|
|
return gFalse;
|
|
}
|
|
if (table[i].rangeLen == jbig2HuffmanLOW) {
|
|
*x = table[i].val - readBits(32);
|
|
} else if (table[i].rangeLen > 0) {
|
|
*x = table[i].val + readBits(table[i].rangeLen);
|
|
} else {
|
|
*x = table[i].val;
|
|
}
|
|
return gTrue;
|
|
}
|
|
++i;
|
|
}
|
|
return gFalse;
|
|
}
|
|
|
|
Guint JBIG2HuffmanDecoder::readBits(Guint n) {
|
|
Guint x, mask, nLeft;
|
|
|
|
mask = (n == 32) ? 0xffffffff : ((1 << n) - 1);
|
|
if (bufLen >= n) {
|
|
x = (buf >> (bufLen - n)) & mask;
|
|
bufLen -= n;
|
|
} else {
|
|
x = buf & ((1 << bufLen) - 1);
|
|
nLeft = n - bufLen;
|
|
bufLen = 0;
|
|
while (nLeft >= 8) {
|
|
x = (x << 8) | (str->getChar() & 0xff);
|
|
nLeft -= 8;
|
|
}
|
|
if (nLeft > 0) {
|
|
buf = str->getChar();
|
|
bufLen = 8 - nLeft;
|
|
x = (x << nLeft) | ((buf >> bufLen) & ((1 << nLeft) - 1));
|
|
}
|
|
}
|
|
return x;
|
|
}
|
|
|
|
Guint JBIG2HuffmanDecoder::readBit() {
|
|
if (bufLen == 0) {
|
|
buf = str->getChar();
|
|
bufLen = 8;
|
|
}
|
|
--bufLen;
|
|
return (buf >> bufLen) & 1;
|
|
}
|
|
|
|
void JBIG2HuffmanDecoder::buildTable(JBIG2HuffmanTable *table, Guint len) {
|
|
Guint i, j, k, prefix;
|
|
JBIG2HuffmanTable tab;
|
|
|
|
// stable selection sort:
|
|
// - entries with prefixLen > 0, in ascending prefixLen order
|
|
// - entry with prefixLen = 0, rangeLen = EOT
|
|
// - all other entries with prefixLen = 0
|
|
// (on entry, table[len] has prefixLen = 0, rangeLen = EOT)
|
|
for (i = 0; i < len; ++i) {
|
|
for (j = i; j < len && table[j].prefixLen == 0; ++j) ;
|
|
if (j == len) {
|
|
break;
|
|
}
|
|
for (k = j + 1; k < len; ++k) {
|
|
if (table[k].prefixLen > 0 &&
|
|
table[k].prefixLen < table[j].prefixLen) {
|
|
j = k;
|
|
}
|
|
}
|
|
if (j != i) {
|
|
tab = table[j];
|
|
for (k = j; k > i; --k) {
|
|
table[k] = table[k - 1];
|
|
}
|
|
table[i] = tab;
|
|
}
|
|
}
|
|
table[i] = table[len];
|
|
|
|
// assign prefixes
|
|
i = 0;
|
|
prefix = 0;
|
|
table[i++].prefix = prefix++;
|
|
for (; table[i].rangeLen != jbig2HuffmanEOT; ++i) {
|
|
prefix <<= table[i].prefixLen - table[i-1].prefixLen;
|
|
table[i].prefix = prefix++;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// JBIG2MMRDecoder
|
|
//------------------------------------------------------------------------
|
|
|
|
class JBIG2MMRDecoder {
|
|
public:
|
|
|
|
JBIG2MMRDecoder();
|
|
~JBIG2MMRDecoder();
|
|
void setStream(Stream *strA) { str = strA; }
|
|
void reset();
|
|
int get2DCode();
|
|
int getBlackCode();
|
|
int getWhiteCode();
|
|
Guint get24Bits();
|
|
void skipTo(Guint length);
|
|
|
|
private:
|
|
|
|
Stream *str;
|
|
Guint buf;
|
|
Guint bufLen;
|
|
Guint nBytesRead;
|
|
};
|
|
|
|
JBIG2MMRDecoder::JBIG2MMRDecoder() {
|
|
str = NULL;
|
|
reset();
|
|
}
|
|
|
|
JBIG2MMRDecoder::~JBIG2MMRDecoder() {
|
|
}
|
|
|
|
void JBIG2MMRDecoder::reset() {
|
|
buf = 0;
|
|
bufLen = 0;
|
|
nBytesRead = 0;
|
|
}
|
|
|
|
int JBIG2MMRDecoder::get2DCode() {
|
|
CCITTCode *p;
|
|
|
|
if (bufLen == 0) {
|
|
buf = str->getChar() & 0xff;
|
|
bufLen = 8;
|
|
++nBytesRead;
|
|
p = &twoDimTab1[(buf >> 1) & 0x7f];
|
|
} else if (bufLen == 8) {
|
|
p = &twoDimTab1[(buf >> 1) & 0x7f];
|
|
} else {
|
|
p = &twoDimTab1[(buf << (7 - bufLen)) & 0x7f];
|
|
if (p->bits < 0 || p->bits > (int)bufLen) {
|
|
buf = (buf << 8) | (str->getChar() & 0xff);
|
|
bufLen += 8;
|
|
++nBytesRead;
|
|
p = &twoDimTab1[(buf >> (bufLen - 7)) & 0x7f];
|
|
}
|
|
}
|
|
if (p->bits < 0) {
|
|
error(str->getPos(), "Bad two dim code in JBIG2 MMR stream");
|
|
return 0;
|
|
}
|
|
bufLen -= p->bits;
|
|
return p->n;
|
|
}
|
|
|
|
int JBIG2MMRDecoder::getWhiteCode() {
|
|
CCITTCode *p;
|
|
Guint code;
|
|
|
|
if (bufLen == 0) {
|
|
buf = str->getChar() & 0xff;
|
|
bufLen = 8;
|
|
++nBytesRead;
|
|
}
|
|
while (1) {
|
|
if (bufLen >= 7 && ((buf >> (bufLen - 7)) & 0x7f) == 0) {
|
|
if (bufLen <= 12) {
|
|
code = buf << (12 - bufLen);
|
|
} else {
|
|
code = buf >> (bufLen - 12);
|
|
}
|
|
p = &whiteTab1[code & 0x1f];
|
|
} else {
|
|
if (bufLen <= 9) {
|
|
code = buf << (9 - bufLen);
|
|
} else {
|
|
code = buf >> (bufLen - 9);
|
|
}
|
|
p = &whiteTab2[code & 0x1ff];
|
|
}
|
|
if (p->bits > 0 && p->bits <= (int)bufLen) {
|
|
bufLen -= p->bits;
|
|
return p->n;
|
|
}
|
|
if (bufLen >= 12) {
|
|
break;
|
|
}
|
|
buf = (buf << 8) | (str->getChar() & 0xff);
|
|
bufLen += 8;
|
|
++nBytesRead;
|
|
}
|
|
error(str->getPos(), "Bad white code in JBIG2 MMR stream");
|
|
// eat a bit and return a positive number so that the caller doesn't
|
|
// go into an infinite loop
|
|
--bufLen;
|
|
return 1;
|
|
}
|
|
|
|
int JBIG2MMRDecoder::getBlackCode() {
|
|
CCITTCode *p;
|
|
Guint code;
|
|
|
|
if (bufLen == 0) {
|
|
buf = str->getChar() & 0xff;
|
|
bufLen = 8;
|
|
++nBytesRead;
|
|
}
|
|
while (1) {
|
|
if (bufLen >= 6 && ((buf >> (bufLen - 6)) & 0x3f) == 0) {
|
|
if (bufLen <= 13) {
|
|
code = buf << (13 - bufLen);
|
|
} else {
|
|
code = buf >> (bufLen - 13);
|
|
}
|
|
p = &blackTab1[code & 0x7f];
|
|
} else if (bufLen >= 4 && ((buf >> (bufLen - 4)) & 0x0f) == 0) {
|
|
if (bufLen <= 12) {
|
|
code = buf << (12 - bufLen);
|
|
} else {
|
|
code = buf >> (bufLen - 12);
|
|
}
|
|
p = &blackTab2[(code & 0xff) - 64];
|
|
} else {
|
|
if (bufLen <= 6) {
|
|
code = buf << (6 - bufLen);
|
|
} else {
|
|
code = buf >> (bufLen - 6);
|
|
}
|
|
p = &blackTab3[code & 0x3f];
|
|
}
|
|
if (p->bits > 0 && p->bits <= (int)bufLen) {
|
|
bufLen -= p->bits;
|
|
return p->n;
|
|
}
|
|
if (bufLen >= 13) {
|
|
break;
|
|
}
|
|
buf = (buf << 8) | (str->getChar() & 0xff);
|
|
bufLen += 8;
|
|
++nBytesRead;
|
|
}
|
|
error(str->getPos(), "Bad black code in JBIG2 MMR stream");
|
|
// eat a bit and return a positive number so that the caller doesn't
|
|
// go into an infinite loop
|
|
--bufLen;
|
|
return 1;
|
|
}
|
|
|
|
Guint JBIG2MMRDecoder::get24Bits() {
|
|
while (bufLen < 24) {
|
|
buf = (buf << 8) | (str->getChar() & 0xff);
|
|
bufLen += 8;
|
|
++nBytesRead;
|
|
}
|
|
return (buf >> (bufLen - 24)) & 0xffffff;
|
|
}
|
|
|
|
void JBIG2MMRDecoder::skipTo(Guint length) {
|
|
while (nBytesRead < length) {
|
|
str->getChar();
|
|
++nBytesRead;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// JBIG2Segment
|
|
//------------------------------------------------------------------------
|
|
|
|
enum JBIG2SegmentType {
|
|
jbig2SegBitmap,
|
|
jbig2SegSymbolDict,
|
|
jbig2SegPatternDict,
|
|
jbig2SegCodeTable
|
|
};
|
|
|
|
class JBIG2Segment {
|
|
public:
|
|
|
|
JBIG2Segment(Guint segNumA) { segNum = segNumA; }
|
|
virtual ~JBIG2Segment() {}
|
|
void setSegNum(Guint segNumA) { segNum = segNumA; }
|
|
Guint getSegNum() { return segNum; }
|
|
virtual JBIG2SegmentType getType() = 0;
|
|
|
|
private:
|
|
|
|
Guint segNum;
|
|
};
|
|
|
|
//------------------------------------------------------------------------
|
|
// JBIG2Bitmap
|
|
//------------------------------------------------------------------------
|
|
|
|
struct JBIG2BitmapPtr {
|
|
Guchar *p;
|
|
int shift;
|
|
int x;
|
|
};
|
|
|
|
class JBIG2Bitmap: public JBIG2Segment {
|
|
public:
|
|
|
|
JBIG2Bitmap(Guint segNumA, int wA, int hA);
|
|
virtual ~JBIG2Bitmap();
|
|
virtual JBIG2SegmentType getType() { return jbig2SegBitmap; }
|
|
JBIG2Bitmap *copy() { return new JBIG2Bitmap(0, this); }
|
|
JBIG2Bitmap *getSlice(Guint x, Guint y, Guint wA, Guint hA);
|
|
void expand(int newH, Guint pixel);
|
|
void clearToZero();
|
|
void clearToOne();
|
|
int getWidth() { return w; }
|
|
int getHeight() { return h; }
|
|
int getPixel(int x, int y)
|
|
{ return (x < 0 || x >= w || y < 0 || y >= h) ? 0 :
|
|
(data[y * line + (x >> 3)] >> (7 - (x & 7))) & 1; }
|
|
void setPixel(int x, int y)
|
|
{ data[y * line + (x >> 3)] |= 1 << (7 - (x & 7)); }
|
|
void clearPixel(int x, int y)
|
|
{ data[y * line + (x >> 3)] &= 0x7f7f >> (x & 7); }
|
|
void getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr);
|
|
int nextPixel(JBIG2BitmapPtr *ptr);
|
|
void duplicateRow(int yDest, int ySrc);
|
|
void combine(JBIG2Bitmap *bitmap, int x, int y, Guint combOp);
|
|
Guchar *getDataPtr() { return data; }
|
|
int getDataSize() { return h * line; }
|
|
|
|
private:
|
|
|
|
JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap);
|
|
|
|
int w, h, line;
|
|
Guchar *data;
|
|
};
|
|
|
|
JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, int wA, int hA):
|
|
JBIG2Segment(segNumA)
|
|
{
|
|
w = wA;
|
|
h = hA;
|
|
line = (wA + 7) >> 3;
|
|
if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) {
|
|
data = NULL;
|
|
return;
|
|
}
|
|
// need to allocate one extra guard byte for use in combine()
|
|
data = (Guchar *)gmalloc(h * line + 1);
|
|
data[h * line] = 0;
|
|
}
|
|
|
|
JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap):
|
|
JBIG2Segment(segNumA)
|
|
{
|
|
w = bitmap->w;
|
|
h = bitmap->h;
|
|
line = bitmap->line;
|
|
if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) {
|
|
data = NULL;
|
|
return;
|
|
}
|
|
// need to allocate one extra guard byte for use in combine()
|
|
data = (Guchar *)gmalloc(h * line + 1);
|
|
memcpy(data, bitmap->data, h * line);
|
|
data[h * line] = 0;
|
|
}
|
|
|
|
JBIG2Bitmap::~JBIG2Bitmap() {
|
|
gfree(data);
|
|
}
|
|
|
|
//~ optimize this
|
|
JBIG2Bitmap *JBIG2Bitmap::getSlice(Guint x, Guint y, Guint wA, Guint hA) {
|
|
JBIG2Bitmap *slice;
|
|
Guint xx, yy;
|
|
|
|
slice = new JBIG2Bitmap(0, wA, hA);
|
|
slice->clearToZero();
|
|
for (yy = 0; yy < hA; ++yy) {
|
|
for (xx = 0; xx < wA; ++xx) {
|
|
if (getPixel(x + xx, y + yy)) {
|
|
slice->setPixel(xx, yy);
|
|
}
|
|
}
|
|
}
|
|
return slice;
|
|
}
|
|
|
|
void JBIG2Bitmap::expand(int newH, Guint pixel) {
|
|
if (newH <= h || line <= 0 || newH >= (INT_MAX - 1) / line) {
|
|
return;
|
|
}
|
|
// need to allocate one extra guard byte for use in combine()
|
|
data = (Guchar *)grealloc(data, newH * line + 1);
|
|
if (pixel) {
|
|
memset(data + h * line, 0xff, (newH - h) * line);
|
|
} else {
|
|
memset(data + h * line, 0x00, (newH - h) * line);
|
|
}
|
|
h = newH;
|
|
data[h * line] = 0;
|
|
}
|
|
|
|
void JBIG2Bitmap::clearToZero() {
|
|
memset(data, 0, h * line);
|
|
}
|
|
|
|
void JBIG2Bitmap::clearToOne() {
|
|
memset(data, 0xff, h * line);
|
|
}
|
|
|
|
inline void JBIG2Bitmap::getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr) {
|
|
if (y < 0 || y >= h || x >= w) {
|
|
ptr->p = NULL;
|
|
} else if (x < 0) {
|
|
ptr->p = &data[y * line];
|
|
ptr->shift = 7;
|
|
ptr->x = x;
|
|
} else {
|
|
ptr->p = &data[y * line + (x >> 3)];
|
|
ptr->shift = 7 - (x & 7);
|
|
ptr->x = x;
|
|
}
|
|
}
|
|
|
|
inline int JBIG2Bitmap::nextPixel(JBIG2BitmapPtr *ptr) {
|
|
int pix;
|
|
|
|
if (!ptr->p) {
|
|
pix = 0;
|
|
} else if (ptr->x < 0) {
|
|
++ptr->x;
|
|
pix = 0;
|
|
} else {
|
|
pix = (*ptr->p >> ptr->shift) & 1;
|
|
if (++ptr->x == w) {
|
|
ptr->p = NULL;
|
|
} else if (ptr->shift == 0) {
|
|
++ptr->p;
|
|
ptr->shift = 7;
|
|
} else {
|
|
--ptr->shift;
|
|
}
|
|
}
|
|
return pix;
|
|
}
|
|
|
|
void JBIG2Bitmap::duplicateRow(int yDest, int ySrc) {
|
|
memcpy(data + yDest * line, data + ySrc * line, line);
|
|
}
|
|
|
|
void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y,
|
|
Guint combOp) {
|
|
int x0, x1, y0, y1, xx, yy;
|
|
Guchar *srcPtr, *destPtr;
|
|
Guint src0, src1, src, dest, s1, s2, m1, m2, m3;
|
|
GBool oneByte;
|
|
|
|
if (y < 0) {
|
|
y0 = -y;
|
|
} else {
|
|
y0 = 0;
|
|
}
|
|
if (y + bitmap->h > h) {
|
|
y1 = h - y;
|
|
} else {
|
|
y1 = bitmap->h;
|
|
}
|
|
if (y0 >= y1) {
|
|
return;
|
|
}
|
|
|
|
if (x >= 0) {
|
|
x0 = x & ~7;
|
|
} else {
|
|
x0 = 0;
|
|
}
|
|
x1 = x + bitmap->w;
|
|
if (x1 > w) {
|
|
x1 = w;
|
|
}
|
|
if (x0 >= x1) {
|
|
return;
|
|
}
|
|
|
|
s1 = x & 7;
|
|
s2 = 8 - s1;
|
|
m1 = 0xff >> (x1 & 7);
|
|
m2 = 0xff << (((x1 & 7) == 0) ? 0 : 8 - (x1 & 7));
|
|
m3 = (0xff >> s1) & m2;
|
|
|
|
oneByte = x0 == ((x1 - 1) & ~7);
|
|
|
|
for (yy = y0; yy < y1; ++yy) {
|
|
|
|
// one byte per line -- need to mask both left and right side
|
|
if (oneByte) {
|
|
if (x >= 0) {
|
|
destPtr = data + (y + yy) * line + (x >> 3);
|
|
srcPtr = bitmap->data + yy * bitmap->line;
|
|
dest = *destPtr;
|
|
src1 = *srcPtr;
|
|
switch (combOp) {
|
|
case 0: // or
|
|
dest |= (src1 >> s1) & m2;
|
|
break;
|
|
case 1: // and
|
|
dest &= ((0xff00 | src1) >> s1) | m1;
|
|
break;
|
|
case 2: // xor
|
|
dest ^= (src1 >> s1) & m2;
|
|
break;
|
|
case 3: // xnor
|
|
dest ^= ((src1 ^ 0xff) >> s1) & m2;
|
|
break;
|
|
case 4: // replace
|
|
dest = (dest & ~m3) | ((src1 >> s1) & m3);
|
|
break;
|
|
}
|
|
*destPtr = dest;
|
|
} else {
|
|
destPtr = data + (y + yy) * line;
|
|
srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3);
|
|
dest = *destPtr;
|
|
src1 = *srcPtr;
|
|
switch (combOp) {
|
|
case 0: // or
|
|
dest |= src1 & m2;
|
|
break;
|
|
case 1: // and
|
|
dest &= src1 | m1;
|
|
break;
|
|
case 2: // xor
|
|
dest ^= src1 & m2;
|
|
break;
|
|
case 3: // xnor
|
|
dest ^= (src1 ^ 0xff) & m2;
|
|
break;
|
|
case 4: // replace
|
|
dest = (src1 & m2) | (dest & m1);
|
|
break;
|
|
}
|
|
*destPtr = dest;
|
|
}
|
|
|
|
// multiple bytes per line -- need to mask left side of left-most
|
|
// byte and right side of right-most byte
|
|
} else {
|
|
|
|
// left-most byte
|
|
if (x >= 0) {
|
|
destPtr = data + (y + yy) * line + (x >> 3);
|
|
srcPtr = bitmap->data + yy * bitmap->line;
|
|
src1 = *srcPtr++;
|
|
dest = *destPtr;
|
|
switch (combOp) {
|
|
case 0: // or
|
|
dest |= src1 >> s1;
|
|
break;
|
|
case 1: // and
|
|
dest &= (0xff00 | src1) >> s1;
|
|
break;
|
|
case 2: // xor
|
|
dest ^= src1 >> s1;
|
|
break;
|
|
case 3: // xnor
|
|
dest ^= (src1 ^ 0xff) >> s1;
|
|
break;
|
|
case 4: // replace
|
|
dest = (dest & (0xff << s2)) | (src1 >> s1);
|
|
break;
|
|
}
|
|
*destPtr++ = dest;
|
|
xx = x0 + 8;
|
|
} else {
|
|
destPtr = data + (y + yy) * line;
|
|
srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3);
|
|
src1 = *srcPtr++;
|
|
xx = x0;
|
|
}
|
|
|
|
// middle bytes
|
|
for (; xx < x1 - 8; xx += 8) {
|
|
dest = *destPtr;
|
|
src0 = src1;
|
|
src1 = *srcPtr++;
|
|
src = (((src0 << 8) | src1) >> s1) & 0xff;
|
|
switch (combOp) {
|
|
case 0: // or
|
|
dest |= src;
|
|
break;
|
|
case 1: // and
|
|
dest &= src;
|
|
break;
|
|
case 2: // xor
|
|
dest ^= src;
|
|
break;
|
|
case 3: // xnor
|
|
dest ^= src ^ 0xff;
|
|
break;
|
|
case 4: // replace
|
|
dest = src;
|
|
break;
|
|
}
|
|
*destPtr++ = dest;
|
|
}
|
|
|
|
// right-most byte
|
|
// note: this last byte (src1) may not actually be used, depending
|
|
// on the values of s1, m1, and m2 - and in fact, it may be off
|
|
// the edge of the source bitmap, which means we need to allocate
|
|
// one extra guard byte at the end of each bitmap
|
|
dest = *destPtr;
|
|
src0 = src1;
|
|
src1 = *srcPtr++;
|
|
src = (((src0 << 8) | src1) >> s1) & 0xff;
|
|
switch (combOp) {
|
|
case 0: // or
|
|
dest |= src & m2;
|
|
break;
|
|
case 1: // and
|
|
dest &= src | m1;
|
|
break;
|
|
case 2: // xor
|
|
dest ^= src & m2;
|
|
break;
|
|
case 3: // xnor
|
|
dest ^= (src ^ 0xff) & m2;
|
|
break;
|
|
case 4: // replace
|
|
dest = (src & m2) | (dest & m1);
|
|
break;
|
|
}
|
|
*destPtr = dest;
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// JBIG2SymbolDict
|
|
//------------------------------------------------------------------------
|
|
|
|
class JBIG2SymbolDict: public JBIG2Segment {
|
|
public:
|
|
|
|
JBIG2SymbolDict(Guint segNumA, Guint sizeA);
|
|
virtual ~JBIG2SymbolDict();
|
|
virtual JBIG2SegmentType getType() { return jbig2SegSymbolDict; }
|
|
Guint getSize() { return size; }
|
|
void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }
|
|
JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; }
|
|
void setGenericRegionStats(JArithmeticDecoderStats *stats)
|
|
{ genericRegionStats = stats; }
|
|
void setRefinementRegionStats(JArithmeticDecoderStats *stats)
|
|
{ refinementRegionStats = stats; }
|
|
JArithmeticDecoderStats *getGenericRegionStats()
|
|
{ return genericRegionStats; }
|
|
JArithmeticDecoderStats *getRefinementRegionStats()
|
|
{ return refinementRegionStats; }
|
|
|
|
private:
|
|
|
|
Guint size;
|
|
JBIG2Bitmap **bitmaps;
|
|
JArithmeticDecoderStats *genericRegionStats;
|
|
JArithmeticDecoderStats *refinementRegionStats;
|
|
};
|
|
|
|
JBIG2SymbolDict::JBIG2SymbolDict(Guint segNumA, Guint sizeA):
|
|
JBIG2Segment(segNumA)
|
|
{
|
|
size = sizeA;
|
|
bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *));
|
|
genericRegionStats = NULL;
|
|
refinementRegionStats = NULL;
|
|
}
|
|
|
|
JBIG2SymbolDict::~JBIG2SymbolDict() {
|
|
Guint i;
|
|
|
|
for (i = 0; i < size; ++i) {
|
|
delete bitmaps[i];
|
|
}
|
|
gfree(bitmaps);
|
|
if (genericRegionStats) {
|
|
delete genericRegionStats;
|
|
}
|
|
if (refinementRegionStats) {
|
|
delete refinementRegionStats;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// JBIG2PatternDict
|
|
//------------------------------------------------------------------------
|
|
|
|
class JBIG2PatternDict: public JBIG2Segment {
|
|
public:
|
|
|
|
JBIG2PatternDict(Guint segNumA, Guint sizeA);
|
|
virtual ~JBIG2PatternDict();
|
|
virtual JBIG2SegmentType getType() { return jbig2SegPatternDict; }
|
|
Guint getSize() { return size; }
|
|
void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }
|
|
JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; }
|
|
|
|
private:
|
|
|
|
Guint size;
|
|
JBIG2Bitmap **bitmaps;
|
|
};
|
|
|
|
JBIG2PatternDict::JBIG2PatternDict(Guint segNumA, Guint sizeA):
|
|
JBIG2Segment(segNumA)
|
|
{
|
|
size = sizeA;
|
|
bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *));
|
|
}
|
|
|
|
JBIG2PatternDict::~JBIG2PatternDict() {
|
|
Guint i;
|
|
|
|
for (i = 0; i < size; ++i) {
|
|
delete bitmaps[i];
|
|
}
|
|
gfree(bitmaps);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// JBIG2CodeTable
|
|
//------------------------------------------------------------------------
|
|
|
|
class JBIG2CodeTable: public JBIG2Segment {
|
|
public:
|
|
|
|
JBIG2CodeTable(Guint segNumA, JBIG2HuffmanTable *tableA);
|
|
virtual ~JBIG2CodeTable();
|
|
virtual JBIG2SegmentType getType() { return jbig2SegCodeTable; }
|
|
JBIG2HuffmanTable *getHuffTable() { return table; }
|
|
|
|
private:
|
|
|
|
JBIG2HuffmanTable *table;
|
|
};
|
|
|
|
JBIG2CodeTable::JBIG2CodeTable(Guint segNumA, JBIG2HuffmanTable *tableA):
|
|
JBIG2Segment(segNumA)
|
|
{
|
|
table = tableA;
|
|
}
|
|
|
|
JBIG2CodeTable::~JBIG2CodeTable() {
|
|
gfree(table);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// JBIG2Stream
|
|
//------------------------------------------------------------------------
|
|
|
|
JBIG2Stream::JBIG2Stream(Stream *strA, Object *globalsStreamA):
|
|
FilterStream(strA)
|
|
{
|
|
pageBitmap = NULL;
|
|
|
|
arithDecoder = new JArithmeticDecoder();
|
|
genericRegionStats = new JArithmeticDecoderStats(1 << 1);
|
|
refinementRegionStats = new JArithmeticDecoderStats(1 << 1);
|
|
iadhStats = new JArithmeticDecoderStats(1 << 9);
|
|
iadwStats = new JArithmeticDecoderStats(1 << 9);
|
|
iaexStats = new JArithmeticDecoderStats(1 << 9);
|
|
iaaiStats = new JArithmeticDecoderStats(1 << 9);
|
|
iadtStats = new JArithmeticDecoderStats(1 << 9);
|
|
iaitStats = new JArithmeticDecoderStats(1 << 9);
|
|
iafsStats = new JArithmeticDecoderStats(1 << 9);
|
|
iadsStats = new JArithmeticDecoderStats(1 << 9);
|
|
iardxStats = new JArithmeticDecoderStats(1 << 9);
|
|
iardyStats = new JArithmeticDecoderStats(1 << 9);
|
|
iardwStats = new JArithmeticDecoderStats(1 << 9);
|
|
iardhStats = new JArithmeticDecoderStats(1 << 9);
|
|
iariStats = new JArithmeticDecoderStats(1 << 9);
|
|
iaidStats = new JArithmeticDecoderStats(1 << 1);
|
|
huffDecoder = new JBIG2HuffmanDecoder();
|
|
mmrDecoder = new JBIG2MMRDecoder();
|
|
|
|
globalsStreamA->copy(&globalsStream);
|
|
segments = globalSegments = NULL;
|
|
curStr = NULL;
|
|
dataPtr = dataEnd = NULL;
|
|
}
|
|
|
|
JBIG2Stream::~JBIG2Stream() {
|
|
close();
|
|
globalsStream.free();
|
|
delete arithDecoder;
|
|
delete genericRegionStats;
|
|
delete refinementRegionStats;
|
|
delete iadhStats;
|
|
delete iadwStats;
|
|
delete iaexStats;
|
|
delete iaaiStats;
|
|
delete iadtStats;
|
|
delete iaitStats;
|
|
delete iafsStats;
|
|
delete iadsStats;
|
|
delete iardxStats;
|
|
delete iardyStats;
|
|
delete iardwStats;
|
|
delete iardhStats;
|
|
delete iariStats;
|
|
delete iaidStats;
|
|
delete huffDecoder;
|
|
delete mmrDecoder;
|
|
delete str;
|
|
}
|
|
|
|
void JBIG2Stream::reset() {
|
|
// read the globals stream
|
|
globalSegments = new GList();
|
|
if (globalsStream.isStream()) {
|
|
segments = globalSegments;
|
|
curStr = globalsStream.getStream();
|
|
curStr->reset();
|
|
arithDecoder->setStream(curStr);
|
|
huffDecoder->setStream(curStr);
|
|
mmrDecoder->setStream(curStr);
|
|
readSegments();
|
|
curStr->close();
|
|
}
|
|
|
|
// read the main stream
|
|
segments = new GList();
|
|
curStr = str;
|
|
curStr->reset();
|
|
arithDecoder->setStream(curStr);
|
|
huffDecoder->setStream(curStr);
|
|
mmrDecoder->setStream(curStr);
|
|
readSegments();
|
|
|
|
if (pageBitmap) {
|
|
dataPtr = pageBitmap->getDataPtr();
|
|
dataEnd = dataPtr + pageBitmap->getDataSize();
|
|
} else {
|
|
dataPtr = dataEnd = NULL;
|
|
}
|
|
}
|
|
|
|
void JBIG2Stream::close() {
|
|
if (pageBitmap) {
|
|
delete pageBitmap;
|
|
pageBitmap = NULL;
|
|
}
|
|
if (segments) {
|
|
deleteGList(segments, JBIG2Segment);
|
|
segments = NULL;
|
|
}
|
|
if (globalSegments) {
|
|
deleteGList(globalSegments, JBIG2Segment);
|
|
globalSegments = NULL;
|
|
}
|
|
dataPtr = dataEnd = NULL;
|
|
FilterStream::close();
|
|
}
|
|
|
|
int JBIG2Stream::getChar() {
|
|
if (dataPtr && dataPtr < dataEnd) {
|
|
return (*dataPtr++ ^ 0xff) & 0xff;
|
|
}
|
|
return EOF;
|
|
}
|
|
|
|
int JBIG2Stream::lookChar() {
|
|
if (dataPtr && dataPtr < dataEnd) {
|
|
return (*dataPtr ^ 0xff) & 0xff;
|
|
}
|
|
return EOF;
|
|
}
|
|
|
|
GString *JBIG2Stream::getPSFilter(int psLevel, char *indent) {
|
|
return NULL;
|
|
}
|
|
|
|
GBool JBIG2Stream::isBinary(GBool last) {
|
|
return str->isBinary(gTrue);
|
|
}
|
|
|
|
void JBIG2Stream::readSegments() {
|
|
Guint segNum, segFlags, segType, page, segLength;
|
|
Guint refFlags, nRefSegs;
|
|
Guint *refSegs;
|
|
int c1, c2, c3;
|
|
Guint i;
|
|
|
|
while (readULong(&segNum)) {
|
|
|
|
// segment header flags
|
|
if (!readUByte(&segFlags)) {
|
|
goto eofError1;
|
|
}
|
|
segType = segFlags & 0x3f;
|
|
|
|
// referred-to segment count and retention flags
|
|
if (!readUByte(&refFlags)) {
|
|
goto eofError1;
|
|
}
|
|
nRefSegs = refFlags >> 5;
|
|
if (nRefSegs == 7) {
|
|
if ((c1 = curStr->getChar()) == EOF ||
|
|
(c2 = curStr->getChar()) == EOF ||
|
|
(c3 = curStr->getChar()) == EOF) {
|
|
goto eofError1;
|
|
}
|
|
refFlags = (refFlags << 24) | (c1 << 16) | (c2 << 8) | c3;
|
|
nRefSegs = refFlags & 0x1fffffff;
|
|
for (i = 0; i < (nRefSegs + 9) >> 3; ++i) {
|
|
c1 = curStr->getChar();
|
|
}
|
|
}
|
|
|
|
// referred-to segment numbers
|
|
refSegs = (Guint *)gmallocn(nRefSegs, sizeof(Guint));
|
|
if (segNum <= 256) {
|
|
for (i = 0; i < nRefSegs; ++i) {
|
|
if (!readUByte(&refSegs[i])) {
|
|
goto eofError2;
|
|
}
|
|
}
|
|
} else if (segNum <= 65536) {
|
|
for (i = 0; i < nRefSegs; ++i) {
|
|
if (!readUWord(&refSegs[i])) {
|
|
goto eofError2;
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 0; i < nRefSegs; ++i) {
|
|
if (!readULong(&refSegs[i])) {
|
|
goto eofError2;
|
|
}
|
|
}
|
|
}
|
|
|
|
// segment page association
|
|
if (segFlags & 0x40) {
|
|
if (!readULong(&page)) {
|
|
goto eofError2;
|
|
}
|
|
} else {
|
|
if (!readUByte(&page)) {
|
|
goto eofError2;
|
|
}
|
|
}
|
|
|
|
// segment data length
|
|
if (!readULong(&segLength)) {
|
|
goto eofError2;
|
|
}
|
|
|
|
// read the segment data
|
|
switch (segType) {
|
|
case 0:
|
|
if (!readSymbolDictSeg(segNum, segLength, refSegs, nRefSegs)) {
|
|
goto syntaxError;
|
|
}
|
|
break;
|
|
case 4:
|
|
readTextRegionSeg(segNum, gFalse, gFalse, segLength, refSegs, nRefSegs);
|
|
break;
|
|
case 6:
|
|
readTextRegionSeg(segNum, gTrue, gFalse, segLength, refSegs, nRefSegs);
|
|
break;
|
|
case 7:
|
|
readTextRegionSeg(segNum, gTrue, gTrue, segLength, refSegs, nRefSegs);
|
|
break;
|
|
case 16:
|
|
readPatternDictSeg(segNum, segLength);
|
|
break;
|
|
case 20:
|
|
readHalftoneRegionSeg(segNum, gFalse, gFalse, segLength,
|
|
refSegs, nRefSegs);
|
|
break;
|
|
case 22:
|
|
readHalftoneRegionSeg(segNum, gTrue, gFalse, segLength,
|
|
refSegs, nRefSegs);
|
|
break;
|
|
case 23:
|
|
readHalftoneRegionSeg(segNum, gTrue, gTrue, segLength,
|
|
refSegs, nRefSegs);
|
|
break;
|
|
case 36:
|
|
readGenericRegionSeg(segNum, gFalse, gFalse, segLength);
|
|
break;
|
|
case 38:
|
|
readGenericRegionSeg(segNum, gTrue, gFalse, segLength);
|
|
break;
|
|
case 39:
|
|
readGenericRegionSeg(segNum, gTrue, gTrue, segLength);
|
|
break;
|
|
case 40:
|
|
readGenericRefinementRegionSeg(segNum, gFalse, gFalse, segLength,
|
|
refSegs, nRefSegs);
|
|
break;
|
|
case 42:
|
|
readGenericRefinementRegionSeg(segNum, gTrue, gFalse, segLength,
|
|
refSegs, nRefSegs);
|
|
break;
|
|
case 43:
|
|
readGenericRefinementRegionSeg(segNum, gTrue, gTrue, segLength,
|
|
refSegs, nRefSegs);
|
|
break;
|
|
case 48:
|
|
readPageInfoSeg(segLength);
|
|
break;
|
|
case 50:
|
|
readEndOfStripeSeg(segLength);
|
|
break;
|
|
case 52:
|
|
readProfilesSeg(segLength);
|
|
break;
|
|
case 53:
|
|
readCodeTableSeg(segNum, segLength);
|
|
break;
|
|
case 62:
|
|
readExtensionSeg(segLength);
|
|
break;
|
|
default:
|
|
error(getPos(), "Unknown segment type in JBIG2 stream");
|
|
for (i = 0; i < segLength; ++i) {
|
|
if ((c1 = curStr->getChar()) == EOF) {
|
|
goto eofError2;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
gfree(refSegs);
|
|
}
|
|
|
|
return;
|
|
|
|
syntaxError:
|
|
gfree(refSegs);
|
|
return;
|
|
|
|
eofError2:
|
|
gfree(refSegs);
|
|
eofError1:
|
|
error(getPos(), "Unexpected EOF in JBIG2 stream");
|
|
}
|
|
|
|
GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
|
|
Guint *refSegs, Guint nRefSegs) {
|
|
JBIG2SymbolDict *symbolDict;
|
|
JBIG2HuffmanTable *huffDHTable, *huffDWTable;
|
|
JBIG2HuffmanTable *huffBMSizeTable, *huffAggInstTable;
|
|
JBIG2Segment *seg;
|
|
GList *codeTables;
|
|
JBIG2SymbolDict *inputSymbolDict;
|
|
Guint flags, sdTemplate, sdrTemplate, huff, refAgg;
|
|
Guint huffDH, huffDW, huffBMSize, huffAggInst;
|
|
Guint contextUsed, contextRetained;
|
|
int sdATX[4], sdATY[4], sdrATX[2], sdrATY[2];
|
|
Guint numExSyms, numNewSyms, numInputSyms, symCodeLen;
|
|
JBIG2Bitmap **bitmaps;
|
|
JBIG2Bitmap *collBitmap, *refBitmap;
|
|
Guint *symWidths;
|
|
Guint symHeight, symWidth, totalWidth, x, symID;
|
|
int dh, dw, refAggNum, refDX, refDY, bmSize;
|
|
GBool ex;
|
|
int run, cnt;
|
|
Guint i, j, k;
|
|
Guchar *p;
|
|
|
|
// symbol dictionary flags
|
|
if (!readUWord(&flags)) {
|
|
goto eofError;
|
|
}
|
|
sdTemplate = (flags >> 10) & 3;
|
|
sdrTemplate = (flags >> 12) & 1;
|
|
huff = flags & 1;
|
|
refAgg = (flags >> 1) & 1;
|
|
huffDH = (flags >> 2) & 3;
|
|
huffDW = (flags >> 4) & 3;
|
|
huffBMSize = (flags >> 6) & 1;
|
|
huffAggInst = (flags >> 7) & 1;
|
|
contextUsed = (flags >> 8) & 1;
|
|
contextRetained = (flags >> 9) & 1;
|
|
|
|
// symbol dictionary AT flags
|
|
if (!huff) {
|
|
if (sdTemplate == 0) {
|
|
if (!readByte(&sdATX[0]) ||
|
|
!readByte(&sdATY[0]) ||
|
|
!readByte(&sdATX[1]) ||
|
|
!readByte(&sdATY[1]) ||
|
|
!readByte(&sdATX[2]) ||
|
|
!readByte(&sdATY[2]) ||
|
|
!readByte(&sdATX[3]) ||
|
|
!readByte(&sdATY[3])) {
|
|
goto eofError;
|
|
}
|
|
} else {
|
|
if (!readByte(&sdATX[0]) ||
|
|
!readByte(&sdATY[0])) {
|
|
goto eofError;
|
|
}
|
|
}
|
|
}
|
|
|
|
// symbol dictionary refinement AT flags
|
|
if (refAgg && !sdrTemplate) {
|
|
if (!readByte(&sdrATX[0]) ||
|
|
!readByte(&sdrATY[0]) ||
|
|
!readByte(&sdrATX[1]) ||
|
|
!readByte(&sdrATY[1])) {
|
|
goto eofError;
|
|
}
|
|
}
|
|
|
|
// SDNUMEXSYMS and SDNUMNEWSYMS
|
|
if (!readULong(&numExSyms) || !readULong(&numNewSyms)) {
|
|
goto eofError;
|
|
}
|
|
|
|
// get referenced segments: input symbol dictionaries and code tables
|
|
codeTables = new GList();
|
|
numInputSyms = 0;
|
|
for (i = 0; i < nRefSegs; ++i) {
|
|
seg = findSegment(refSegs[i]);
|
|
if (seg->getType() == jbig2SegSymbolDict) {
|
|
numInputSyms += ((JBIG2SymbolDict *)seg)->getSize();
|
|
} else if (seg->getType() == jbig2SegCodeTable) {
|
|
codeTables->append(seg);
|
|
}
|
|
}
|
|
|
|
// compute symbol code length
|
|
symCodeLen = 0;
|
|
i = 1;
|
|
while (i < numInputSyms + numNewSyms) {
|
|
++symCodeLen;
|
|
i <<= 1;
|
|
}
|
|
|
|
// get the input symbol bitmaps
|
|
bitmaps = (JBIG2Bitmap **)gmallocn(numInputSyms + numNewSyms,
|
|
sizeof(JBIG2Bitmap *));
|
|
for (i = 0; i < numInputSyms + numNewSyms; ++i) {
|
|
bitmaps[i] = NULL;
|
|
}
|
|
k = 0;
|
|
inputSymbolDict = NULL;
|
|
for (i = 0; i < nRefSegs; ++i) {
|
|
seg = findSegment(refSegs[i]);
|
|
if (seg->getType() == jbig2SegSymbolDict) {
|
|
inputSymbolDict = (JBIG2SymbolDict *)seg;
|
|
for (j = 0; j < inputSymbolDict->getSize(); ++j) {
|
|
bitmaps[k++] = inputSymbolDict->getBitmap(j);
|
|
}
|
|
}
|
|
}
|
|
|
|
// get the Huffman tables
|
|
huffDHTable = huffDWTable = NULL; // make gcc happy
|
|
huffBMSizeTable = huffAggInstTable = NULL; // make gcc happy
|
|
i = 0;
|
|
if (huff) {
|
|
if (huffDH == 0) {
|
|
huffDHTable = huffTableD;
|
|
} else if (huffDH == 1) {
|
|
huffDHTable = huffTableE;
|
|
} else {
|
|
huffDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
|
|
}
|
|
if (huffDW == 0) {
|
|
huffDWTable = huffTableB;
|
|
} else if (huffDW == 1) {
|
|
huffDWTable = huffTableC;
|
|
} else {
|
|
huffDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
|
|
}
|
|
if (huffBMSize == 0) {
|
|
huffBMSizeTable = huffTableA;
|
|
} else {
|
|
huffBMSizeTable =
|
|
((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
|
|
}
|
|
if (huffAggInst == 0) {
|
|
huffAggInstTable = huffTableA;
|
|
} else {
|
|
huffAggInstTable =
|
|
((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
|
|
}
|
|
}
|
|
delete codeTables;
|
|
|
|
// set up the Huffman decoder
|
|
if (huff) {
|
|
huffDecoder->reset();
|
|
|
|
// set up the arithmetic decoder
|
|
} else {
|
|
if (contextUsed && inputSymbolDict) {
|
|
resetGenericStats(sdTemplate, inputSymbolDict->getGenericRegionStats());
|
|
} else {
|
|
resetGenericStats(sdTemplate, NULL);
|
|
}
|
|
resetIntStats(symCodeLen);
|
|
arithDecoder->start();
|
|
}
|
|
|
|
// set up the arithmetic decoder for refinement/aggregation
|
|
if (refAgg) {
|
|
if (contextUsed && inputSymbolDict) {
|
|
resetRefinementStats(sdrTemplate,
|
|
inputSymbolDict->getRefinementRegionStats());
|
|
} else {
|
|
resetRefinementStats(sdrTemplate, NULL);
|
|
}
|
|
}
|
|
|
|
// allocate symbol widths storage
|
|
symWidths = NULL;
|
|
if (huff && !refAgg) {
|
|
symWidths = (Guint *)gmallocn(numNewSyms, sizeof(Guint));
|
|
}
|
|
|
|
symHeight = 0;
|
|
i = 0;
|
|
while (i < numNewSyms) {
|
|
|
|
// read the height class delta height
|
|
if (huff) {
|
|
huffDecoder->decodeInt(&dh, huffDHTable);
|
|
} else {
|
|
arithDecoder->decodeInt(&dh, iadhStats);
|
|
}
|
|
if (dh < 0 && (Guint)-dh >= symHeight) {
|
|
error(getPos(), "Bad delta-height value in JBIG2 symbol dictionary");
|
|
goto syntaxError;
|
|
}
|
|
symHeight += dh;
|
|
symWidth = 0;
|
|
totalWidth = 0;
|
|
j = i;
|
|
|
|
// read the symbols in this height class
|
|
while (1) {
|
|
|
|
// read the delta width
|
|
if (huff) {
|
|
if (!huffDecoder->decodeInt(&dw, huffDWTable)) {
|
|
break;
|
|
}
|
|
} else {
|
|
if (!arithDecoder->decodeInt(&dw, iadwStats)) {
|
|
break;
|
|
}
|
|
}
|
|
if (dw < 0 && (Guint)-dw >= symWidth) {
|
|
error(getPos(), "Bad delta-height value in JBIG2 symbol dictionary");
|
|
goto syntaxError;
|
|
}
|
|
symWidth += dw;
|
|
|
|
// using a collective bitmap, so don't read a bitmap here
|
|
if (huff && !refAgg) {
|
|
symWidths[i] = symWidth;
|
|
totalWidth += symWidth;
|
|
|
|
// refinement/aggregate coding
|
|
} else if (refAgg) {
|
|
if (huff) {
|
|
if (!huffDecoder->decodeInt(&refAggNum, huffAggInstTable)) {
|
|
break;
|
|
}
|
|
} else {
|
|
if (!arithDecoder->decodeInt(&refAggNum, iaaiStats)) {
|
|
break;
|
|
}
|
|
}
|
|
#if 0 //~ This special case was added about a year before the final draft
|
|
//~ of the JBIG2 spec was released. I have encountered some old
|
|
//~ JBIG2 images that predate it.
|
|
if (0) {
|
|
#else
|
|
if (refAggNum == 1) {
|
|
#endif
|
|
if (huff) {
|
|
symID = huffDecoder->readBits(symCodeLen);
|
|
huffDecoder->decodeInt(&refDX, huffTableO);
|
|
huffDecoder->decodeInt(&refDY, huffTableO);
|
|
huffDecoder->decodeInt(&bmSize, huffTableA);
|
|
huffDecoder->reset();
|
|
arithDecoder->start();
|
|
} else {
|
|
symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);
|
|
arithDecoder->decodeInt(&refDX, iardxStats);
|
|
arithDecoder->decodeInt(&refDY, iardyStats);
|
|
}
|
|
refBitmap = bitmaps[symID];
|
|
bitmaps[numInputSyms + i] =
|
|
readGenericRefinementRegion(symWidth, symHeight,
|
|
sdrTemplate, gFalse,
|
|
refBitmap, refDX, refDY,
|
|
sdrATX, sdrATY);
|
|
//~ do we need to use the bmSize value here (in Huffman mode)?
|
|
} else {
|
|
bitmaps[numInputSyms + i] =
|
|
readTextRegion(huff, gTrue, symWidth, symHeight,
|
|
refAggNum, 0, numInputSyms + i, NULL,
|
|
symCodeLen, bitmaps, 0, 0, 0, 1, 0,
|
|
huffTableF, huffTableH, huffTableK, huffTableO,
|
|
huffTableO, huffTableO, huffTableO, huffTableA,
|
|
sdrTemplate, sdrATX, sdrATY);
|
|
}
|
|
|
|
// non-ref/agg coding
|
|
} else {
|
|
bitmaps[numInputSyms + i] =
|
|
readGenericBitmap(gFalse, symWidth, symHeight,
|
|
sdTemplate, gFalse, gFalse, NULL,
|
|
sdATX, sdATY, 0);
|
|
}
|
|
|
|
++i;
|
|
}
|
|
|
|
// read the collective bitmap
|
|
if (huff && !refAgg) {
|
|
huffDecoder->decodeInt(&bmSize, huffBMSizeTable);
|
|
huffDecoder->reset();
|
|
if (bmSize == 0) {
|
|
collBitmap = new JBIG2Bitmap(0, totalWidth, symHeight);
|
|
bmSize = symHeight * ((totalWidth + 7) >> 3);
|
|
p = collBitmap->getDataPtr();
|
|
for (k = 0; k < (Guint)bmSize; ++k) {
|
|
*p++ = curStr->getChar();
|
|
}
|
|
} else {
|
|
collBitmap = readGenericBitmap(gTrue, totalWidth, symHeight,
|
|
0, gFalse, gFalse, NULL, NULL, NULL,
|
|
bmSize);
|
|
}
|
|
x = 0;
|
|
for (; j < i; ++j) {
|
|
bitmaps[numInputSyms + j] =
|
|
collBitmap->getSlice(x, 0, symWidths[j], symHeight);
|
|
x += symWidths[j];
|
|
}
|
|
delete collBitmap;
|
|
}
|
|
}
|
|
|
|
// create the symbol dict object
|
|
symbolDict = new JBIG2SymbolDict(segNum, numExSyms);
|
|
|
|
// exported symbol list
|
|
i = j = 0;
|
|
ex = gFalse;
|
|
while (i < numInputSyms + numNewSyms) {
|
|
if (huff) {
|
|
huffDecoder->decodeInt(&run, huffTableA);
|
|
} else {
|
|
arithDecoder->decodeInt(&run, iaexStats);
|
|
}
|
|
if (ex) {
|
|
for (cnt = 0; cnt < run; ++cnt) {
|
|
symbolDict->setBitmap(j++, bitmaps[i++]->copy());
|
|
}
|
|
} else {
|
|
i += run;
|
|
}
|
|
ex = !ex;
|
|
}
|
|
|
|
for (i = 0; i < numNewSyms; ++i) {
|
|
delete bitmaps[numInputSyms + i];
|
|
}
|
|
gfree(bitmaps);
|
|
if (symWidths) {
|
|
gfree(symWidths);
|
|
}
|
|
|
|
// save the arithmetic decoder stats
|
|
if (!huff && contextRetained) {
|
|
symbolDict->setGenericRegionStats(genericRegionStats->copy());
|
|
if (refAgg) {
|
|
symbolDict->setRefinementRegionStats(refinementRegionStats->copy());
|
|
}
|
|
}
|
|
|
|
// store the new symbol dict
|
|
segments->append(symbolDict);
|
|
|
|
return gTrue;
|
|
|
|
syntaxError:
|
|
for (i = 0; i < numNewSyms; ++i) {
|
|
if (bitmaps[numInputSyms + i]) {
|
|
delete bitmaps[numInputSyms + i];
|
|
}
|
|
}
|
|
gfree(bitmaps);
|
|
if (symWidths) {
|
|
gfree(symWidths);
|
|
}
|
|
return gFalse;
|
|
|
|
eofError:
|
|
error(getPos(), "Unexpected EOF in JBIG2 stream");
|
|
return gFalse;
|
|
}
|
|
|
|
void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm,
|
|
GBool lossless, Guint length,
|
|
Guint *refSegs, Guint nRefSegs) {
|
|
JBIG2Bitmap *bitmap;
|
|
JBIG2HuffmanTable runLengthTab[36];
|
|
JBIG2HuffmanTable *symCodeTab;
|
|
JBIG2HuffmanTable *huffFSTable, *huffDSTable, *huffDTTable;
|
|
JBIG2HuffmanTable *huffRDWTable, *huffRDHTable;
|
|
JBIG2HuffmanTable *huffRDXTable, *huffRDYTable, *huffRSizeTable;
|
|
JBIG2Segment *seg;
|
|
GList *codeTables;
|
|
JBIG2SymbolDict *symbolDict;
|
|
JBIG2Bitmap **syms;
|
|
Guint w, h, x, y, segInfoFlags, extCombOp;
|
|
Guint flags, huff, refine, logStrips, refCorner, transposed;
|
|
Guint combOp, defPixel, templ;
|
|
int sOffset;
|
|
Guint huffFlags, huffFS, huffDS, huffDT;
|
|
Guint huffRDW, huffRDH, huffRDX, huffRDY, huffRSize;
|
|
Guint numInstances, numSyms, symCodeLen;
|
|
int atx[2], aty[2];
|
|
Guint i, k, kk;
|
|
int j;
|
|
|
|
// region segment info field
|
|
if (!readULong(&w) || !readULong(&h) ||
|
|
!readULong(&x) || !readULong(&y) ||
|
|
!readUByte(&segInfoFlags)) {
|
|
goto eofError;
|
|
}
|
|
extCombOp = segInfoFlags & 7;
|
|
|
|
// rest of the text region header
|
|
if (!readUWord(&flags)) {
|
|
goto eofError;
|
|
}
|
|
huff = flags & 1;
|
|
refine = (flags >> 1) & 1;
|
|
logStrips = (flags >> 2) & 3;
|
|
refCorner = (flags >> 4) & 3;
|
|
transposed = (flags >> 6) & 1;
|
|
combOp = (flags >> 7) & 3;
|
|
defPixel = (flags >> 9) & 1;
|
|
sOffset = (flags >> 10) & 0x1f;
|
|
if (sOffset & 0x10) {
|
|
sOffset |= -1 - 0x0f;
|
|
}
|
|
templ = (flags >> 15) & 1;
|
|
huffFS = huffDS = huffDT = 0; // make gcc happy
|
|
huffRDW = huffRDH = huffRDX = huffRDY = huffRSize = 0; // make gcc happy
|
|
if (huff) {
|
|
if (!readUWord(&huffFlags)) {
|
|
goto eofError;
|
|
}
|
|
huffFS = huffFlags & 3;
|
|
huffDS = (huffFlags >> 2) & 3;
|
|
huffDT = (huffFlags >> 4) & 3;
|
|
huffRDW = (huffFlags >> 6) & 3;
|
|
huffRDH = (huffFlags >> 8) & 3;
|
|
huffRDX = (huffFlags >> 10) & 3;
|
|
huffRDY = (huffFlags >> 12) & 3;
|
|
huffRSize = (huffFlags >> 14) & 1;
|
|
}
|
|
if (refine && templ == 0) {
|
|
if (!readByte(&atx[0]) || !readByte(&aty[0]) ||
|
|
!readByte(&atx[1]) || !readByte(&aty[1])) {
|
|
goto eofError;
|
|
}
|
|
}
|
|
if (!readULong(&numInstances)) {
|
|
goto eofError;
|
|
}
|
|
|
|
// get symbol dictionaries and tables
|
|
codeTables = new GList();
|
|
numSyms = 0;
|
|
for (i = 0; i < nRefSegs; ++i) {
|
|
if ((seg = findSegment(refSegs[i]))) {
|
|
if (seg->getType() == jbig2SegSymbolDict) {
|
|
numSyms += ((JBIG2SymbolDict *)seg)->getSize();
|
|
} else if (seg->getType() == jbig2SegCodeTable) {
|
|
codeTables->append(seg);
|
|
}
|
|
} else {
|
|
error(getPos(), "Invalid segment reference in JBIG2 text region");
|
|
}
|
|
}
|
|
symCodeLen = 0;
|
|
i = 1;
|
|
while (i < numSyms) {
|
|
++symCodeLen;
|
|
i <<= 1;
|
|
}
|
|
|
|
// get the symbol bitmaps
|
|
syms = (JBIG2Bitmap **)gmallocn(numSyms, sizeof(JBIG2Bitmap *));
|
|
kk = 0;
|
|
for (i = 0; i < nRefSegs; ++i) {
|
|
if ((seg = findSegment(refSegs[i]))) {
|
|
if (seg->getType() == jbig2SegSymbolDict) {
|
|
symbolDict = (JBIG2SymbolDict *)seg;
|
|
for (k = 0; k < symbolDict->getSize(); ++k) {
|
|
syms[kk++] = symbolDict->getBitmap(k);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// get the Huffman tables
|
|
huffFSTable = huffDSTable = huffDTTable = NULL; // make gcc happy
|
|
huffRDWTable = huffRDHTable = NULL; // make gcc happy
|
|
huffRDXTable = huffRDYTable = huffRSizeTable = NULL; // make gcc happy
|
|
i = 0;
|
|
if (huff) {
|
|
if (huffFS == 0) {
|
|
huffFSTable = huffTableF;
|
|
} else if (huffFS == 1) {
|
|
huffFSTable = huffTableG;
|
|
} else {
|
|
huffFSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
|
|
}
|
|
if (huffDS == 0) {
|
|
huffDSTable = huffTableH;
|
|
} else if (huffDS == 1) {
|
|
huffDSTable = huffTableI;
|
|
} else if (huffDS == 2) {
|
|
huffDSTable = huffTableJ;
|
|
} else {
|
|
huffDSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
|
|
}
|
|
if (huffDT == 0) {
|
|
huffDTTable = huffTableK;
|
|
} else if (huffDT == 1) {
|
|
huffDTTable = huffTableL;
|
|
} else if (huffDT == 2) {
|
|
huffDTTable = huffTableM;
|
|
} else {
|
|
huffDTTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
|
|
}
|
|
if (huffRDW == 0) {
|
|
huffRDWTable = huffTableN;
|
|
} else if (huffRDW == 1) {
|
|
huffRDWTable = huffTableO;
|
|
} else {
|
|
huffRDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
|
|
}
|
|
if (huffRDH == 0) {
|
|
huffRDHTable = huffTableN;
|
|
} else if (huffRDH == 1) {
|
|
huffRDHTable = huffTableO;
|
|
} else {
|
|
huffRDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
|
|
}
|
|
if (huffRDX == 0) {
|
|
huffRDXTable = huffTableN;
|
|
} else if (huffRDX == 1) {
|
|
huffRDXTable = huffTableO;
|
|
} else {
|
|
huffRDXTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
|
|
}
|
|
if (huffRDY == 0) {
|
|
huffRDYTable = huffTableN;
|
|
} else if (huffRDY == 1) {
|
|
huffRDYTable = huffTableO;
|
|
} else {
|
|
huffRDYTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
|
|
}
|
|
if (huffRSize == 0) {
|
|
huffRSizeTable = huffTableA;
|
|
} else {
|
|
huffRSizeTable =
|
|
((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
|
|
}
|
|
}
|
|
delete codeTables;
|
|
|
|
// symbol ID Huffman decoding table
|
|
if (huff) {
|
|
huffDecoder->reset();
|
|
for (i = 0; i < 32; ++i) {
|
|
runLengthTab[i].val = i;
|
|
runLengthTab[i].prefixLen = huffDecoder->readBits(4);
|
|
runLengthTab[i].rangeLen = 0;
|
|
}
|
|
runLengthTab[32].val = 0x103;
|
|
runLengthTab[32].prefixLen = huffDecoder->readBits(4);
|
|
runLengthTab[32].rangeLen = 2;
|
|
runLengthTab[33].val = 0x203;
|
|
runLengthTab[33].prefixLen = huffDecoder->readBits(4);
|
|
runLengthTab[33].rangeLen = 3;
|
|
runLengthTab[34].val = 0x20b;
|
|
runLengthTab[34].prefixLen = huffDecoder->readBits(4);
|
|
runLengthTab[34].rangeLen = 7;
|
|
runLengthTab[35].prefixLen = 0;
|
|
runLengthTab[35].rangeLen = jbig2HuffmanEOT;
|
|
huffDecoder->buildTable(runLengthTab, 35);
|
|
symCodeTab = (JBIG2HuffmanTable *)gmallocn(numSyms + 1,
|
|
sizeof(JBIG2HuffmanTable));
|
|
for (i = 0; i < numSyms; ++i) {
|
|
symCodeTab[i].val = i;
|
|
symCodeTab[i].rangeLen = 0;
|
|
}
|
|
i = 0;
|
|
while (i < numSyms) {
|
|
huffDecoder->decodeInt(&j, runLengthTab);
|
|
if (j > 0x200) {
|
|
for (j -= 0x200; j && i < numSyms; --j) {
|
|
symCodeTab[i++].prefixLen = 0;
|
|
}
|
|
} else if (j > 0x100) {
|
|
for (j -= 0x100; j && i < numSyms; --j) {
|
|
symCodeTab[i].prefixLen = symCodeTab[i-1].prefixLen;
|
|
++i;
|
|
}
|
|
} else {
|
|
symCodeTab[i++].prefixLen = j;
|
|
}
|
|
}
|
|
symCodeTab[numSyms].prefixLen = 0;
|
|
symCodeTab[numSyms].rangeLen = jbig2HuffmanEOT;
|
|
huffDecoder->buildTable(symCodeTab, numSyms);
|
|
huffDecoder->reset();
|
|
|
|
// set up the arithmetic decoder
|
|
} else {
|
|
symCodeTab = NULL;
|
|
resetIntStats(symCodeLen);
|
|
arithDecoder->start();
|
|
}
|
|
if (refine) {
|
|
resetRefinementStats(templ, NULL);
|
|
}
|
|
|
|
bitmap = readTextRegion(huff, refine, w, h, numInstances,
|
|
logStrips, numSyms, symCodeTab, symCodeLen, syms,
|
|
defPixel, combOp, transposed, refCorner, sOffset,
|
|
huffFSTable, huffDSTable, huffDTTable,
|
|
huffRDWTable, huffRDHTable,
|
|
huffRDXTable, huffRDYTable, huffRSizeTable,
|
|
templ, atx, aty);
|
|
|
|
gfree(syms);
|
|
|
|
// combine the region bitmap into the page bitmap
|
|
if (imm) {
|
|
if (pageH == 0xffffffff && y + h > curPageH) {
|
|
pageBitmap->expand(y + h, pageDefPixel);
|
|
}
|
|
pageBitmap->combine(bitmap, x, y, extCombOp);
|
|
delete bitmap;
|
|
|
|
// store the region bitmap
|
|
} else {
|
|
bitmap->setSegNum(segNum);
|
|
segments->append(bitmap);
|
|
}
|
|
|
|
// clean up the Huffman decoder
|
|
if (huff) {
|
|
gfree(symCodeTab);
|
|
}
|
|
|
|
return;
|
|
|
|
eofError:
|
|
error(getPos(), "Unexpected EOF in JBIG2 stream");
|
|
}
|
|
|
|
JBIG2Bitmap *JBIG2Stream::readTextRegion(GBool huff, GBool refine,
|
|
int w, int h,
|
|
Guint numInstances,
|
|
Guint logStrips,
|
|
int numSyms,
|
|
JBIG2HuffmanTable *symCodeTab,
|
|
Guint symCodeLen,
|
|
JBIG2Bitmap **syms,
|
|
Guint defPixel, Guint combOp,
|
|
Guint transposed, Guint refCorner,
|
|
int sOffset,
|
|
JBIG2HuffmanTable *huffFSTable,
|
|
JBIG2HuffmanTable *huffDSTable,
|
|
JBIG2HuffmanTable *huffDTTable,
|
|
JBIG2HuffmanTable *huffRDWTable,
|
|
JBIG2HuffmanTable *huffRDHTable,
|
|
JBIG2HuffmanTable *huffRDXTable,
|
|
JBIG2HuffmanTable *huffRDYTable,
|
|
JBIG2HuffmanTable *huffRSizeTable,
|
|
Guint templ,
|
|
int *atx, int *aty) {
|
|
JBIG2Bitmap *bitmap;
|
|
JBIG2Bitmap *symbolBitmap;
|
|
Guint strips;
|
|
int t, dt, tt, s, ds, sFirst, j;
|
|
int rdw, rdh, rdx, rdy, ri, refDX, refDY, bmSize;
|
|
Guint symID, inst, bw, bh;
|
|
|
|
strips = 1 << logStrips;
|
|
|
|
// allocate the bitmap
|
|
bitmap = new JBIG2Bitmap(0, w, h);
|
|
if (defPixel) {
|
|
bitmap->clearToOne();
|
|
} else {
|
|
bitmap->clearToZero();
|
|
}
|
|
|
|
// decode initial T value
|
|
if (huff) {
|
|
huffDecoder->decodeInt(&t, huffDTTable);
|
|
} else {
|
|
arithDecoder->decodeInt(&t, iadtStats);
|
|
}
|
|
t *= -(int)strips;
|
|
|
|
inst = 0;
|
|
sFirst = 0;
|
|
while (inst < numInstances) {
|
|
|
|
// decode delta-T
|
|
if (huff) {
|
|
huffDecoder->decodeInt(&dt, huffDTTable);
|
|
} else {
|
|
arithDecoder->decodeInt(&dt, iadtStats);
|
|
}
|
|
t += dt * strips;
|
|
|
|
// first S value
|
|
if (huff) {
|
|
huffDecoder->decodeInt(&ds, huffFSTable);
|
|
} else {
|
|
arithDecoder->decodeInt(&ds, iafsStats);
|
|
}
|
|
sFirst += ds;
|
|
s = sFirst;
|
|
|
|
// read the instances
|
|
while (1) {
|
|
|
|
// T value
|
|
if (strips == 1) {
|
|
dt = 0;
|
|
} else if (huff) {
|
|
dt = huffDecoder->readBits(logStrips);
|
|
} else {
|
|
arithDecoder->decodeInt(&dt, iaitStats);
|
|
}
|
|
tt = t + dt;
|
|
|
|
// symbol ID
|
|
if (huff) {
|
|
if (symCodeTab) {
|
|
huffDecoder->decodeInt(&j, symCodeTab);
|
|
symID = (Guint)j;
|
|
} else {
|
|
symID = huffDecoder->readBits(symCodeLen);
|
|
}
|
|
} else {
|
|
symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);
|
|
}
|
|
|
|
if (symID >= (Guint)numSyms) {
|
|
error(getPos(), "Invalid symbol number in JBIG2 text region");
|
|
} else {
|
|
|
|
// get the symbol bitmap
|
|
symbolBitmap = NULL;
|
|
if (refine) {
|
|
if (huff) {
|
|
ri = (int)huffDecoder->readBit();
|
|
} else {
|
|
arithDecoder->decodeInt(&ri, iariStats);
|
|
}
|
|
} else {
|
|
ri = 0;
|
|
}
|
|
if (ri) {
|
|
if (huff) {
|
|
huffDecoder->decodeInt(&rdw, huffRDWTable);
|
|
huffDecoder->decodeInt(&rdh, huffRDHTable);
|
|
huffDecoder->decodeInt(&rdx, huffRDXTable);
|
|
huffDecoder->decodeInt(&rdy, huffRDYTable);
|
|
huffDecoder->decodeInt(&bmSize, huffRSizeTable);
|
|
huffDecoder->reset();
|
|
arithDecoder->start();
|
|
} else {
|
|
arithDecoder->decodeInt(&rdw, iardwStats);
|
|
arithDecoder->decodeInt(&rdh, iardhStats);
|
|
arithDecoder->decodeInt(&rdx, iardxStats);
|
|
arithDecoder->decodeInt(&rdy, iardyStats);
|
|
}
|
|
refDX = ((rdw >= 0) ? rdw : rdw - 1) / 2 + rdx;
|
|
refDY = ((rdh >= 0) ? rdh : rdh - 1) / 2 + rdy;
|
|
|
|
symbolBitmap =
|
|
readGenericRefinementRegion(rdw + syms[symID]->getWidth(),
|
|
rdh + syms[symID]->getHeight(),
|
|
templ, gFalse, syms[symID],
|
|
refDX, refDY, atx, aty);
|
|
//~ do we need to use the bmSize value here (in Huffman mode)?
|
|
} else {
|
|
symbolBitmap = syms[symID];
|
|
}
|
|
|
|
// combine the symbol bitmap into the region bitmap
|
|
//~ something is wrong here - refCorner shouldn't degenerate into
|
|
//~ two cases
|
|
bw = symbolBitmap->getWidth() - 1;
|
|
bh = symbolBitmap->getHeight() - 1;
|
|
if (transposed) {
|
|
switch (refCorner) {
|
|
case 0: // bottom left
|
|
bitmap->combine(symbolBitmap, tt, s, combOp);
|
|
break;
|
|
case 1: // top left
|
|
bitmap->combine(symbolBitmap, tt, s, combOp);
|
|
break;
|
|
case 2: // bottom right
|
|
bitmap->combine(symbolBitmap, tt - bw, s, combOp);
|
|
break;
|
|
case 3: // top right
|
|
bitmap->combine(symbolBitmap, tt - bw, s, combOp);
|
|
break;
|
|
}
|
|
s += bh;
|
|
} else {
|
|
switch (refCorner) {
|
|
case 0: // bottom left
|
|
bitmap->combine(symbolBitmap, s, tt - bh, combOp);
|
|
break;
|
|
case 1: // top left
|
|
bitmap->combine(symbolBitmap, s, tt, combOp);
|
|
break;
|
|
case 2: // bottom right
|
|
bitmap->combine(symbolBitmap, s, tt - bh, combOp);
|
|
break;
|
|
case 3: // top right
|
|
bitmap->combine(symbolBitmap, s, tt, combOp);
|
|
break;
|
|
}
|
|
s += bw;
|
|
}
|
|
if (ri) {
|
|
delete symbolBitmap;
|
|
}
|
|
}
|
|
|
|
// next instance
|
|
++inst;
|
|
|
|
// next S value
|
|
if (huff) {
|
|
if (!huffDecoder->decodeInt(&ds, huffDSTable)) {
|
|
break;
|
|
}
|
|
} else {
|
|
if (!arithDecoder->decodeInt(&ds, iadsStats)) {
|
|
break;
|
|
}
|
|
}
|
|
s += sOffset + ds;
|
|
}
|
|
}
|
|
|
|
return bitmap;
|
|
}
|
|
|
|
void JBIG2Stream::readPatternDictSeg(Guint segNum, Guint length) {
|
|
JBIG2PatternDict *patternDict;
|
|
JBIG2Bitmap *bitmap;
|
|
Guint flags, patternW, patternH, grayMax, templ, mmr;
|
|
int atx[4], aty[4];
|
|
Guint i, x;
|
|
|
|
// halftone dictionary flags, pattern width and height, max gray value
|
|
if (!readUByte(&flags) ||
|
|
!readUByte(&patternW) ||
|
|
!readUByte(&patternH) ||
|
|
!readULong(&grayMax)) {
|
|
goto eofError;
|
|
}
|
|
templ = (flags >> 1) & 3;
|
|
mmr = flags & 1;
|
|
|
|
// set up the arithmetic decoder
|
|
if (!mmr) {
|
|
resetGenericStats(templ, NULL);
|
|
arithDecoder->start();
|
|
}
|
|
|
|
// read the bitmap
|
|
atx[0] = -(int)patternW; aty[0] = 0;
|
|
atx[1] = -3; aty[1] = -1;
|
|
atx[2] = 2; aty[2] = -2;
|
|
atx[3] = -2; aty[3] = -2;
|
|
bitmap = readGenericBitmap(mmr, (grayMax + 1) * patternW, patternH,
|
|
templ, gFalse, gFalse, NULL,
|
|
atx, aty, length - 7);
|
|
|
|
// create the pattern dict object
|
|
patternDict = new JBIG2PatternDict(segNum, grayMax + 1);
|
|
|
|
// split up the bitmap
|
|
x = 0;
|
|
for (i = 0; i <= grayMax; ++i) {
|
|
patternDict->setBitmap(i, bitmap->getSlice(x, 0, patternW, patternH));
|
|
x += patternW;
|
|
}
|
|
|
|
// free memory
|
|
delete bitmap;
|
|
|
|
// store the new pattern dict
|
|
segments->append(patternDict);
|
|
|
|
return;
|
|
|
|
eofError:
|
|
error(getPos(), "Unexpected EOF in JBIG2 stream");
|
|
}
|
|
|
|
void JBIG2Stream::readHalftoneRegionSeg(Guint segNum, GBool imm,
|
|
GBool lossless, Guint length,
|
|
Guint *refSegs, Guint nRefSegs) {
|
|
JBIG2Bitmap *bitmap;
|
|
JBIG2Segment *seg;
|
|
JBIG2PatternDict *patternDict;
|
|
JBIG2Bitmap *skipBitmap;
|
|
Guint *grayImg;
|
|
JBIG2Bitmap *grayBitmap;
|
|
JBIG2Bitmap *patternBitmap;
|
|
Guint w, h, x, y, segInfoFlags, extCombOp;
|
|
Guint flags, mmr, templ, enableSkip, combOp;
|
|
Guint gridW, gridH, stepX, stepY, patW, patH;
|
|
int atx[4], aty[4];
|
|
int gridX, gridY, xx, yy, bit, j;
|
|
Guint bpp, m, n, i;
|
|
|
|
// region segment info field
|
|
if (!readULong(&w) || !readULong(&h) ||
|
|
!readULong(&x) || !readULong(&y) ||
|
|
!readUByte(&segInfoFlags)) {
|
|
goto eofError;
|
|
}
|
|
extCombOp = segInfoFlags & 7;
|
|
|
|
// rest of the halftone region header
|
|
if (!readUByte(&flags)) {
|
|
goto eofError;
|
|
}
|
|
mmr = flags & 1;
|
|
templ = (flags >> 1) & 3;
|
|
enableSkip = (flags >> 3) & 1;
|
|
combOp = (flags >> 4) & 7;
|
|
if (!readULong(&gridW) || !readULong(&gridH) ||
|
|
!readLong(&gridX) || !readLong(&gridY) ||
|
|
!readUWord(&stepX) || !readUWord(&stepY)) {
|
|
goto eofError;
|
|
}
|
|
if (w == 0 || h == 0 || w >= INT_MAX / h) {
|
|
error(getPos(), "Bad bitmap size in JBIG2 halftone segment");
|
|
return;
|
|
}
|
|
if (gridH == 0 || gridW >= INT_MAX / gridH) {
|
|
error(getPos(), "Bad grid size in JBIG2 halftone segment");
|
|
return;
|
|
}
|
|
|
|
// get pattern dictionary
|
|
if (nRefSegs != 1) {
|
|
error(getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");
|
|
return;
|
|
}
|
|
seg = findSegment(refSegs[0]);
|
|
if (seg->getType() != jbig2SegPatternDict) {
|
|
error(getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");
|
|
return;
|
|
}
|
|
patternDict = (JBIG2PatternDict *)seg;
|
|
bpp = 0;
|
|
i = 1;
|
|
while (i < patternDict->getSize()) {
|
|
++bpp;
|
|
i <<= 1;
|
|
}
|
|
patW = patternDict->getBitmap(0)->getWidth();
|
|
patH = patternDict->getBitmap(0)->getHeight();
|
|
|
|
// set up the arithmetic decoder
|
|
if (!mmr) {
|
|
resetGenericStats(templ, NULL);
|
|
arithDecoder->start();
|
|
}
|
|
|
|
// allocate the bitmap
|
|
bitmap = new JBIG2Bitmap(segNum, w, h);
|
|
if (flags & 0x80) { // HDEFPIXEL
|
|
bitmap->clearToOne();
|
|
} else {
|
|
bitmap->clearToZero();
|
|
}
|
|
|
|
// compute the skip bitmap
|
|
skipBitmap = NULL;
|
|
if (enableSkip) {
|
|
skipBitmap = new JBIG2Bitmap(0, gridW, gridH);
|
|
skipBitmap->clearToZero();
|
|
for (m = 0; m < gridH; ++m) {
|
|
for (n = 0; n < gridW; ++n) {
|
|
xx = gridX + m * stepY + n * stepX;
|
|
yy = gridY + m * stepX - n * stepY;
|
|
if (((xx + (int)patW) >> 8) <= 0 || (xx >> 8) >= (int)w ||
|
|
((yy + (int)patH) >> 8) <= 0 || (yy >> 8) >= (int)h) {
|
|
skipBitmap->setPixel(n, m);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// read the gray-scale image
|
|
grayImg = (Guint *)gmallocn(gridW * gridH, sizeof(Guint));
|
|
memset(grayImg, 0, gridW * gridH * sizeof(Guint));
|
|
atx[0] = templ <= 1 ? 3 : 2; aty[0] = -1;
|
|
atx[1] = -3; aty[1] = -1;
|
|
atx[2] = 2; aty[2] = -2;
|
|
atx[3] = -2; aty[3] = -2;
|
|
for (j = bpp - 1; j >= 0; --j) {
|
|
grayBitmap = readGenericBitmap(mmr, gridW, gridH, templ, gFalse,
|
|
enableSkip, skipBitmap, atx, aty, -1);
|
|
i = 0;
|
|
for (m = 0; m < gridH; ++m) {
|
|
for (n = 0; n < gridW; ++n) {
|
|
bit = grayBitmap->getPixel(n, m) ^ (grayImg[i] & 1);
|
|
grayImg[i] = (grayImg[i] << 1) | bit;
|
|
++i;
|
|
}
|
|
}
|
|
delete grayBitmap;
|
|
}
|
|
|
|
// decode the image
|
|
i = 0;
|
|
for (m = 0; m < gridH; ++m) {
|
|
xx = gridX + m * stepY;
|
|
yy = gridY + m * stepX;
|
|
for (n = 0; n < gridW; ++n) {
|
|
if (!(enableSkip && skipBitmap->getPixel(n, m))) {
|
|
patternBitmap = patternDict->getBitmap(grayImg[i]);
|
|
bitmap->combine(patternBitmap, xx >> 8, yy >> 8, combOp);
|
|
}
|
|
xx += stepX;
|
|
yy -= stepY;
|
|
++i;
|
|
}
|
|
}
|
|
|
|
gfree(grayImg);
|
|
if (skipBitmap) {
|
|
delete skipBitmap;
|
|
}
|
|
|
|
// combine the region bitmap into the page bitmap
|
|
if (imm) {
|
|
if (pageH == 0xffffffff && y + h > curPageH) {
|
|
pageBitmap->expand(y + h, pageDefPixel);
|
|
}
|
|
pageBitmap->combine(bitmap, x, y, extCombOp);
|
|
delete bitmap;
|
|
|
|
// store the region bitmap
|
|
} else {
|
|
segments->append(bitmap);
|
|
}
|
|
|
|
return;
|
|
|
|
eofError:
|
|
error(getPos(), "Unexpected EOF in JBIG2 stream");
|
|
}
|
|
|
|
void JBIG2Stream::readGenericRegionSeg(Guint segNum, GBool imm,
|
|
GBool lossless, Guint length) {
|
|
JBIG2Bitmap *bitmap;
|
|
Guint w, h, x, y, segInfoFlags, extCombOp;
|
|
Guint flags, mmr, templ, tpgdOn;
|
|
int atx[4], aty[4];
|
|
|
|
// region segment info field
|
|
if (!readULong(&w) || !readULong(&h) ||
|
|
!readULong(&x) || !readULong(&y) ||
|
|
!readUByte(&segInfoFlags)) {
|
|
goto eofError;
|
|
}
|
|
extCombOp = segInfoFlags & 7;
|
|
|
|
// rest of the generic region segment header
|
|
if (!readUByte(&flags)) {
|
|
goto eofError;
|
|
}
|
|
mmr = flags & 1;
|
|
templ = (flags >> 1) & 3;
|
|
tpgdOn = (flags >> 3) & 1;
|
|
|
|
// AT flags
|
|
if (!mmr) {
|
|
if (templ == 0) {
|
|
if (!readByte(&atx[0]) ||
|
|
!readByte(&aty[0]) ||
|
|
!readByte(&atx[1]) ||
|
|
!readByte(&aty[1]) ||
|
|
!readByte(&atx[2]) ||
|
|
!readByte(&aty[2]) ||
|
|
!readByte(&atx[3]) ||
|
|
!readByte(&aty[3])) {
|
|
goto eofError;
|
|
}
|
|
} else {
|
|
if (!readByte(&atx[0]) ||
|
|
!readByte(&aty[0])) {
|
|
goto eofError;
|
|
}
|
|
}
|
|
}
|
|
|
|
// set up the arithmetic decoder
|
|
if (!mmr) {
|
|
resetGenericStats(templ, NULL);
|
|
arithDecoder->start();
|
|
}
|
|
|
|
// read the bitmap
|
|
bitmap = readGenericBitmap(mmr, w, h, templ, tpgdOn, gFalse,
|
|
NULL, atx, aty, mmr ? 0 : length - 18);
|
|
|
|
// combine the region bitmap into the page bitmap
|
|
if (imm) {
|
|
if (pageH == 0xffffffff && y + h > curPageH) {
|
|
pageBitmap->expand(y + h, pageDefPixel);
|
|
}
|
|
pageBitmap->combine(bitmap, x, y, extCombOp);
|
|
delete bitmap;
|
|
|
|
// store the region bitmap
|
|
} else {
|
|
bitmap->setSegNum(segNum);
|
|
segments->append(bitmap);
|
|
}
|
|
|
|
return;
|
|
|
|
eofError:
|
|
error(getPos(), "Unexpected EOF in JBIG2 stream");
|
|
}
|
|
|
|
JBIG2Bitmap *JBIG2Stream::readGenericBitmap(GBool mmr, int w, int h,
|
|
int templ, GBool tpgdOn,
|
|
GBool useSkip, JBIG2Bitmap *skip,
|
|
int *atx, int *aty,
|
|
int mmrDataLength) {
|
|
JBIG2Bitmap *bitmap;
|
|
GBool ltp;
|
|
Guint ltpCX, cx, cx0, cx1, cx2;
|
|
JBIG2BitmapPtr cxPtr0, cxPtr1;
|
|
JBIG2BitmapPtr atPtr0, atPtr1, atPtr2, atPtr3;
|
|
int *refLine, *codingLine;
|
|
int code1, code2, code3;
|
|
int x, y, a0, pix, i, refI, codingI;
|
|
|
|
bitmap = new JBIG2Bitmap(0, w, h);
|
|
bitmap->clearToZero();
|
|
|
|
//----- MMR decode
|
|
|
|
if (mmr) {
|
|
|
|
mmrDecoder->reset();
|
|
refLine = (int *)gmallocn(w + 2, sizeof(int));
|
|
codingLine = (int *)gmallocn(w + 2, sizeof(int));
|
|
codingLine[0] = codingLine[1] = w;
|
|
|
|
for (y = 0; y < h; ++y) {
|
|
|
|
// copy coding line to ref line
|
|
for (i = 0; codingLine[i] < w; ++i) {
|
|
refLine[i] = codingLine[i];
|
|
}
|
|
refLine[i] = refLine[i + 1] = w;
|
|
|
|
// decode a line
|
|
refI = 0; // b1 = refLine[refI]
|
|
codingI = 0; // a1 = codingLine[codingI]
|
|
a0 = 0;
|
|
do {
|
|
code1 = mmrDecoder->get2DCode();
|
|
switch (code1) {
|
|
case twoDimPass:
|
|
if (refLine[refI] < w) {
|
|
a0 = refLine[refI + 1];
|
|
refI += 2;
|
|
}
|
|
break;
|
|
case twoDimHoriz:
|
|
if (codingI & 1) {
|
|
code1 = 0;
|
|
do {
|
|
code1 += code3 = mmrDecoder->getBlackCode();
|
|
} while (code3 >= 64);
|
|
code2 = 0;
|
|
do {
|
|
code2 += code3 = mmrDecoder->getWhiteCode();
|
|
} while (code3 >= 64);
|
|
} else {
|
|
code1 = 0;
|
|
do {
|
|
code1 += code3 = mmrDecoder->getWhiteCode();
|
|
} while (code3 >= 64);
|
|
code2 = 0;
|
|
do {
|
|
code2 += code3 = mmrDecoder->getBlackCode();
|
|
} while (code3 >= 64);
|
|
}
|
|
if (code1 > 0 || code2 > 0) {
|
|
a0 = codingLine[codingI++] = a0 + code1;
|
|
a0 = codingLine[codingI++] = a0 + code2;
|
|
while (refLine[refI] <= a0 && refLine[refI] < w) {
|
|
refI += 2;
|
|
}
|
|
}
|
|
break;
|
|
case twoDimVert0:
|
|
a0 = codingLine[codingI++] = refLine[refI];
|
|
if (refLine[refI] < w) {
|
|
++refI;
|
|
}
|
|
break;
|
|
case twoDimVertR1:
|
|
a0 = codingLine[codingI++] = refLine[refI] + 1;
|
|
if (refLine[refI] < w) {
|
|
++refI;
|
|
while (refLine[refI] <= a0 && refLine[refI] < w) {
|
|
refI += 2;
|
|
}
|
|
}
|
|
break;
|
|
case twoDimVertR2:
|
|
a0 = codingLine[codingI++] = refLine[refI] + 2;
|
|
if (refLine[refI] < w) {
|
|
++refI;
|
|
while (refLine[refI] <= a0 && refLine[refI] < w) {
|
|
refI += 2;
|
|
}
|
|
}
|
|
break;
|
|
case twoDimVertR3:
|
|
a0 = codingLine[codingI++] = refLine[refI] + 3;
|
|
if (refLine[refI] < w) {
|
|
++refI;
|
|
while (refLine[refI] <= a0 && refLine[refI] < w) {
|
|
refI += 2;
|
|
}
|
|
}
|
|
break;
|
|
case twoDimVertL1:
|
|
a0 = codingLine[codingI++] = refLine[refI] - 1;
|
|
if (refI > 0) {
|
|
--refI;
|
|
} else {
|
|
++refI;
|
|
}
|
|
while (refLine[refI] <= a0 && refLine[refI] < w) {
|
|
refI += 2;
|
|
}
|
|
break;
|
|
case twoDimVertL2:
|
|
a0 = codingLine[codingI++] = refLine[refI] - 2;
|
|
if (refI > 0) {
|
|
--refI;
|
|
} else {
|
|
++refI;
|
|
}
|
|
while (refLine[refI] <= a0 && refLine[refI] < w) {
|
|
refI += 2;
|
|
}
|
|
break;
|
|
case twoDimVertL3:
|
|
a0 = codingLine[codingI++] = refLine[refI] - 3;
|
|
if (refI > 0) {
|
|
--refI;
|
|
} else {
|
|
++refI;
|
|
}
|
|
while (refLine[refI] <= a0 && refLine[refI] < w) {
|
|
refI += 2;
|
|
}
|
|
break;
|
|
default:
|
|
error(getPos(), "Illegal code in JBIG2 MMR bitmap data");
|
|
break;
|
|
}
|
|
} while (a0 < w);
|
|
codingLine[codingI++] = w;
|
|
|
|
// convert the run lengths to a bitmap line
|
|
i = 0;
|
|
while (codingLine[i] < w) {
|
|
for (x = codingLine[i]; x < codingLine[i+1]; ++x) {
|
|
bitmap->setPixel(x, y);
|
|
}
|
|
i += 2;
|
|
}
|
|
}
|
|
|
|
if (mmrDataLength >= 0) {
|
|
mmrDecoder->skipTo(mmrDataLength);
|
|
} else {
|
|
if (mmrDecoder->get24Bits() != 0x001001) {
|
|
error(getPos(), "Missing EOFB in JBIG2 MMR bitmap data");
|
|
}
|
|
}
|
|
|
|
gfree(refLine);
|
|
gfree(codingLine);
|
|
|
|
//----- arithmetic decode
|
|
|
|
} else {
|
|
// set up the typical row context
|
|
ltpCX = 0; // make gcc happy
|
|
if (tpgdOn) {
|
|
switch (templ) {
|
|
case 0:
|
|
ltpCX = 0x3953; // 001 11001 0101 0011
|
|
break;
|
|
case 1:
|
|
ltpCX = 0x079a; // 0011 11001 101 0
|
|
break;
|
|
case 2:
|
|
ltpCX = 0x0e3; // 001 1100 01 1
|
|
break;
|
|
case 3:
|
|
ltpCX = 0x18a; // 01100 0101 1
|
|
break;
|
|
}
|
|
}
|
|
|
|
ltp = 0;
|
|
cx = cx0 = cx1 = cx2 = 0; // make gcc happy
|
|
for (y = 0; y < h; ++y) {
|
|
|
|
// check for a "typical" (duplicate) row
|
|
if (tpgdOn) {
|
|
if (arithDecoder->decodeBit(ltpCX, genericRegionStats)) {
|
|
ltp = !ltp;
|
|
}
|
|
if (ltp) {
|
|
bitmap->duplicateRow(y, y-1);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch (templ) {
|
|
case 0:
|
|
|
|
// set up the context
|
|
bitmap->getPixelPtr(0, y-2, &cxPtr0);
|
|
cx0 = bitmap->nextPixel(&cxPtr0);
|
|
cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
|
|
bitmap->getPixelPtr(0, y-1, &cxPtr1);
|
|
cx1 = bitmap->nextPixel(&cxPtr1);
|
|
cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
|
|
cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
|
|
cx2 = 0;
|
|
bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);
|
|
bitmap->getPixelPtr(atx[1], y + aty[1], &atPtr1);
|
|
bitmap->getPixelPtr(atx[2], y + aty[2], &atPtr2);
|
|
bitmap->getPixelPtr(atx[3], y + aty[3], &atPtr3);
|
|
|
|
// decode the row
|
|
for (x = 0; x < w; ++x) {
|
|
|
|
// build the context
|
|
cx = (cx0 << 13) | (cx1 << 8) | (cx2 << 4) |
|
|
(bitmap->nextPixel(&atPtr0) << 3) |
|
|
(bitmap->nextPixel(&atPtr1) << 2) |
|
|
(bitmap->nextPixel(&atPtr2) << 1) |
|
|
bitmap->nextPixel(&atPtr3);
|
|
|
|
// check for a skipped pixel
|
|
if (useSkip && skip->getPixel(x, y)) {
|
|
pix = 0;
|
|
|
|
// decode the pixel
|
|
} else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
|
|
bitmap->setPixel(x, y);
|
|
}
|
|
|
|
// update the context
|
|
cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x07;
|
|
cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;
|
|
cx2 = ((cx2 << 1) | pix) & 0x0f;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
|
|
// set up the context
|
|
bitmap->getPixelPtr(0, y-2, &cxPtr0);
|
|
cx0 = bitmap->nextPixel(&cxPtr0);
|
|
cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
|
|
cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
|
|
bitmap->getPixelPtr(0, y-1, &cxPtr1);
|
|
cx1 = bitmap->nextPixel(&cxPtr1);
|
|
cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
|
|
cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
|
|
cx2 = 0;
|
|
bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);
|
|
|
|
// decode the row
|
|
for (x = 0; x < w; ++x) {
|
|
|
|
// build the context
|
|
cx = (cx0 << 9) | (cx1 << 4) | (cx2 << 1) |
|
|
bitmap->nextPixel(&atPtr0);
|
|
|
|
// check for a skipped pixel
|
|
if (useSkip && skip->getPixel(x, y)) {
|
|
pix = 0;
|
|
|
|
// decode the pixel
|
|
} else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
|
|
bitmap->setPixel(x, y);
|
|
}
|
|
|
|
// update the context
|
|
cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x0f;
|
|
cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;
|
|
cx2 = ((cx2 << 1) | pix) & 0x07;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
|
|
// set up the context
|
|
bitmap->getPixelPtr(0, y-2, &cxPtr0);
|
|
cx0 = bitmap->nextPixel(&cxPtr0);
|
|
cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
|
|
bitmap->getPixelPtr(0, y-1, &cxPtr1);
|
|
cx1 = bitmap->nextPixel(&cxPtr1);
|
|
cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
|
|
cx2 = 0;
|
|
bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);
|
|
|
|
// decode the row
|
|
for (x = 0; x < w; ++x) {
|
|
|
|
// build the context
|
|
cx = (cx0 << 7) | (cx1 << 3) | (cx2 << 1) |
|
|
bitmap->nextPixel(&atPtr0);
|
|
|
|
// check for a skipped pixel
|
|
if (useSkip && skip->getPixel(x, y)) {
|
|
pix = 0;
|
|
|
|
// decode the pixel
|
|
} else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
|
|
bitmap->setPixel(x, y);
|
|
}
|
|
|
|
// update the context
|
|
cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x07;
|
|
cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x0f;
|
|
cx2 = ((cx2 << 1) | pix) & 0x03;
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
|
|
// set up the context
|
|
bitmap->getPixelPtr(0, y-1, &cxPtr1);
|
|
cx1 = bitmap->nextPixel(&cxPtr1);
|
|
cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
|
|
cx2 = 0;
|
|
bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);
|
|
|
|
// decode the row
|
|
for (x = 0; x < w; ++x) {
|
|
|
|
// build the context
|
|
cx = (cx1 << 5) | (cx2 << 1) |
|
|
bitmap->nextPixel(&atPtr0);
|
|
|
|
// check for a skipped pixel
|
|
if (useSkip && skip->getPixel(x, y)) {
|
|
pix = 0;
|
|
|
|
// decode the pixel
|
|
} else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
|
|
bitmap->setPixel(x, y);
|
|
}
|
|
|
|
// update the context
|
|
cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;
|
|
cx2 = ((cx2 << 1) | pix) & 0x0f;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bitmap;
|
|
}
|
|
|
|
void JBIG2Stream::readGenericRefinementRegionSeg(Guint segNum, GBool imm,
|
|
GBool lossless, Guint length,
|
|
Guint *refSegs,
|
|
Guint nRefSegs) {
|
|
JBIG2Bitmap *bitmap, *refBitmap;
|
|
Guint w, h, x, y, segInfoFlags, extCombOp;
|
|
Guint flags, templ, tpgrOn;
|
|
int atx[2], aty[2];
|
|
JBIG2Segment *seg;
|
|
|
|
// region segment info field
|
|
if (!readULong(&w) || !readULong(&h) ||
|
|
!readULong(&x) || !readULong(&y) ||
|
|
!readUByte(&segInfoFlags)) {
|
|
goto eofError;
|
|
}
|
|
extCombOp = segInfoFlags & 7;
|
|
|
|
// rest of the generic refinement region segment header
|
|
if (!readUByte(&flags)) {
|
|
goto eofError;
|
|
}
|
|
templ = flags & 1;
|
|
tpgrOn = (flags >> 1) & 1;
|
|
|
|
// AT flags
|
|
if (!templ) {
|
|
if (!readByte(&atx[0]) || !readByte(&aty[0]) ||
|
|
!readByte(&atx[1]) || !readByte(&aty[1])) {
|
|
goto eofError;
|
|
}
|
|
}
|
|
|
|
// resize the page bitmap if needed
|
|
if (nRefSegs == 0 || imm) {
|
|
if (pageH == 0xffffffff && y + h > curPageH) {
|
|
pageBitmap->expand(y + h, pageDefPixel);
|
|
}
|
|
}
|
|
|
|
// get referenced bitmap
|
|
if (nRefSegs > 1) {
|
|
error(getPos(), "Bad reference in JBIG2 generic refinement segment");
|
|
return;
|
|
}
|
|
if (nRefSegs == 1) {
|
|
seg = findSegment(refSegs[0]);
|
|
if (seg->getType() != jbig2SegBitmap) {
|
|
error(getPos(), "Bad bitmap reference in JBIG2 generic refinement segment");
|
|
return;
|
|
}
|
|
refBitmap = (JBIG2Bitmap *)seg;
|
|
} else {
|
|
refBitmap = pageBitmap->getSlice(x, y, w, h);
|
|
}
|
|
|
|
// set up the arithmetic decoder
|
|
resetRefinementStats(templ, NULL);
|
|
arithDecoder->start();
|
|
|
|
// read
|
|
bitmap = readGenericRefinementRegion(w, h, templ, tpgrOn,
|
|
refBitmap, 0, 0, atx, aty);
|
|
|
|
// combine the region bitmap into the page bitmap
|
|
if (imm) {
|
|
pageBitmap->combine(bitmap, x, y, extCombOp);
|
|
delete bitmap;
|
|
|
|
// store the region bitmap
|
|
} else {
|
|
bitmap->setSegNum(segNum);
|
|
segments->append(bitmap);
|
|
}
|
|
|
|
// delete the referenced bitmap
|
|
if (nRefSegs == 1) {
|
|
discardSegment(refSegs[0]);
|
|
} else {
|
|
delete refBitmap;
|
|
}
|
|
|
|
return;
|
|
|
|
eofError:
|
|
error(getPos(), "Unexpected EOF in JBIG2 stream");
|
|
}
|
|
|
|
JBIG2Bitmap *JBIG2Stream::readGenericRefinementRegion(int w, int h,
|
|
int templ, GBool tpgrOn,
|
|
JBIG2Bitmap *refBitmap,
|
|
int refDX, int refDY,
|
|
int *atx, int *aty) {
|
|
JBIG2Bitmap *bitmap;
|
|
GBool ltp;
|
|
Guint ltpCX, cx, cx0, cx2, cx3, cx4, tpgrCX0, tpgrCX1, tpgrCX2;
|
|
JBIG2BitmapPtr cxPtr0, cxPtr1, cxPtr2, cxPtr3, cxPtr4, cxPtr5, cxPtr6;
|
|
JBIG2BitmapPtr tpgrCXPtr0, tpgrCXPtr1, tpgrCXPtr2;
|
|
int x, y, pix;
|
|
|
|
bitmap = new JBIG2Bitmap(0, w, h);
|
|
bitmap->clearToZero();
|
|
|
|
// set up the typical row context
|
|
if (templ) {
|
|
ltpCX = 0x008;
|
|
} else {
|
|
ltpCX = 0x0010;
|
|
}
|
|
|
|
ltp = 0;
|
|
for (y = 0; y < h; ++y) {
|
|
|
|
if (templ) {
|
|
|
|
// set up the context
|
|
bitmap->getPixelPtr(0, y-1, &cxPtr0);
|
|
cx0 = bitmap->nextPixel(&cxPtr0);
|
|
bitmap->getPixelPtr(-1, y, &cxPtr1);
|
|
refBitmap->getPixelPtr(-refDX, y-1-refDY, &cxPtr2);
|
|
refBitmap->getPixelPtr(-1-refDX, y-refDY, &cxPtr3);
|
|
cx3 = refBitmap->nextPixel(&cxPtr3);
|
|
cx3 = (cx3 << 1) | refBitmap->nextPixel(&cxPtr3);
|
|
refBitmap->getPixelPtr(-refDX, y+1-refDY, &cxPtr4);
|
|
cx4 = refBitmap->nextPixel(&cxPtr4);
|
|
|
|
// set up the typical prediction context
|
|
tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy
|
|
if (tpgrOn) {
|
|
refBitmap->getPixelPtr(-1-refDX, y-1-refDY, &tpgrCXPtr0);
|
|
tpgrCX0 = refBitmap->nextPixel(&tpgrCXPtr0);
|
|
tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
|
|
tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
|
|
refBitmap->getPixelPtr(-1-refDX, y-refDY, &tpgrCXPtr1);
|
|
tpgrCX1 = refBitmap->nextPixel(&tpgrCXPtr1);
|
|
tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
|
|
tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
|
|
refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &tpgrCXPtr2);
|
|
tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2);
|
|
tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
|
|
tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
|
|
}
|
|
|
|
for (x = 0; x < w; ++x) {
|
|
|
|
// update the context
|
|
cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 7;
|
|
cx3 = ((cx3 << 1) | refBitmap->nextPixel(&cxPtr3)) & 7;
|
|
cx4 = ((cx4 << 1) | refBitmap->nextPixel(&cxPtr4)) & 3;
|
|
|
|
if (tpgrOn) {
|
|
// update the typical predictor context
|
|
tpgrCX0 = ((tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0)) & 7;
|
|
tpgrCX1 = ((tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1)) & 7;
|
|
tpgrCX2 = ((tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2)) & 7;
|
|
|
|
// check for a "typical" pixel
|
|
if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) {
|
|
ltp = !ltp;
|
|
}
|
|
if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) {
|
|
bitmap->clearPixel(x, y);
|
|
continue;
|
|
} else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) {
|
|
bitmap->setPixel(x, y);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// build the context
|
|
cx = (cx0 << 7) | (bitmap->nextPixel(&cxPtr1) << 6) |
|
|
(refBitmap->nextPixel(&cxPtr2) << 5) |
|
|
(cx3 << 2) | cx4;
|
|
|
|
// decode the pixel
|
|
if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) {
|
|
bitmap->setPixel(x, y);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// set up the context
|
|
bitmap->getPixelPtr(0, y-1, &cxPtr0);
|
|
cx0 = bitmap->nextPixel(&cxPtr0);
|
|
bitmap->getPixelPtr(-1, y, &cxPtr1);
|
|
refBitmap->getPixelPtr(-refDX, y-1-refDY, &cxPtr2);
|
|
cx2 = refBitmap->nextPixel(&cxPtr2);
|
|
refBitmap->getPixelPtr(-1-refDX, y-refDY, &cxPtr3);
|
|
cx3 = refBitmap->nextPixel(&cxPtr3);
|
|
cx3 = (cx3 << 1) | refBitmap->nextPixel(&cxPtr3);
|
|
refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &cxPtr4);
|
|
cx4 = refBitmap->nextPixel(&cxPtr4);
|
|
cx4 = (cx4 << 1) | refBitmap->nextPixel(&cxPtr4);
|
|
bitmap->getPixelPtr(atx[0], y+aty[0], &cxPtr5);
|
|
refBitmap->getPixelPtr(atx[1]-refDX, y+aty[1]-refDY, &cxPtr6);
|
|
|
|
// set up the typical prediction context
|
|
tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy
|
|
if (tpgrOn) {
|
|
refBitmap->getPixelPtr(-1-refDX, y-1-refDY, &tpgrCXPtr0);
|
|
tpgrCX0 = refBitmap->nextPixel(&tpgrCXPtr0);
|
|
tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
|
|
tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
|
|
refBitmap->getPixelPtr(-1-refDX, y-refDY, &tpgrCXPtr1);
|
|
tpgrCX1 = refBitmap->nextPixel(&tpgrCXPtr1);
|
|
tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
|
|
tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
|
|
refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &tpgrCXPtr2);
|
|
tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2);
|
|
tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
|
|
tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
|
|
}
|
|
|
|
for (x = 0; x < w; ++x) {
|
|
|
|
// update the context
|
|
cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 3;
|
|
cx2 = ((cx2 << 1) | refBitmap->nextPixel(&cxPtr2)) & 3;
|
|
cx3 = ((cx3 << 1) | refBitmap->nextPixel(&cxPtr3)) & 7;
|
|
cx4 = ((cx4 << 1) | refBitmap->nextPixel(&cxPtr4)) & 7;
|
|
|
|
if (tpgrOn) {
|
|
// update the typical predictor context
|
|
tpgrCX0 = ((tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0)) & 7;
|
|
tpgrCX1 = ((tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1)) & 7;
|
|
tpgrCX2 = ((tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2)) & 7;
|
|
|
|
// check for a "typical" pixel
|
|
if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) {
|
|
ltp = !ltp;
|
|
}
|
|
if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) {
|
|
bitmap->clearPixel(x, y);
|
|
continue;
|
|
} else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) {
|
|
bitmap->setPixel(x, y);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// build the context
|
|
cx = (cx0 << 11) | (bitmap->nextPixel(&cxPtr1) << 10) |
|
|
(cx2 << 8) | (cx3 << 5) | (cx4 << 2) |
|
|
(bitmap->nextPixel(&cxPtr5) << 1) |
|
|
refBitmap->nextPixel(&cxPtr6);
|
|
|
|
// decode the pixel
|
|
if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) {
|
|
bitmap->setPixel(x, y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bitmap;
|
|
}
|
|
|
|
void JBIG2Stream::readPageInfoSeg(Guint length) {
|
|
Guint xRes, yRes, flags, striping;
|
|
|
|
if (!readULong(&pageW) || !readULong(&pageH) ||
|
|
!readULong(&xRes) || !readULong(&yRes) ||
|
|
!readUByte(&flags) || !readUWord(&striping)) {
|
|
goto eofError;
|
|
}
|
|
pageDefPixel = (flags >> 2) & 1;
|
|
defCombOp = (flags >> 3) & 3;
|
|
|
|
// allocate the page bitmap
|
|
if (pageH == 0xffffffff) {
|
|
curPageH = striping & 0x7fff;
|
|
} else {
|
|
curPageH = pageH;
|
|
}
|
|
pageBitmap = new JBIG2Bitmap(0, pageW, curPageH);
|
|
|
|
// default pixel value
|
|
if (pageDefPixel) {
|
|
pageBitmap->clearToOne();
|
|
} else {
|
|
pageBitmap->clearToZero();
|
|
}
|
|
|
|
return;
|
|
|
|
eofError:
|
|
error(getPos(), "Unexpected EOF in JBIG2 stream");
|
|
}
|
|
|
|
void JBIG2Stream::readEndOfStripeSeg(Guint length) {
|
|
Guint i;
|
|
|
|
// skip the segment
|
|
for (i = 0; i < length; ++i) {
|
|
curStr->getChar();
|
|
}
|
|
}
|
|
|
|
void JBIG2Stream::readProfilesSeg(Guint length) {
|
|
Guint i;
|
|
|
|
// skip the segment
|
|
for (i = 0; i < length; ++i) {
|
|
curStr->getChar();
|
|
}
|
|
}
|
|
|
|
void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) {
|
|
JBIG2HuffmanTable *huffTab;
|
|
Guint flags, oob, prefixBits, rangeBits;
|
|
int lowVal, highVal, val;
|
|
Guint huffTabSize, i;
|
|
|
|
if (!readUByte(&flags) || !readLong(&lowVal) || !readLong(&highVal)) {
|
|
goto eofError;
|
|
}
|
|
oob = flags & 1;
|
|
prefixBits = ((flags >> 1) & 7) + 1;
|
|
rangeBits = ((flags >> 4) & 7) + 1;
|
|
|
|
huffDecoder->reset();
|
|
huffTabSize = 8;
|
|
huffTab = (JBIG2HuffmanTable *)
|
|
gmallocn(huffTabSize, sizeof(JBIG2HuffmanTable));
|
|
i = 0;
|
|
val = lowVal;
|
|
while (val < highVal) {
|
|
if (i == huffTabSize) {
|
|
huffTabSize *= 2;
|
|
huffTab = (JBIG2HuffmanTable *)
|
|
greallocn(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable));
|
|
}
|
|
huffTab[i].val = val;
|
|
huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
|
|
huffTab[i].rangeLen = huffDecoder->readBits(rangeBits);
|
|
val += 1 << huffTab[i].rangeLen;
|
|
++i;
|
|
}
|
|
if (i + oob + 3 > huffTabSize) {
|
|
huffTabSize = i + oob + 3;
|
|
huffTab = (JBIG2HuffmanTable *)
|
|
greallocn(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable));
|
|
}
|
|
huffTab[i].val = lowVal - 1;
|
|
huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
|
|
huffTab[i].rangeLen = jbig2HuffmanLOW;
|
|
++i;
|
|
huffTab[i].val = highVal;
|
|
huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
|
|
huffTab[i].rangeLen = 32;
|
|
++i;
|
|
if (oob) {
|
|
huffTab[i].val = 0;
|
|
huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
|
|
huffTab[i].rangeLen = jbig2HuffmanOOB;
|
|
++i;
|
|
}
|
|
huffTab[i].val = 0;
|
|
huffTab[i].prefixLen = 0;
|
|
huffTab[i].rangeLen = jbig2HuffmanEOT;
|
|
huffDecoder->buildTable(huffTab, i);
|
|
|
|
// create and store the new table segment
|
|
segments->append(new JBIG2CodeTable(segNum, huffTab));
|
|
|
|
return;
|
|
|
|
eofError:
|
|
error(getPos(), "Unexpected EOF in JBIG2 stream");
|
|
}
|
|
|
|
void JBIG2Stream::readExtensionSeg(Guint length) {
|
|
Guint i;
|
|
|
|
// skip the segment
|
|
for (i = 0; i < length; ++i) {
|
|
curStr->getChar();
|
|
}
|
|
}
|
|
|
|
JBIG2Segment *JBIG2Stream::findSegment(Guint segNum) {
|
|
JBIG2Segment *seg;
|
|
int i;
|
|
|
|
for (i = 0; i < globalSegments->getLength(); ++i) {
|
|
seg = (JBIG2Segment *)globalSegments->get(i);
|
|
if (seg->getSegNum() == segNum) {
|
|
return seg;
|
|
}
|
|
}
|
|
for (i = 0; i < segments->getLength(); ++i) {
|
|
seg = (JBIG2Segment *)segments->get(i);
|
|
if (seg->getSegNum() == segNum) {
|
|
return seg;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void JBIG2Stream::discardSegment(Guint segNum) {
|
|
JBIG2Segment *seg;
|
|
int i;
|
|
|
|
for (i = 0; i < globalSegments->getLength(); ++i) {
|
|
seg = (JBIG2Segment *)globalSegments->get(i);
|
|
if (seg->getSegNum() == segNum) {
|
|
globalSegments->del(i);
|
|
return;
|
|
}
|
|
}
|
|
for (i = 0; i < segments->getLength(); ++i) {
|
|
seg = (JBIG2Segment *)segments->get(i);
|
|
if (seg->getSegNum() == segNum) {
|
|
segments->del(i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void JBIG2Stream::resetGenericStats(Guint templ,
|
|
JArithmeticDecoderStats *prevStats) {
|
|
int size;
|
|
|
|
size = contextSize[templ];
|
|
if (prevStats && prevStats->getContextSize() == size) {
|
|
if (genericRegionStats->getContextSize() == size) {
|
|
genericRegionStats->copyFrom(prevStats);
|
|
} else {
|
|
delete genericRegionStats;
|
|
genericRegionStats = prevStats->copy();
|
|
}
|
|
} else {
|
|
if (genericRegionStats->getContextSize() == size) {
|
|
genericRegionStats->reset();
|
|
} else {
|
|
delete genericRegionStats;
|
|
genericRegionStats = new JArithmeticDecoderStats(1 << size);
|
|
}
|
|
}
|
|
}
|
|
|
|
void JBIG2Stream::resetRefinementStats(Guint templ,
|
|
JArithmeticDecoderStats *prevStats) {
|
|
int size;
|
|
|
|
size = refContextSize[templ];
|
|
if (prevStats && prevStats->getContextSize() == size) {
|
|
if (refinementRegionStats->getContextSize() == size) {
|
|
refinementRegionStats->copyFrom(prevStats);
|
|
} else {
|
|
delete refinementRegionStats;
|
|
refinementRegionStats = prevStats->copy();
|
|
}
|
|
} else {
|
|
if (refinementRegionStats->getContextSize() == size) {
|
|
refinementRegionStats->reset();
|
|
} else {
|
|
delete refinementRegionStats;
|
|
refinementRegionStats = new JArithmeticDecoderStats(1 << size);
|
|
}
|
|
}
|
|
}
|
|
|
|
void JBIG2Stream::resetIntStats(int symCodeLen) {
|
|
iadhStats->reset();
|
|
iadwStats->reset();
|
|
iaexStats->reset();
|
|
iaaiStats->reset();
|
|
iadtStats->reset();
|
|
iaitStats->reset();
|
|
iafsStats->reset();
|
|
iadsStats->reset();
|
|
iardxStats->reset();
|
|
iardyStats->reset();
|
|
iardwStats->reset();
|
|
iardhStats->reset();
|
|
iariStats->reset();
|
|
if (iaidStats->getContextSize() == 1 << (symCodeLen + 1)) {
|
|
iaidStats->reset();
|
|
} else {
|
|
delete iaidStats;
|
|
iaidStats = new JArithmeticDecoderStats(1 << (symCodeLen + 1));
|
|
}
|
|
}
|
|
|
|
GBool JBIG2Stream::readUByte(Guint *x) {
|
|
int c0;
|
|
|
|
if ((c0 = curStr->getChar()) == EOF) {
|
|
return gFalse;
|
|
}
|
|
*x = (Guint)c0;
|
|
return gTrue;
|
|
}
|
|
|
|
GBool JBIG2Stream::readByte(int *x) {
|
|
int c0;
|
|
|
|
if ((c0 = curStr->getChar()) == EOF) {
|
|
return gFalse;
|
|
}
|
|
*x = c0;
|
|
if (c0 & 0x80) {
|
|
*x |= -1 - 0xff;
|
|
}
|
|
return gTrue;
|
|
}
|
|
|
|
GBool JBIG2Stream::readUWord(Guint *x) {
|
|
int c0, c1;
|
|
|
|
if ((c0 = curStr->getChar()) == EOF ||
|
|
(c1 = curStr->getChar()) == EOF) {
|
|
return gFalse;
|
|
}
|
|
*x = (Guint)((c0 << 8) | c1);
|
|
return gTrue;
|
|
}
|
|
|
|
GBool JBIG2Stream::readULong(Guint *x) {
|
|
int c0, c1, c2, c3;
|
|
|
|
if ((c0 = curStr->getChar()) == EOF ||
|
|
(c1 = curStr->getChar()) == EOF ||
|
|
(c2 = curStr->getChar()) == EOF ||
|
|
(c3 = curStr->getChar()) == EOF) {
|
|
return gFalse;
|
|
}
|
|
*x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
|
|
return gTrue;
|
|
}
|
|
|
|
GBool JBIG2Stream::readLong(int *x) {
|
|
int c0, c1, c2, c3;
|
|
|
|
if ((c0 = curStr->getChar()) == EOF ||
|
|
(c1 = curStr->getChar()) == EOF ||
|
|
(c2 = curStr->getChar()) == EOF ||
|
|
(c3 = curStr->getChar()) == EOF) {
|
|
return gFalse;
|
|
}
|
|
*x = ((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
|
|
if (c0 & 0x80) {
|
|
*x |= -1 - (int)0xffffffff;
|
|
}
|
|
return gTrue;
|
|
}
|
|
|