blob: 1bb4751cf1eebcc531f7fa05b4e4714cb2e6e743 [file] [log] [blame]
//========================================================================
//
// JBIG2Stream.cc
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================
//========================================================================
//
// Modified under the Poppler project - http://poppler.freedesktop.org
//
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
// Copyright (C) 2006 Raj Kumar <rkumar@archive.org>
// Copyright (C) 2006 Paul Walmsley <paul@booyaka.com>
// Copyright (C) 2006-2010, 2012, 2014-2019 Albert Astals Cid <aacid@kde.org>
// Copyright (C) 2009 David Benjamin <davidben@mit.edu>
// Copyright (C) 2011 Edward Jiang <ejiang@google.com>
// Copyright (C) 2012 William Bader <williambader@hotmail.com>
// Copyright (C) 2012 Thomas Freitag <Thomas.Freitag@alfa.de>
// Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
// Copyright (C) 2013, 2014 Fabio D'Urso <fabiodurso@hotmail.it>
// Copyright (C) 2015 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
// Copyright (C) 2019 LE GARREC Vincent <legarrec.vincent@gmail.com>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
//
//========================================================================
#include <config.h>
#include <stdlib.h>
#include <limits.h>
#include "Error.h"
#include "JArithmeticDecoder.h"
#include "JBIG2Stream.h"
//~ share these tables
#include "Stream-CCITT.h"
//------------------------------------------------------------------------
static const int contextSize[4] = { 16, 13, 10, 10 };
static const int refContextSize[2] = { 13, 10 };
//------------------------------------------------------------------------
// JBIG2HuffmanTable
//------------------------------------------------------------------------
#define jbig2HuffmanLOW 0xfffffffd
#define jbig2HuffmanOOB 0xfffffffe
#define jbig2HuffmanEOT 0xffffffff
struct JBIG2HuffmanTable {
int val;
unsigned int prefixLen;
unsigned int rangeLen; // can also be LOW, OOB, or EOT
unsigned int prefix;
};
static JBIG2HuffmanTable huffTableA[] = {
{ 0, 1, 4, 0x000 },
{ 16, 2, 8, 0x002 },
{ 272, 3, 16, 0x006 },
{ 65808, 3, 32, 0x007 },
{ 0, 0, jbig2HuffmanEOT, 0 }
};
static 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 }
};
static 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 }
};
static 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 }
};
static 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 }
};
static 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 }
};
static 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 }
};
static 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 }
};
static 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 }
};
static 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 }
};
static 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 }
};
static 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 }
};
static 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 }
};
static 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 }
};
static 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.
bool decodeInt(int *x, JBIG2HuffmanTable *table);
unsigned int readBits(unsigned int n);
unsigned int readBit();
// Sort the table by prefix length and assign prefix values.
static bool buildTable(JBIG2HuffmanTable *table, unsigned int len);
private:
Stream *str;
unsigned int buf;
unsigned int bufLen;
};
JBIG2HuffmanDecoder::JBIG2HuffmanDecoder() {
str = nullptr;
reset();
}
JBIG2HuffmanDecoder::~JBIG2HuffmanDecoder() {
}
void JBIG2HuffmanDecoder::reset() {
buf = 0;
bufLen = 0;
}
//~ optimize this
bool JBIG2HuffmanDecoder::decodeInt(int *x, JBIG2HuffmanTable *table) {
unsigned int 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 false;
}
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 true;
}
++i;
}
return false;
}
unsigned int JBIG2HuffmanDecoder::readBits(unsigned int n) {
unsigned int 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;
}
unsigned int JBIG2HuffmanDecoder::readBit() {
if (bufLen == 0) {
buf = str->getChar();
bufLen = 8;
}
--bufLen;
return (buf >> bufLen) & 1;
}
bool JBIG2HuffmanDecoder::buildTable(JBIG2HuffmanTable *table, unsigned int len) {
unsigned int 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
if (table[0].rangeLen != jbig2HuffmanEOT) {
i = 0;
prefix = 0;
table[i++].prefix = prefix++;
for (; table[i].rangeLen != jbig2HuffmanEOT; ++i) {
if (table[i].prefixLen - table[i-1].prefixLen > 32) {
error(errSyntaxError, -1, "Failed to build table for JBIG2 stream");
return false;
} else {
prefix <<= table[i].prefixLen - table[i-1].prefixLen;
}
table[i].prefix = prefix++;
}
}
return true;
}
//------------------------------------------------------------------------
// JBIG2MMRDecoder
//------------------------------------------------------------------------
class JBIG2MMRDecoder {
public:
JBIG2MMRDecoder();
~JBIG2MMRDecoder();
void setStream(Stream *strA) { str = strA; }
void reset();
int get2DCode();
int getBlackCode();
int getWhiteCode();
unsigned int get24Bits();
void skipTo(unsigned int length);
private:
Stream *str;
unsigned int buf;
unsigned int bufLen;
unsigned int nBytesRead;
};
JBIG2MMRDecoder::JBIG2MMRDecoder() {
str = nullptr;
reset();
}
JBIG2MMRDecoder::~JBIG2MMRDecoder() {
}
void JBIG2MMRDecoder::reset() {
buf = 0;
bufLen = 0;
nBytesRead = 0;
}
int JBIG2MMRDecoder::get2DCode() {
const CCITTCode *p = nullptr;
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 if (bufLen < 8) {
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 == nullptr || p->bits < 0) {
error(errSyntaxError, str->getPos(), "Bad two dim code in JBIG2 MMR stream");
return EOF;
}
bufLen -= p->bits;
return p->n;
}
int JBIG2MMRDecoder::getWhiteCode() {
const CCITTCode *p;
unsigned int code;
if (bufLen == 0) {
buf = str->getChar() & 0xff;
bufLen = 8;
++nBytesRead;
}
while (1) {
if (bufLen >= 11 && ((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(errSyntaxError, 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() {
const CCITTCode *p;
unsigned int code;
if (bufLen == 0) {
buf = str->getChar() & 0xff;
bufLen = 8;
++nBytesRead;
}
while (1) {
if (bufLen >= 10 && ((buf >> (bufLen - 6)) & 0x3f) == 0) {
if (bufLen <= 13) {
code = buf << (13 - bufLen);
} else {
code = buf >> (bufLen - 13);
}
p = &blackTab1[code & 0x7f];
} else if (bufLen >= 7 && ((buf >> (bufLen - 4)) & 0x0f) == 0 &&
((buf >> (bufLen - 6)) & 0x03) != 0) {
if (bufLen <= 12) {
code = buf << (12 - bufLen);
} else {
code = buf >> (bufLen - 12);
}
if (unlikely((code & 0xff) < 64)) {
break;
}
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(errSyntaxError, 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;
}
unsigned int JBIG2MMRDecoder::get24Bits() {
while (bufLen < 24) {
buf = (buf << 8) | (str->getChar() & 0xff);
bufLen += 8;
++nBytesRead;
}
return (buf >> (bufLen - 24)) & 0xffffff;
}
void JBIG2MMRDecoder::skipTo(unsigned int length) {
while (nBytesRead < length) {
str->getChar();
++nBytesRead;
}
}
//------------------------------------------------------------------------
// JBIG2Segment
//------------------------------------------------------------------------
enum JBIG2SegmentType {
jbig2SegBitmap,
jbig2SegSymbolDict,
jbig2SegPatternDict,
jbig2SegCodeTable
};
class JBIG2Segment {
public:
JBIG2Segment(unsigned int segNumA) { segNum = segNumA; }
virtual ~JBIG2Segment() {}
JBIG2Segment(const JBIG2Segment &) = delete;
JBIG2Segment& operator=(const JBIG2Segment &) = delete;
void setSegNum(unsigned int segNumA) { segNum = segNumA; }
unsigned int getSegNum() { return segNum; }
virtual JBIG2SegmentType getType() = 0;
private:
unsigned int segNum;
};
//------------------------------------------------------------------------
// JBIG2Bitmap
//------------------------------------------------------------------------
struct JBIG2BitmapPtr {
unsigned char *p;
int shift;
int x;
};
class JBIG2Bitmap: public JBIG2Segment {
public:
JBIG2Bitmap(unsigned int segNumA, int wA, int hA);
~JBIG2Bitmap();
JBIG2SegmentType getType() override { return jbig2SegBitmap; }
JBIG2Bitmap *copy() { return new JBIG2Bitmap(0, this); }
JBIG2Bitmap *getSlice(unsigned int x, unsigned int y, unsigned int wA, unsigned int hA);
void expand(int newH, unsigned int pixel);
void clearToZero();
void clearToOne();
int getWidth() { return w; }
int getHeight() { return h; }
int getLineSize() { return line; }
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, unsigned int combOp);
unsigned char *getDataPtr() { return data; }
int getDataSize() { return h * line; }
bool isOk() { return data != nullptr; }
private:
JBIG2Bitmap(unsigned int segNumA, JBIG2Bitmap *bitmap);
int w, h, line;
unsigned char *data;
};
JBIG2Bitmap::JBIG2Bitmap(unsigned int 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) {
error(errSyntaxError, -1, "invalid width/height");
data = nullptr;
return;
}
// need to allocate one extra guard byte for use in combine()
data = (unsigned char *)gmalloc_checkoverflow(h * line + 1);
if (data != nullptr) {
data[h * line] = 0;
}
}
JBIG2Bitmap::JBIG2Bitmap(unsigned int segNumA, JBIG2Bitmap *bitmap):
JBIG2Segment(segNumA)
{
if (unlikely(bitmap == nullptr)) {
error(errSyntaxError, -1, "NULL bitmap in JBIG2Bitmap");
w = h = line = 0;
data = nullptr;
return;
}
w = bitmap->w;
h = bitmap->h;
line = bitmap->line;
if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) {
error(errSyntaxError, -1, "invalid width/height");
data = nullptr;
return;
}
// need to allocate one extra guard byte for use in combine()
data = (unsigned char *)gmalloc(h * line + 1);
memcpy(data, bitmap->data, h * line);
data[h * line] = 0;
}
JBIG2Bitmap::~JBIG2Bitmap() {
gfree(data);
}
//~ optimize this
JBIG2Bitmap *JBIG2Bitmap::getSlice(unsigned int x, unsigned int y, unsigned int wA, unsigned int hA) {
JBIG2Bitmap *slice;
unsigned int xx, yy;
if (!data) {
return nullptr;
}
slice = new JBIG2Bitmap(0, wA, hA);
if (slice->isOk()) {
slice->clearToZero();
for (yy = 0; yy < hA; ++yy) {
for (xx = 0; xx < wA; ++xx) {
if (getPixel(x + xx, y + yy)) {
slice->setPixel(xx, yy);
}
}
}
} else {
delete slice;
slice = nullptr;
}
return slice;
}
void JBIG2Bitmap::expand(int newH, unsigned int pixel) {
if (newH <= h || line <= 0 || newH >= (INT_MAX - 1) / line) {
error(errSyntaxError, -1, "invalid width/height");
gfree(data);
data = nullptr;
return;
}
// need to allocate one extra guard byte for use in combine()
data = (unsigned char *)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 = nullptr;
ptr->shift = 0; // make gcc happy
ptr->x = 0; // make gcc happy
} 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 = nullptr;
} 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,
unsigned int combOp) {
int x0, x1, y0, y1, xx, yy;
unsigned char *srcPtr, *destPtr;
unsigned int src0, src1, src, dest, s1, s2, m1, m2, m3;
bool oneByte;
// check for the pathological case where y = -2^31
if (y < -0x7fffffff) {
return;
}
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) {
if (unlikely((y + yy >= h) || (y + yy < 0)))
continue;
// 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(unsigned int segNumA, unsigned int sizeA);
~JBIG2SymbolDict();
JBIG2SegmentType getType() override { return jbig2SegSymbolDict; }
unsigned int getSize() { return size; }
void setBitmap(unsigned int idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }
JBIG2Bitmap *getBitmap(unsigned int idx) { return bitmaps[idx]; }
bool isOk() { return bitmaps != nullptr; }
void setGenericRegionStats(JArithmeticDecoderStats *stats)
{ genericRegionStats = stats; }
void setRefinementRegionStats(JArithmeticDecoderStats *stats)
{ refinementRegionStats = stats; }
JArithmeticDecoderStats *getGenericRegionStats()
{ return genericRegionStats; }
JArithmeticDecoderStats *getRefinementRegionStats()
{ return refinementRegionStats; }
private:
unsigned int size;
JBIG2Bitmap **bitmaps;
JArithmeticDecoderStats *genericRegionStats;
JArithmeticDecoderStats *refinementRegionStats;
};
JBIG2SymbolDict::JBIG2SymbolDict(unsigned int segNumA, unsigned int sizeA):
JBIG2Segment(segNumA)
{
unsigned int i;
size = sizeA;
bitmaps = (JBIG2Bitmap **)gmallocn_checkoverflow(size, sizeof(JBIG2Bitmap *));
if (!bitmaps) size = 0;
for (i = 0; i < size; ++i) {
bitmaps[i] = nullptr;
}
genericRegionStats = nullptr;
refinementRegionStats = nullptr;
}
JBIG2SymbolDict::~JBIG2SymbolDict() {
unsigned int 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(unsigned int segNumA, unsigned int sizeA);
~JBIG2PatternDict();
JBIG2SegmentType getType() override { return jbig2SegPatternDict; }
unsigned int getSize() { return size; }
void setBitmap(unsigned int idx, JBIG2Bitmap *bitmap) { if (likely(idx < size)) bitmaps[idx] = bitmap; }
JBIG2Bitmap *getBitmap(unsigned int idx) { return (idx < size) ? bitmaps[idx] : nullptr; }
private:
unsigned int size;
JBIG2Bitmap **bitmaps;
};
JBIG2PatternDict::JBIG2PatternDict(unsigned int segNumA, unsigned int sizeA):
JBIG2Segment(segNumA)
{
bitmaps = (JBIG2Bitmap **)gmallocn_checkoverflow(sizeA, sizeof(JBIG2Bitmap *));
if (bitmaps) {
size = sizeA;
} else {
size = 0;
error(errSyntaxError, -1, "JBIG2PatternDict: can't allocate bitmaps");
}
}
JBIG2PatternDict::~JBIG2PatternDict() {
unsigned int i;
for (i = 0; i < size; ++i) {
delete bitmaps[i];
}
gfree(bitmaps);
}
//------------------------------------------------------------------------
// JBIG2CodeTable
//------------------------------------------------------------------------
class JBIG2CodeTable: public JBIG2Segment {
public:
JBIG2CodeTable(unsigned int segNumA, JBIG2HuffmanTable *tableA);
~JBIG2CodeTable();
JBIG2SegmentType getType() override { return jbig2SegCodeTable; }
JBIG2HuffmanTable *getHuffTable() { return table; }
private:
JBIG2HuffmanTable *table;
};
JBIG2CodeTable::JBIG2CodeTable(unsigned int segNumA, JBIG2HuffmanTable *tableA):
JBIG2Segment(segNumA)
{
table = tableA;
}
JBIG2CodeTable::~JBIG2CodeTable() {
gfree(table);
}
//------------------------------------------------------------------------
// JBIG2Stream
//------------------------------------------------------------------------
JBIG2Stream::JBIG2Stream(Stream *strA, Object &&globalsStreamA, Object *globalsStreamRefA):
FilterStream(strA)
{
pageBitmap = nullptr;
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();
if (globalsStreamA.isStream()) {
globalsStream = std::move(globalsStreamA);
if (globalsStreamRefA->isRef())
globalsStreamRef = globalsStreamRefA->getRef();
}
segments = globalSegments = nullptr;
curStr = nullptr;
dataPtr = dataEnd = nullptr;
}
JBIG2Stream::~JBIG2Stream() {
close();
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 std::vector<JBIG2Segment*>();
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 std::vector<JBIG2Segment*>();
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 = nullptr;
}
}
void JBIG2Stream::close() {
if (pageBitmap) {
delete pageBitmap;
pageBitmap = nullptr;
}
if (segments) {
for (auto entry : *segments) {
delete entry;
}
delete segments;
segments = nullptr;
}
if (globalSegments) {
for (auto entry : *globalSegments) {
delete entry;
}
delete globalSegments;
globalSegments = nullptr;
}
dataPtr = dataEnd = nullptr;
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;
}
Goffset JBIG2Stream::getPos() {
if (pageBitmap == nullptr) {
return 0;
}
return dataPtr - pageBitmap->getDataPtr();
}
int JBIG2Stream::getChars(int nChars, unsigned char *buffer) {
int n, i;
if (nChars <= 0 || !dataPtr) {
return 0;
}
if (dataEnd - dataPtr < nChars) {
n = (int)(dataEnd - dataPtr);
} else {
n = nChars;
}
for (i = 0; i < n; ++i) {
buffer[i] = *dataPtr++ ^ 0xff;
}
return n;
}
GooString *JBIG2Stream::getPSFilter(int psLevel, const char *indent) {
return nullptr;
}
bool JBIG2Stream::isBinary(bool last) {
return str->isBinary(true);
}
void JBIG2Stream::readSegments() {
unsigned int segNum, segFlags, segType, page, segLength;
unsigned int refFlags, nRefSegs;
unsigned int *refSegs;
Goffset segDataPos;
int c1, c2, c3;
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 (unsigned int i = 0; i < (nRefSegs + 9) >> 3; ++i) {
if ((c1 = curStr->getChar()) == EOF) {
goto eofError1;
}
}
}
// referred-to segment numbers
refSegs = (unsigned int *)gmallocn(nRefSegs, sizeof(unsigned int));
if (segNum <= 256) {
for (unsigned int i = 0; i < nRefSegs; ++i) {
if (!readUByte(&refSegs[i])) {
goto eofError2;
}
}
} else if (segNum <= 65536) {
for (unsigned int i = 0; i < nRefSegs; ++i) {
if (!readUWord(&refSegs[i])) {
goto eofError2;
}
}
} else {
for (unsigned int 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;
}
// keep track of the start of the segment data
segDataPos = curStr->getPos();
// check for missing page information segment
if (!pageBitmap && ((segType >= 4 && segType <= 7) ||
(segType >= 20 && segType <= 43))) {
error(errSyntaxError, curStr->getPos(), "First JBIG2 segment associated with a page must be a page information segment");
goto syntaxError;
}
// read the segment data
switch (segType) {
case 0:
if (!readSymbolDictSeg(segNum, segLength, refSegs, nRefSegs)) {
goto syntaxError;
}
break;
case 4:
readTextRegionSeg(segNum, false, false, segLength, refSegs, nRefSegs);
break;
case 6:
readTextRegionSeg(segNum, true, false, segLength, refSegs, nRefSegs);
break;
case 7:
readTextRegionSeg(segNum, true, true, segLength, refSegs, nRefSegs);
break;
case 16:
readPatternDictSeg(segNum, segLength);
break;
case 20:
readHalftoneRegionSeg(segNum, false, false, segLength,
refSegs, nRefSegs);
break;
case 22:
readHalftoneRegionSeg(segNum, true, false, segLength,
refSegs, nRefSegs);
break;
case 23:
readHalftoneRegionSeg(segNum, true, true, segLength,
refSegs, nRefSegs);
break;
case 36:
readGenericRegionSeg(segNum, false, false, segLength);
break;
case 38:
readGenericRegionSeg(segNum, true, false, segLength);
break;
case 39:
readGenericRegionSeg(segNum, true, true, segLength);
break;
case 40:
readGenericRefinementRegionSeg(segNum, false, false, segLength,
refSegs, nRefSegs);
break;
case 42:
readGenericRefinementRegionSeg(segNum, true, false, segLength,
refSegs, nRefSegs);
break;
case 43:
readGenericRefinementRegionSeg(segNum, true, true, 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(errSyntaxError, curStr->getPos(), "Unknown segment type in JBIG2 stream");
for (unsigned int i = 0; i < segLength; ++i) {
if ((c1 = curStr->getChar()) == EOF) {
goto eofError2;
}
}
break;
}
// Make sure the segment handler read all of the bytes in the
// segment data, unless this segment is marked as having an
// unknown length (section 7.2.7 of the JBIG2 Final Committee Draft)
if (segLength != 0xffffffff) {
Goffset segExtraBytes = segDataPos + segLength - curStr->getPos();
if (segExtraBytes > 0) {
// If we didn't read all of the bytes in the segment data,
// indicate an error, and throw away the rest of the data.
// v.3.1.01.13 of the LuraTech PDF Compressor Server will
// sometimes generate an extraneous NULL byte at the end of
// arithmetic-coded symbol dictionary segments when numNewSyms
// == 0. Segments like this often occur for blank pages.
error(errSyntaxError, curStr->getPos(), "{0:lld} extraneous byte{1:s} after segment",
segExtraBytes, (segExtraBytes > 1) ? "s" : "");
// Burn through the remaining bytes -- inefficient, but
// hopefully we're not doing this much
int trash;
for (Goffset i = segExtraBytes; i > 0; i--) {
readByte(&trash);
}
} else if (segExtraBytes < 0) {
// If we read more bytes than we should have, according to the
// segment length field, note an error.
error(errSyntaxError, curStr->getPos(), "Previous segment handler read too many bytes");
}
}
gfree(refSegs);
}
return;
syntaxError:
gfree(refSegs);
return;
eofError2:
gfree(refSegs);
eofError1:
error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream");
}
bool JBIG2Stream::readSymbolDictSeg(unsigned int segNum, unsigned int length,
unsigned int *refSegs, unsigned int nRefSegs) {
JBIG2SymbolDict *symbolDict;
JBIG2HuffmanTable *huffDHTable, *huffDWTable;
JBIG2HuffmanTable *huffBMSizeTable, *huffAggInstTable;
JBIG2Segment *seg;
std::vector<JBIG2Segment*> *codeTables;
JBIG2SymbolDict *inputSymbolDict;
unsigned int flags, sdTemplate, sdrTemplate, huff, refAgg;
unsigned int huffDH, huffDW, huffBMSize, huffAggInst;
unsigned int contextUsed, contextRetained;
int sdATX[4], sdATY[4], sdrATX[2], sdrATY[2];
unsigned int numExSyms, numNewSyms, numInputSyms, symCodeLen;
JBIG2Bitmap **bitmaps;
JBIG2Bitmap *collBitmap, *refBitmap;
unsigned int *symWidths;
unsigned int symHeight, symWidth, totalWidth, x, symID;
int dh = 0, dw, refAggNum, refDX = 0, refDY = 0, bmSize;
bool ex;
int run, cnt, c;
unsigned int i, j, k;
unsigned char *p;
symWidths = nullptr;
// 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 std::vector<JBIG2Segment*>();
numInputSyms = 0;
for (i = 0; i < nRefSegs; ++i) {
// This is need by bug 12014, returning false makes it not crash
// but we end up with a empty page while acroread is able to render
// part of it
if ((seg = findSegment(refSegs[i]))) {
if (seg->getType() == jbig2SegSymbolDict) {
j = ((JBIG2SymbolDict *)seg)->getSize();
if (numInputSyms > UINT_MAX - j) {
error(errSyntaxError, curStr->getPos(), "Too many input symbols in JBIG2 symbol dictionary");
delete codeTables;
goto eofError;
}
numInputSyms += j;
} else if (seg->getType() == jbig2SegCodeTable) {
codeTables->push_back(seg);
}
} else {
delete codeTables;
return false;
}
}
if (numInputSyms > UINT_MAX - numNewSyms) {
error(errSyntaxError, curStr->getPos(), "Too many input symbols in JBIG2 symbol dictionary");
delete codeTables;
goto eofError;
}
// compute symbol code length, per 6.5.8.2.3
// symCodeLen = ceil( log2( numInputSyms + numNewSyms ) )
i = numInputSyms + numNewSyms;
if (i <= 1) {
symCodeLen = huff ? 1 : 0;
} else {
--i;
symCodeLen = 0;
// i = floor((numSyms-1) / 2^symCodeLen)
while (i > 0) {
++symCodeLen;
i >>= 1;
}
}
// get the input symbol bitmaps
bitmaps = (JBIG2Bitmap **)gmallocn_checkoverflow(numInputSyms + numNewSyms,
sizeof(JBIG2Bitmap *));
if (!bitmaps && (numInputSyms + numNewSyms > 0)) {
error(errSyntaxError, curStr->getPos(), "Too many input symbols in JBIG2 symbol dictionary");
delete codeTables;
goto eofError;
}
for (i = 0; i < numInputSyms + numNewSyms; ++i) {
bitmaps[i] = nullptr;
}
k = 0;
inputSymbolDict = nullptr;
for (i = 0; i < nRefSegs; ++i) {
seg = findSegment(refSegs[i]);
if (seg != nullptr && seg->getType() == jbig2SegSymbolDict) {
inputSymbolDict = (JBIG2SymbolDict *)seg;
for (j = 0; j < inputSymbolDict->getSize(); ++j) {
bitmaps[k++] = inputSymbolDict->getBitmap(j);
}
}
}
// get the Huffman tables
huffDHTable = huffDWTable = nullptr; // make gcc happy
huffBMSizeTable = huffAggInstTable = nullptr; // make gcc happy
i = 0;
if (huff) {
if (huffDH == 0) {
huffDHTable = huffTableD;
} else if (huffDH == 1) {
huffDHTable = huffTableE;
} else {
if (i >= codeTables->size()) {
goto codeTableError;
}
huffDHTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable();
}
if (huffDW == 0) {
huffDWTable = huffTableB;
} else if (huffDW == 1) {
huffDWTable = huffTableC;
} else {
if (i >= codeTables->size()) {
goto codeTableError;
}
huffDWTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable();
}
if (huffBMSize == 0) {
huffBMSizeTable = huffTableA;
} else {
if (i >= codeTables->size()) {
goto codeTableError;
}
huffBMSizeTable =
((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable();
}
if (huffAggInst == 0) {
huffAggInstTable = huffTableA;
} else {
if (i >= codeTables->size()) {
goto codeTableError;
}
huffAggInstTable =
((JBIG2CodeTable *)(*codeTables)[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, nullptr);
}
resetIntStats(symCodeLen);
arithDecoder->start();
}
// set up the arithmetic decoder for refinement/aggregation
if (refAgg) {
if (contextUsed && inputSymbolDict) {
resetRefinementStats(sdrTemplate,
inputSymbolDict->getRefinementRegionStats());
} else {
resetRefinementStats(sdrTemplate, nullptr);
}
}
// allocate symbol widths storage
if (huff && !refAgg) {
symWidths = (unsigned int *)gmallocn(numNewSyms, sizeof(unsigned int));
}
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 && (unsigned int)-dh >= symHeight) {
error(errSyntaxError, curStr->getPos(), "Bad delta-height value in JBIG2 symbol dictionary");
goto syntaxError;
}
symHeight += dh;
if (unlikely(symHeight > 0x40000000)) {
error(errSyntaxError, curStr->getPos(), "Bad height value in JBIG2 symbol dictionary");
goto syntaxError;
}
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 && (unsigned int)-dw >= symWidth) {
error(errSyntaxError, curStr->getPos(), "Bad delta-height value in JBIG2 symbol dictionary");
goto syntaxError;
}
symWidth += dw;
if (i >= numNewSyms) {
error(errSyntaxError, curStr->getPos(), "Too many symbols in JBIG2 symbol dictionary");
goto syntaxError;
}
// 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);
}
if (symID >= numInputSyms + i) {
error(errSyntaxError, curStr->getPos(), "Invalid symbol ID in JBIG2 symbol dictionary");
goto syntaxError;
}
refBitmap = bitmaps[symID];
if (unlikely(refBitmap == nullptr)) {
error(errSyntaxError, curStr->getPos(), "Invalid ref bitmap for symbol ID {0:ud} in JBIG2 symbol dictionary", symID);
goto syntaxError;
}
bitmaps[numInputSyms + i] =
readGenericRefinementRegion(symWidth, symHeight,
sdrTemplate, false,
refBitmap, refDX, refDY,
sdrATX, sdrATY);
//~ do we need to use the bmSize value here (in Huffman mode)?
} else {
bitmaps[numInputSyms + i] =
readTextRegion(huff, true, symWidth, symHeight,
refAggNum, 0, numInputSyms + i, nullptr,
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(false, symWidth, symHeight,
sdTemplate, false, false, nullptr,
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();
if (unlikely(p == nullptr)) {
delete collBitmap;
goto syntaxError;
}
for (k = 0; k < (unsigned int)bmSize; ++k) {
if ((c = curStr->getChar()) == EOF) {
memset(p, 0, bmSize - k);
break;
}
*p++ = (unsigned char)c;
}
} else {
collBitmap = readGenericBitmap(true, totalWidth, symHeight,
0, false, false, nullptr, nullptr, nullptr,
bmSize);
}
if (likely(collBitmap != nullptr)) {
x = 0;
for (; j < i; ++j) {
bitmaps[numInputSyms + j] =
collBitmap->getSlice(x, 0, symWidths[j], symHeight);
x += symWidths[j];
}
delete collBitmap;
} else {
error(errSyntaxError, curStr->getPos(), "collBitmap was null");
goto syntaxError;
}
}
}
// create the symbol dict object
symbolDict = new JBIG2SymbolDict(segNum, numExSyms);
if (!symbolDict->isOk()) {
delete symbolDict;
goto syntaxError;
}
// exported symbol list
i = j = 0;
ex = false;
run = 0; // initialize it once in case the first decodeInt fails
// we do not want to use uninitialized memory
while (i < numInputSyms + numNewSyms) {
if (huff) {
huffDecoder->decodeInt(&run, huffTableA);
} else {
arithDecoder->decodeInt(&run, iaexStats);
}
if (i + run > numInputSyms + numNewSyms ||
(ex && j + run > numExSyms)) {
error(errSyntaxError, curStr->getPos(), "Too many exported symbols in JBIG2 symbol dictionary");
for ( ; j < numExSyms; ++j) symbolDict->setBitmap(j, nullptr);
delete symbolDict;
goto syntaxError;
}
if (ex) {
for (cnt = 0; cnt < run; ++cnt) {
symbolDict->setBitmap(j++, bitmaps[i++]->copy());
}
} else {
i += run;
}
ex = !ex;
}
if (j != numExSyms) {
error(errSyntaxError, curStr->getPos(), "Too few symbols in JBIG2 symbol dictionary");
for ( ; j < numExSyms; ++j) symbolDict->setBitmap(j, nullptr);
delete symbolDict;
goto syntaxError;
}
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->push_back(symbolDict);
return true;
codeTableError:
error(errSyntaxError, curStr->getPos(), "Missing code table in JBIG2 symbol dictionary");
delete codeTables;
syntaxError:
for (i = 0; i < numNewSyms; ++i) {
if (bitmaps[numInputSyms + i]) {
delete bitmaps[numInputSyms + i];
}
}
gfree(bitmaps);
if (symWidths) {
gfree(symWidths);
}
return false;
eofError:
error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream");
return false;
}
void JBIG2Stream::readTextRegionSeg(unsigned int segNum, bool imm,
bool lossless, unsigned int length,
unsigned int *refSegs, unsigned int nRefSegs) {
JBIG2Bitmap *bitmap;
JBIG2HuffmanTable runLengthTab[36];
JBIG2HuffmanTable *symCodeTab = nullptr;
JBIG2HuffmanTable *huffFSTable, *huffDSTable, *huffDTTable;
JBIG2HuffmanTable *huffRDWTable, *huffRDHTable;
JBIG2HuffmanTable *huffRDXTable, *huffRDYTable, *huffRSizeTable;
JBIG2Segment *seg;
std::vector<JBIG2Segment*> *codeTables;
JBIG2SymbolDict *symbolDict;
JBIG2Bitmap **syms;
unsigned int w, h, x, y, segInfoFlags, extCombOp;
unsigned int flags, huff, refine, logStrips, refCorner, transposed;
unsigned int combOp, defPixel, templ;
int sOffset;
unsigned int huffFlags, huffFS, huffDS, huffDT;
unsigned int huffRDW, huffRDH, huffRDX, huffRDY, huffRSize;
unsigned int numInstances, numSyms, symCodeLen;
int atx[2], aty[2];
unsigned int i, k, kk;
int j = 0;
// 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 std::vector<JBIG2Segment*>();
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->push_back(seg);
}
} else {
error(errSyntaxError, curStr->getPos(), "Invalid segment reference in JBIG2 text region");
delete codeTables;
return;
}
}
i = numSyms;
if (i <= 1) {
symCodeLen = huff ? 1 : 0;
} else {
--i;
symCodeLen = 0;
// i = floor((numSyms-1) / 2^symCodeLen)
while (i > 0) {
++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 = nullptr; // make gcc happy
huffRDWTable = huffRDHTable = nullptr; // make gcc happy
huffRDXTable = huffRDYTable = huffRSizeTable = nullptr; // make gcc happy
i = 0;
if (huff) {
if (huffFS == 0) {
huffFSTable = huffTableF;
} else if (huffFS == 1) {
huffFSTable = huffTableG;
} else {
if (i >= codeTables->size()) {
goto codeTableError;
}
huffFSTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable();
}
if (huffDS == 0) {
huffDSTable = huffTableH;
} else if (huffDS == 1) {
huffDSTable = huffTableI;
} else if (huffDS == 2) {
huffDSTable = huffTableJ;
} else {
if (i >= codeTables->size()) {
goto codeTableError;
}
huffDSTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable();
}
if (huffDT == 0) {
huffDTTable = huffTableK;
} else if (huffDT == 1) {
huffDTTable = huffTableL;
} else if (huffDT == 2) {
huffDTTable = huffTableM;
} else {
if (i >= codeTables->size()) {
goto codeTableError;
}
huffDTTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable();
}
if (huffRDW == 0) {
huffRDWTable = huffTableN;
} else if (huffRDW == 1) {
huffRDWTable = huffTableO;
} else {
if (i >= codeTables->size()) {
goto codeTableError;
}
huffRDWTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable();
}
if (huffRDH == 0) {
huffRDHTable = huffTableN;
} else if (huffRDH == 1) {
huffRDHTable = huffTableO;
} else {
if (i >= codeTables->size()) {
goto codeTableError;
}
huffRDHTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable();
}
if (huffRDX == 0) {
huffRDXTable = huffTableN;
} else if (huffRDX == 1) {
huffRDXTable = huffTableO;
} else {
if (i >= codeTables->size()) {
goto codeTableError;
}
huffRDXTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable();
}
if (huffRDY == 0) {
huffRDYTable = huffTableN;
} else if (huffRDY == 1) {
huffRDYTable = huffTableO;
} else {
if (i >= codeTables->size()) {
goto codeTableError;
}
huffRDYTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable();
}
if (huffRSize == 0) {
huffRSizeTable = huffTableA;
} else {
if (i >= codeTables->size()) {
goto codeTableError;
}
huffRSizeTable =
((JBIG2CodeTable *)(*codeTables)[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;
if (!JBIG2HuffmanDecoder::buildTable(runLengthTab, 35)) {
huff = false;
}
}
if (huff) {
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) {
if (unlikely(i == 0)) ++i;
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;
if (!JBIG2HuffmanDecoder::buildTable(symCodeTab, numSyms)) {
huff = false;
gfree(symCodeTab);
symCodeTab = nullptr;
}
huffDecoder->reset();
// set up the arithmetic decoder
}
if (!huff) {
resetIntStats(symCodeLen);
arithDecoder->start();
}
if (refine) {
resetRefinementStats(templ, nullptr);
}
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);
if (bitmap) {
// 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->push_back(bitmap);
}
}
// clean up the Huffman decoder
if (huff) {
gfree(symCodeTab);
}
return;
codeTableError:
error(errSyntaxError, curStr->getPos(), "Missing code table in JBIG2 text region");
delete codeTables;
gfree(syms);
return;
eofError:
error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream");
return;
}
JBIG2Bitmap *JBIG2Stream::readTextRegion(bool huff, bool refine,
int w, int h,
unsigned int numInstances,
unsigned int logStrips,
int numSyms,
JBIG2HuffmanTable *symCodeTab,
unsigned int symCodeLen,
JBIG2Bitmap **syms,
unsigned int defPixel, unsigned int combOp,
unsigned int transposed, unsigned int refCorner,
int sOffset,
JBIG2HuffmanTable *huffFSTable,
JBIG2HuffmanTable *huffDSTable,
JBIG2HuffmanTable *huffDTTable,
JBIG2HuffmanTable *huffRDWTable,
JBIG2HuffmanTable *huffRDHTable,
JBIG2HuffmanTable *huffRDXTable,
JBIG2HuffmanTable *huffRDYTable,
JBIG2HuffmanTable *huffRSizeTable,
unsigned int templ,
int *atx, int *aty) {
JBIG2Bitmap *bitmap;
JBIG2Bitmap *symbolBitmap;
unsigned int strips;
int t = 0, dt = 0, tt, s, ds = 0, sFirst, j = 0;
int rdw, rdh, rdx, rdy, ri = 0, refDX, refDY, bmSize;
unsigned int symID, inst, bw, bh;
strips = 1 << logStrips;
// allocate the bitmap
bitmap = new JBIG2Bitmap(0, w, h);
if (!bitmap->isOk()) {
delete bitmap;
return nullptr;
}
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
// (this loop test is here to avoid an infinite loop with damaged
// JBIG2 streams where the normal loop exit doesn't get triggered)
while (inst < numInstances) {
// 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 = (unsigned int)j;
} else {
symID = huffDecoder->readBits(symCodeLen);
}
} else {
symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);
}
if (symID >= (unsigned int)numSyms) {
error(errSyntaxError, curStr->getPos(), "Invalid symbol number in JBIG2 text region");
if (unlikely(numInstances - inst > 0x800)) {
// don't loop too often with damaged JBIg2 streams
delete bitmap;
return nullptr;
}
} else {
// get the symbol bitmap
symbolBitmap = nullptr;
if (refine) {
if (huff) {
ri = (int)huffDecoder->readBit();
} else {
arithDecoder->decodeInt(&ri, iariStats);
}
} else {
ri = 0;
}
if (ri) {
bool decodeSuccess;
if (huff) {
decodeSuccess = huffDecoder->decodeInt(&rdw, huffRDWTable);
decodeSuccess = decodeSuccess && huffDecoder->decodeInt(&rdh, huffRDHTable);
decodeSuccess = decodeSuccess && huffDecoder->decodeInt(&rdx, huffRDXTable);
decodeSuccess = decodeSuccess && huffDecoder->decodeInt(&rdy, huffRDYTable);
decodeSuccess = decodeSuccess && huffDecoder->decodeInt(&bmSize, huffRSizeTable);
huffDecoder->reset();
arithDecoder->start();
} else {
decodeSuccess = arithDecoder->decodeInt(&rdw, iardwStats);
decodeSuccess = decodeSuccess && arithDecoder->decodeInt(&rdh, iardhStats);
decodeSuccess = decodeSuccess && arithDecoder->decodeInt(&rdx, iardxStats);
decodeSuccess = decodeSuccess && arithDecoder->decodeInt(&rdy, iardyStats);
}
if (decodeSuccess && syms[symID])
{
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, false, syms[symID],
refDX, refDY, atx, aty);
}
//~ do we need to use the bmSize value here (in Huffman mode)?
} else {
symbolBitmap = syms[symID];
}
if (symbolBitmap) {
// combine the symbol bitmap into the region bitmap
//~ something is wrong here - refCorner shouldn't degenerate into
//~ two cases
bw = symbolBitmap->getWidth() - 1;
if (unlikely(symbolBitmap->getHeight() == 0)) {
error(errSyntaxError, curStr->getPos(), "Invalid symbol bitmap height");
if (ri) {
delete symbolBitmap;
}
delete bitmap;
return nullptr;
}
bh = symbolBitmap->getHeight() - 1;
if (transposed) {
if (unlikely(s > 2 * bitmap->getHeight())) {
error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine");
if (ri) {
delete symbolBitmap;
}
delete bitmap;
return nullptr;
}
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
if (unlikely(tt - (int) bh > 2 * bitmap->getHeight())) {
error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine");
if (ri) {
delete symbolBitmap;
}
delete bitmap;
return nullptr;
}
bitmap->combine(symbolBitmap, s, tt - bh, combOp);
break;
case 1: // top left
if (unlikely(tt > 2 * bitmap->getHeight())) {
error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine");
if (ri) {
delete symbolBitmap;
}
delete bitmap;
return nullptr;
}
bitmap->combine(symbolBitmap, s, tt, combOp);
break;
case 2: // bottom right
if (unlikely(tt - (int) bh > 2 * bitmap->getHeight())) {
error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine");
if (ri) {
delete symbolBitmap;
}
delete bitmap;
return nullptr;
}
bitmap->combine(symbolBitmap, s, tt - bh, combOp);
break;
case 3: // top right
if (unlikely(tt > 2 * bitmap->getHeight())) {
error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine");
if (ri) {
delete symbolBitmap;
}
delete bitmap;
return nullptr;
}
bitmap->combine(symbolBitmap, s, tt, combOp);
break;
}
s += bw;
}
if (ri) {
delete symbolBitmap;
}
} else {
// NULL symbolBitmap only happens on error
delete bitmap;
return nullptr;
}
}
// 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(unsigned int segNum, unsigned int length) {
JBIG2PatternDict *patternDict;
JBIG2Bitmap *bitmap;
unsigned int flags, patternW, patternH, grayMax, templ, mmr;
int atx[4], aty[4];
unsigned int 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, nullptr);
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, false, false, nullptr,
atx, aty, length - 7);
if (!bitmap)
return;
// create the pattern dict object
patternDict = new JBIG2PatternDict(segNum, grayMax + 1);
// split up the bitmap
x = 0;
for (i = 0; i <= grayMax && i < patternDict->getSize(); ++i) {
patternDict->setBitmap(i, bitmap->getSlice(x, 0, patternW, patternH));
x += patternW;
}
// free memory
delete bitmap;
// store the new pattern dict
segments->push_back(patternDict);
return;
eofError:
error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream");
}
void JBIG2Stream::readHalftoneRegionSeg(unsigned int segNum, bool imm,
bool lossless, unsigned int length,
unsigned int *refSegs, unsigned int nRefSegs) {
JBIG2Bitmap *bitmap;
JBIG2Segment *seg;
JBIG2PatternDict *patternDict;
JBIG2Bitmap *skipBitmap;
unsigned int *grayImg;
JBIG2Bitmap *grayBitmap;
JBIG2Bitmap *patternBitmap;
unsigned int w, h, x, y, segInfoFlags, extCombOp;
unsigned int flags, mmr, templ, enableSkip, combOp;
unsigned int gridW, gridH, stepX, stepY, patW, patH;
int atx[4], aty[4];
int gridX, gridY, xx, yy, bit, j;
unsigned int 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(errSyntaxError, curStr->getPos(), "Bad bitmap size in JBIG2 halftone segment");
return;
}
if (gridH == 0 || gridW >= INT_MAX / gridH) {
error(errSyntaxError, curStr->getPos(), "Bad grid size in JBIG2 halftone segment");
return;
}
// get pattern dictionary
if (nRefSegs != 1) {
error(errSyntaxError, curStr->getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");
return;
}
seg = findSegment(refSegs[0]);
if (seg == nullptr || seg->getType() != jbig2SegPatternDict) {
error(errSyntaxError, curStr->getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");
return;
}
patternDict = (JBIG2PatternDict *)seg;
i = patternDict->getSize();
if (i <= 1) {
bpp = 0;
} else {
--i;
bpp = 0;
// i = floor((size-1) / 2^bpp)
while (i > 0) {
++bpp;
i >>= 1;
}
}
patternBitmap = patternDict->getBitmap(0);
if (unlikely(patternBitmap == nullptr)) {
error(errSyntaxError, curStr->getPos(), "Bad pattern bitmap");
return;
}
patW = patternBitmap->getWidth();
patH = patternBitmap->getHeight();
// set up the arithmetic decoder
if (!mmr) {
resetGenericStats(templ, nullptr);
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 = nullptr;
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 = (unsigned int *)gmallocn(gridW * gridH, sizeof(unsigned int));
memset(grayImg, 0, gridW * gridH * sizeof(unsigned int));
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, false,
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]);
if (unlikely(patternBitmap == nullptr)) {
delete skipBitmap;
delete bitmap;
gfree(grayImg);
error(errSyntaxError, curStr->getPos(), "Bad pattern bitmap");
return;
}
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->push_back(bitmap);
}
return;
eofError:
error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream");
}
void JBIG2Stream::readGenericRegionSeg(unsigned int segNum, bool imm,
bool lossless, unsigned int length) {
JBIG2Bitmap *bitmap;
unsigned int w, h, x, y, segInfoFlags, extCombOp, rowCount;
unsigned int 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, nullptr);
arithDecoder->start();
}
// read the bitmap
bitmap = readGenericBitmap(mmr, w, h, templ, tpgdOn, false,
nullptr, atx, aty, mmr ? length - 18 : 0);
if (!bitmap)
return;
// 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->push_back(bitmap);
}
// immediate generic segments can have an unspecified length, in
// which case, a row count is stored at the end of the segment
if (imm && length == 0xffffffff) {
readULong(&rowCount);
}
return;
eofError:
error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream");
}
inline void JBIG2Stream::mmrAddPixels(int a1, int blackPixels,
int *codingLine, int *a0i, int w) {
if (a1 > codingLine[*a0i]) {
if (a1 > w) {
error(errSyntaxError, curStr->getPos(), "JBIG2 MMR row is wrong length ({0:d})", a1);
a1 = w;
}
if ((*a0i & 1) ^ blackPixels) {
++*a0i;
}
codingLine[*a0i] = a1;
}
}
inline void JBIG2Stream::mmrAddPixelsNeg(int a1, int blackPixels,
int *codingLine, int *a0i, int w) {
if (a1 > codingLine[*a0i]) {
if (a1 > w) {
error(errSyntaxError, curStr->getPos(), "JBIG2 MMR row is wrong length ({0:d})", a1);
a1 = w;
}
if ((*a0i & 1) ^ blackPixels) {
++*a0i;
}
codingLine[*a0i] = a1;
} else if (a1 < codingLine[*a0i]) {
if (a1 < 0) {
error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 MMR code");
a1 = 0;
}
while (*a0i > 0 && a1 <= codingLine[*a0i - 1]) {
--*a0i;
}
codingLine[*a0i] = a1;
}
}
JBIG2Bitmap *JBIG2Stream::readGenericBitmap(bool mmr, int w, int h,
int templ, bool tpgdOn,
bool useSkip, JBIG2Bitmap *skip,
int *atx, int *aty,
int mmrDataLength) {
JBIG2Bitmap *bitmap;
bool ltp;
unsigned int ltpCX, cx, cx0, cx1, cx2;
int *refLine, *codingLine;
int code1, code2, code3;
unsigned char *p0, *p1, *p2, *pp;
unsigned char *atP0, *atP1, *atP2, *atP3;
unsigned int buf0, buf1, buf2;
unsigned int atBuf0, atBuf1, atBuf2, atBuf3;
int atShift0, atShift1, atShift2, atShift3;
unsigned char mask;
int x, y, x0, x1, a0i, b1i, blackPixels, pix, i;
bitmap = new JBIG2Bitmap(0, w, h);
if (!bitmap->isOk()) {
delete bitmap;
return nullptr;
}
bitmap->clearToZero();
//----- MMR decode
if (mmr) {
mmrDecoder->reset();
// 0 <= codingLine[0] < codingLine[1] < ... < codingLine[n] = w
// ---> max codingLine size = w + 1
// refLine has one extra guard entry at the end
// ---> max refLine size = w + 2
codingLine = (int *)gmallocn_checkoverflow(w + 1, sizeof(int));
refLine = (int *)gmallocn_checkoverflow(w + 2, sizeof(int));
if (unlikely(!codingLine || !refLine)) {
error(errSyntaxError, curStr->getPos(), "Bad width in JBIG2 generic bitmap");
delete bitmap;
return nullptr;
}
memset(refLine, 0, (w + 2) * sizeof(int));
for (i = 0; i < w + 1; ++i) codingLine[i] = 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++] = w;
refLine[i] = w;
// decode a line
codingLine[0] = 0;
a0i = 0;
b1i = 0;
blackPixels = 0;
// invariant:
// refLine[b1i-1] <= codingLine[a0i] < refLine[b1i] < refLine[b1i+1] <= w
// exception at left edge:
// codingLine[a0i = 0] = refLine[b1i = 0] = 0 is possible
// exception at right edge:
// refLine[b1i] = refLine[b1i+1] = w is possible
while (codingLine[a0i] < w) {
code1 = mmrDecoder->get2DCode();
switch (code1) {
case twoDimPass:
if (unlikely(b1i + 1 >= w + 2)) {
break;
}
mmrAddPixels(refLine[b1i + 1], blackPixels, codingLine, &a0i, w);
if (refLine[b1i + 1] < w) {
b1i += 2;
}
break;
case twoDimHoriz:
code1 = code2 = 0;
if (blackPixels) {
do {
code1 += code3 = mmrDecoder->getBlackCode();
} while (code3 >= 64);
do {
code2 += code3 = mmrDecoder->getWhiteCode();
} while (code3 >= 64);
} else {
do {
code1 += code3 = mmrDecoder->getWhiteCode();
} while (code3 >= 64);
do {
code2 += code3 = mmrDecoder->getBlackCode();
} while (code3 >= 64);
}
mmrAddPixels(codingLine[a0i] + code1, blackPixels,
codingLine, &a0i, w);
if (codingLine[a0i] < w) {
mmrAddPixels(codingLine[a0i] + code2, blackPixels ^ 1,
codingLine, &a0i, w);
}
while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
b1i += 2;
}
break;
case twoDimVertR3:
if (unlikely(b1i >= w + 2)) {
break;
}
mmrAddPixels(refLine[b1i] + 3, blackPixels, codingLine, &a0i, w);
blackPixels ^= 1;
if (codingLine[a0i] < w) {
++b1i;
while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
b1i += 2;
}
}
break;
case twoDimVertR2:
if (unlikely(b1i >= w + 2)) {
break;
}
mmrAddPixels(refLine[b1i] + 2, blackPixels, codingLine, &a0i, w);
blackPixels ^= 1;
if (codingLine[a0i] < w) {
++b1i;
while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
b1i += 2;
}
}
break;
case twoDimVertR1:
if (unlikely(b1i >= w + 2)) {
break;
}
mmrAddPixels(refLine[b1i] + 1, blackPixels, codingLine, &a0i, w);
blackPixels ^= 1;
if (codingLine[a0i] < w) {
++b1i;
while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
b1i += 2;
}
}
break;
case twoDimVert0:
if (unlikely(b1i >= w + 2)) {
break;
}
mmrAddPixels(refLine[b1i], blackPixels, codingLine, &a0i, w);
blackPixels ^= 1;
if (codingLine[a0i] < w) {
++b1i;
while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
b1i += 2;
}
}
break;
case twoDimVertL3:
if (unlikely(b1i >= w + 2)) {
break;
}
mmrAddPixelsNeg(refLine[b1i] - 3, blackPixels, codingLine, &a0i, w);
blackPixels ^= 1;
if (codingLine[a0i] < w) {
if (b1i > 0) {
--b1i;
} else {
++b1i;
}
while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
b1i += 2;
}
}
break;
case twoDimVertL2:
if (unlikely(b1i >= w + 2)) {
break;
}
mmrAddPixelsNeg(refLine[b1i] - 2, blackPixels, codingLine, &a0i, w);
blackPixels ^= 1;
if (codingLine[a0i] < w) {
if (b1i > 0) {
--b1i;
} else {
++b1i;
}
while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
b1i += 2;
}
}
break;
case twoDimVertL1:
if (unlikely(b1i >= w + 2)) {
break;
}
mmrAddPixelsNeg(refLine[b1i] - 1, blackPixels, codingLine, &a0i, w);
blackPixels ^= 1;
if (codingLine[a0i] < w) {
if (b1i > 0) {
--b1i;
} else {
++b1i;
}
while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
b1i += 2;
}
}
break;
case EOF:
mmrAddPixels(w, 0, codingLine, &a0i, w);
break;
default:
error(errSyntaxError, curStr->getPos(), "Illegal code in JBIG2 MMR bitmap data");
mmrAddPixels(w, 0, codingLine, &a0i, w);
break;
}
}
// convert the run lengths to a bitmap line
i = 0;
while (1) {
for (x = codingLine[i]; x < codingLine[i+1]; ++x) {
bitmap->setPixel(x, y);
}
if (codingLine[i+1] >= w || codingLine[i+2] >= w) {
break;
}
i += 2;
}
}
if (mmrDataLength >= 0) {
mmrDecoder->skipTo(mmrDataLength);
} else {
if (mmrDecoder->get24Bits() != 0x001001) {
error(errSyntaxError, curStr->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) {
if (y > 0) {
bitmap->duplicateRow(y, y-1);
}
continue;
}
}
switch (templ) {
case 0:
// set up the context
p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize();
buf2 = *p2++ << 8;
if (y >= 1) {
p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize();
buf1 = *p1++ << 8;
if (y >= 2) {
p0 = bitmap->getDataPtr() + (y - 2) * bitmap->getLineSize();
buf0 = *p0++ << 8;
} else {
p0 = nullptr;
buf0 = 0;
}
} else {
p1 = p0 = nullptr;
buf1 = buf0 = 0;
}
if (atx[0] >= -8 && atx[0] <= 8 &&
atx[1] >= -8 && atx[1] <= 8 &&
atx[2] >= -8 && atx[2] <= 8 &&
atx[3] >= -8 && atx[3] <= 8) {
// set up the adaptive context
if (y + aty[0] >= 0 && y + aty[0] < bitmap->getHeight()) {
atP0 = bitmap->getDataPtr() + (y + aty[0]) * bitmap->getLineSize();
atBuf0 = *atP0++ << 8;
} else {
atP0 = nullptr;
atBuf0 = 0;
}
atShift0 = 15 - atx[0];
if (y + aty[1] >= 0 && y + aty[1] < bitmap->getHeight()) {
atP1 = bitmap->getDataPtr() + (y + aty[1]) * bitmap->getLineSize();
atBuf1 = *atP1++ << 8;
} else {
atP1 = nullptr;
atBuf1 = 0;
}
atShift1 = 15 - atx[1];
if (y + aty[2] >= 0 && y + aty[2] < bitmap->getHeight()) {
atP2 = bitmap->getDataPtr() + (y + aty[2]) * bitmap->getLineSize();
atBuf2 = *atP2++ << 8;
} else {
atP2 = nullptr;
atBuf2 = 0;
}
atShift2 = 15 - atx[2];
if (y + aty[3] >= 0 && y + aty[3] < bitmap->getHeight()) {
atP3 = bitmap->getDataPtr() + (y + aty[3]) * bitmap->getLineSize();
atBuf3 = *atP3++ << 8;
} else {
atP3 = nullptr;
atBuf3 = 0;
}
atShift3 = 15 - atx[3];
// decode the row
for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
if (x0 + 8 < w) {
if (p0) {
buf0 |= *p0++;
}
if (p1) {
buf1 |= *p1++;
}
buf2 |= *p2++;
if (atP0) {
atBuf0 |= *atP0++;
}
if (atP1) {
atBuf1 |= *atP1++;
}
if (atP2) {
atBuf2 |= *atP2++;
}
if (atP3) {
atBuf3 |= *atP3++;
}
}
for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) {
// build the context
cx0 = (buf0 >> 14) & 0x07;
cx1 = (buf1 >> 13) & 0x1f;
cx2 = (buf2 >> 16) & 0x0f;
cx = (cx0 << 13) | (cx1 << 8) | (cx2 << 4) |
(((atBuf0 >> atShift0) & 1) << 3) |
(((atBuf1 >> atShift1) & 1) << 2) |
(((atBuf2 >> atShift2) & 1) << 1) |
((atBuf3 >> atShift3) & 1);
// check for a skipped pixel
if (!(useSkip && skip->getPixel(x, y))) {
// decode the pixel
if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
*pp |= mask;
buf2 |= 0x8000;
if (aty[0] == 0) {
atBuf0 |= 0x8000;
}
if (aty[1] == 0) {
atBuf1 |= 0x8000;
}
if (aty[2] == 0) {
atBuf2 |= 0x8000;
}
if (aty[3] == 0) {
atBuf3 |= 0x8000;
}
}
}
// update the context
buf0 <<= 1;
buf1 <<= 1;
buf2 <<= 1;
atBuf0 <<= 1;
atBuf1 <<= 1;
atBuf2 <<= 1;
atBuf3 <<= 1;
}
}
} else {
// decode the row
for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
if (x0 + 8 < w) {
if (p0) {
buf0 |= *p0++;
}
if (p1) {
buf1 |= *p1++;
}
buf2 |= *p2++;
}
for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) {
// build the context
cx0 = (buf0 >> 14) & 0x07;
cx1 = (buf1 >> 13) & 0x1f;
cx2 = (buf2 >> 16) & 0x0f;
cx = (cx0 << 13) | (cx1 << 8) | (cx2 << 4) |
(bitmap->getPixel(x + atx[0], y + aty[0]) << 3) |
(bitmap->getPixel(x + atx[1], y + aty[1]) << 2) |
(bitmap->getPixel(x + atx[2], y + aty[2]) << 1) |
bitmap->getPixel(x + atx[3], y + aty[3]);
// check for a skipped pixel
if (!(useSkip && skip->getPixel(x, y))) {
// decode the pixel
if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
*pp |= mask;
buf2 |= 0x8000;
}
}
// update the context
buf0 <<= 1;
buf1 <<= 1;
buf2 <<= 1;
}
}
}
break;
case 1:
// set up the context
p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize();
buf2 = *p2++ << 8;
if (y >= 1) {
p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize();
buf1 = *p1++ << 8;
if (y >= 2) {
p0 = bitmap->getDataPtr() + (y - 2) * bitmap->getLineSize();
buf0 = *p0++ << 8;
} else {
p0 = nullptr;
buf0 = 0;
}
} else {
p1 = p0 = nullptr;
buf1 = buf0 = 0;
}
if (atx[0] >= -8 && atx[0] <= 8) {
// set up the adaptive context
const int atY = y + aty[0];
if ((atY >= 0) && (atY < bitmap->getHeight())) {
atP0 = bitmap->getDataPtr() + atY * bitmap->getLineSize();
atBuf0 = *atP0++ << 8;
} else {
atP0 = nullptr;
atBuf0 = 0;
}
atShift0 = 15 - atx[0];
// decode the row
for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
if (x0 + 8 < w) {
if (p0) {
buf0 |= *p0++;
}
if (p1) {
buf1 |= *p1++;
}
buf2 |= *p2++;
if (atP0) {
atBuf0 |= *atP0++;
}
}
for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) {
// build the context
cx0 = (buf0 >> 13) & 0x0f;
cx1 = (buf1 >> 13) & 0x1f;
cx2 = (buf2 >> 16) & 0x07;
cx = (cx0 << 9) | (cx1 << 4) | (cx2 << 1) |
((atBuf0 >> atShift0) & 1);
// check for a skipped pixel
if (!(useSkip && skip->getPixel(x, y))) {
// decode the pixel
if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
*pp |= mask;
buf2 |= 0x8000;
if (aty[0] == 0) {
atBuf0 |= 0x8000;
}
}
}
// update the context
buf0 <<= 1;
buf1 <<= 1;
buf2 <<= 1;
atBuf0 <<= 1;
}
}
} else {
// decode the row
for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
if (x0 + 8 < w) {
if (p0) {
buf0 |= *p0++;
}
if (p1) {
buf1 |= *p1++;
}
buf2 |= *p2++;
}
for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) {
// build the context
cx0 = (buf0 >> 13) & 0x0f;
cx1 = (buf1 >> 13) & 0x1f;
cx2 = (buf2 >> 16) & 0x07;
cx = (cx0 << 9) | (cx1 << 4) | (cx2 << 1) |
bitmap->getPixel(x + atx[0], y + aty[0]);
// check for a skipped pixel
if (!(useSkip && skip->getPixel(x, y))) {
// decode the pixel
if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
*pp |= mask;
buf2 |= 0x8000;
}
}
// update the context
buf0 <<= 1;
buf1 <<= 1;
buf2 <<= 1;
}
}
}
break;
case 2:
// set up the context
p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize();
buf2 = *p2++ << 8;
if (y >= 1) {
p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize();
buf1 = *p1++ << 8;
if (y >= 2) {
p0 = bitmap->getDataPtr() + (y - 2) * bitmap->getLineSize();
buf0 = *p0++ << 8;
} else {
p0 = nullptr;
buf0 = 0;
}
} else {
p1 = p0 = nullptr;
buf1 = buf0 = 0;
}
if (atx[0] >= -8 && atx[0] <= 8) {
// set up the adaptive context
const int atY = y + aty[0];
if ((atY >= 0) && (atY < bitmap->getHeight())) {
atP0 = bitmap->getDataPtr() + atY * bitmap->getLineSize();
atBuf0 = *atP0++ << 8;
} else {
atP0 = nullptr;
atBuf0 = 0;
}
atShift0 = 15 - atx[0];
// decode the row
for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
if (x0 + 8 < w) {
if (p0) {
buf0 |= *p0++;
}
if (p1) {
buf1 |= *p1++;
}
buf2 |= *p2++;
if (atP0) {
atBuf0 |= *atP0++;
}
}
for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) {
// build the context
cx0 = (buf0 >> 14) & 0x07;
cx1 = (buf1 >> 14) & 0x0f;
cx2 = (buf2 >> 16) & 0x03;
cx = (cx0 << 7) | (cx1 << 3) | (cx2 << 1) |
((atBuf0 >> atShift0) & 1);
// check for a skipped pixel
if (!(useSkip && skip->getPixel(x, y))) {
// decode the pixel
if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
*pp |= mask;
buf2 |= 0x8000;
if (aty[0] == 0) {
atBuf0 |= 0x8000;
}
}
}
// update the context
buf0 <<= 1;
buf1 <<= 1;
buf2 <<= 1;
atBuf0 <<= 1;
}
}
} else {
// decode the row
for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
if (x0 + 8 < w) {
if (p0) {
buf0 |= *p0++;
}
if (p1) {
buf1 |= *p1++;
}
buf2 |= *p2++;
}
for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) {
// build the context
cx0 = (buf0 >> 14) & 0x07;
cx1 = (buf1 >> 14) & 0x0f;
cx2 = (buf2 >> 16) & 0x03;
cx = (cx0 << 7) | (cx1 << 3) | (cx2 << 1) |
bitmap->getPixel(x + atx[0], y + aty[0]);
// check for a skipped pixel
if (!(useSkip && skip->getPixel(x, y))) {
// decode the pixel
if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
*pp |= mask;
buf2 |= 0x8000;
}
}
// update the context
buf0 <<= 1;
buf1 <<= 1;
buf2 <<= 1;
}
}
}
break;
case 3:
// set up the context
p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize();
buf2 = *p2++ << 8;
if (y >= 1) {
p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize();
buf1 = *p1++ << 8;
} else {
p1 = nullptr;
buf1 = 0;
}
if (atx[0] >= -8 && atx[0] <= 8) {
// set up the adaptive context
const int atY = y + aty[0];
if ((atY >= 0) && (atY < bitmap->getHeight())) {
atP0 = bitmap->getDataPtr() + atY * bitmap->getLineSize();
atBuf0 = *atP0++ << 8;
} else {
atP0 = nullptr;
atBuf0 = 0;
}
atShift0 = 15 - atx[0];
// decode the row
for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
if (x0 + 8 < w) {
if (p1) {
buf1 |= *p1++;
}
buf2 |= *p2++;
if (atP0) {
atBuf0 |= *atP0++;
}
}
for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) {
// build the context
cx1 = (buf1 >> 14) & 0x1f;
cx2 = (buf2 >> 16) & 0x0f;
cx = (cx1 << 5) | (cx2 << 1) |
((atBuf0 >> atShift0) & 1);
// check for a skipped pixel
if (!(useSkip && skip->getPixel(x, y))) {
// decode the pixel
if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
*pp |= mask;
buf2 |= 0x8000;
if (aty[0] == 0) {
atBuf0 |= 0x8000;
}
}
}
// update the context
buf1 <<= 1;
buf2 <<= 1;
atBuf0 <<= 1;
}
}
} else {
// decode the row
for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
if (x0 + 8 < w) {
if (p1) {
buf1 |= *p1++;
}
buf2 |= *p2++;
}
for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) {
// build the context
cx1 = (buf1 >> 14) & 0x1f;
cx2 = (buf2 >> 16) & 0x0f;
cx = (cx1 << 5) | (cx2 << 1) |
bitmap->getPixel(x + atx[0], y + aty[0]);
// check for a skipped pixel
if (!(useSkip && skip->getPixel(x, y))) {
// decode the pixel
if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
*pp |= mask;
buf2 |= 0x8000;
}
}
// update the context
buf1 <<= 1;
buf2 <<= 1;
}
}
}
break;
}
}
}
return bitmap;
}
void JBIG2Stream::readGenericRefinementRegionSeg(unsigned int segNum, bool imm,
bool lossless, unsigned int length,
unsigned int *refSegs,
unsigned int nRefSegs) {
JBIG2Bitmap *bitmap, *refBitmap;
unsigned int w, h, x, y, segInfoFlags, extCombOp;
unsigned int 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(errSyntaxError, curStr->getPos(), "Bad reference in JBIG2 generic refinement segment");
return;
}
if (nRefSegs == 1) {
seg = findSegment(refSegs[0]);
if (seg == nullptr || seg->getType() != jbig2SegBitmap) {
error(errSyntaxError, curStr->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, nullptr);
arithDecoder->start();
// read
bitmap = readGenericRefinementRegion(w, h, templ, tpgrOn,
refBitmap, 0, 0, atx, aty);
// combine the region bitmap into the page bitmap
if (imm && bitmap) {
pageBitmap->combine(bitmap, x, y, extCombOp);
delete bitmap;
// store the region bitmap
} else {
if (bitmap) {
bitmap->setSegNum(segNum);
segments->push_back(bitmap);
} else {
error(errSyntaxError, curStr->getPos(), "readGenericRefinementRegionSeg with null bitmap");
}
}
// delete the referenced bitmap
if (nRefSegs == 1) {
discardSegment(refSegs[0]);
} else {
delete refBitmap;
}
return;
eofError:
error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream");
}
JBIG2Bitmap *JBIG2Stream::readGenericRefinementRegion(int w, int h,
int templ, bool tpgrOn,
JBIG2Bitmap *refBitmap,
int refDX, int refDY,
int *atx, int *aty) {
JBIG2Bitmap *bitmap;
bool ltp;
unsigned int ltpCX, cx, cx0, cx2, cx3, cx4, tpgrCX0, tpgrCX1, tpgrCX2;
JBIG2BitmapPtr cxPtr0 = {nullptr, 0, 0};
JBIG2BitmapPtr cxPtr1 = {nullptr, 0, 0};
JBIG2BitmapPtr cxPtr2 = {nullptr, 0, 0};
JBIG2BitmapPtr cxPtr3 = {nullptr, 0, 0};
JBIG2BitmapPtr cxPtr4 = {nullptr, 0, 0};
JBIG2BitmapPtr cxPtr5 = {nullptr, 0, 0};
JBIG2BitmapPtr cxPtr6 = {nullptr, 0, 0};
JBIG2BitmapPtr tpgrCXPtr0 = {nullptr, 0, 0};
JBIG2BitmapPtr tpgrCXPtr1 = {nullptr, 0, 0};
JBIG2BitmapPtr tpgrCXPtr2 = {nullptr, 0, 0};
int x, y, pix;
if (!refBitmap) {
return nullptr;
}
bitmap = new JBIG2Bitmap(0, w, h);
if (!bitmap->isOk())
{
delete bitmap;
return nullptr;
}
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);
} else {
tpgrCXPtr0.p = tpgrCXPtr1.p = tpgrCXPtr2.p = nullptr; // make gcc happy
tpgrCXPtr0.shift = tpgrCXPtr1.shift = tpgrCXPtr2.shift = 0;
tpgrCXPtr0.x = tpgrCXPtr1.x = tpgrCXPtr2.x = 0;
}
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);
} else {
tpgrCXPtr0.p = tpgrCXPtr1.p = tpgrCXPtr2.p = nullptr; // make gcc happy
tpgrCXPtr0.shift = tpgrCXPtr1.shift = tpgrCXPtr2.shift = 0;
tpgrCXPtr0.x = tpgrCXPtr1.x = tpgrCXPtr2.x = 0;
}
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(unsigned int length) {
unsigned int 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;
}
delete pageBitmap;
pageBitmap = new JBIG2Bitmap(0, pageW, curPageH);
if (!pageBitmap->isOk()) {
delete pageBitmap;
pageBitmap = nullptr;
return;
}
// default pixel value
if (pageDefPixel) {
pageBitmap->clearToOne();
} else {
pageBitmap->clearToZero();
}
return;
eofError:
error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream");
}
void JBIG2Stream::readEndOfStripeSeg(unsigned int length) {
unsigned int i;
// skip the segment
for (i = 0; i < length; ++i) {
if (curStr->getChar() == EOF) {
break;
}
}
}
void JBIG2Stream::readProfilesSeg(unsigned int length) {
unsigned int i;
// skip the segment
for (i = 0; i < length; ++i) {
if (curStr->getChar() == EOF) {
break;
}
}
}
void JBIG2Stream::readCodeTableSeg(unsigned int segNum, unsigned int length) {
JBIG2HuffmanTable *huffTab;
unsigned int flags, oob, prefixBits, rangeBits;
int lowVal, highVal, val;
unsigned int 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_checkoverflow(huffTabSize, sizeof(JBIG2HuffmanTable));
if (unlikely(!huffTab)) {
goto oomError;
}
i = 0;
val = lowVal;
while (val < highVal) {
if (i == huffTabSize) {
huffTabSize *= 2;
huffTab = (JBIG2HuffmanTable *)greallocn_checkoverflow(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable));
if (unlikely(!huffTab)) {
goto oomError;
}
}
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_checkoverflow(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable));
if (unlikely(!huffTab)) {
goto oomError;
}
}
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;
if (JBIG2HuffmanDecoder::buildTable(huffTab, i)) {
// create and store the new table segment
segments->push_back(new JBIG2CodeTable(segNum, huffTab));
} else {
free(huffTab);
}
return;
eofError:
error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream");
oomError:
error(errInternal, curStr->getPos(), "Failed allocation when processing JBIG2 stream");
}
void JBIG2Stream::readExtensionSeg(unsigned int length) {
unsigned int i;
// skip the segment
for (i = 0; i < length; ++i) {
if (curStr->getChar() == EOF) {
break;
}
}
}
JBIG2Segment *JBIG2Stream::findSegment(unsigned int segNum) {
JBIG2Segment *seg;
for (std::size_t i = 0; i < globalSegments->size(); ++i) {
seg = (*globalSegments)[i];
if (seg->getSegNum() == segNum) {
return seg;
}
}
for (std::size_t i = 0; i < segments->size(); ++i) {
seg = (*segments)[i];
if (seg->getSegNum() == segNum) {
return seg;
}
}
return nullptr;
}
void JBIG2Stream::discardSegment(unsigned int segNum) {
for (auto it = globalSegments->begin(); it != globalSegments->end(); ++it) {
auto seg = static_cast<JBIG2Segment *>(*it);
if (seg->getSegNum() == segNum) {
globalSegments->erase(it);
return;
}
}
for (auto it = segments->begin(); it != segments->end(); ++it) {
auto seg = static_cast<JBIG2Segment *>(*it);
if (seg->getSegNum() == segNum) {
segments->erase(it);
return;
}
}
}
void JBIG2Stream::resetGenericStats(unsigned int 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(unsigned int 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));
}
}
bool JBIG2Stream::readUByte(unsigned int *x) {
int c0;
if ((c0 = curStr->getChar()) == EOF) {
return false;
}
*x = (unsigned int)c0;
return true;
}
bool JBIG2Stream::readByte(int *x) {
int c0;
if ((c0 = curStr->getChar()) == EOF) {
return false;
}
*x = c0;
if (c0 & 0x80) {
*x |= -1 - 0xff;
}
return true;
}
bool JBIG2Stream::readUWord(unsigned int *x) {
int c0, c1;
if ((c0 = curStr->getChar()) == EOF ||
(c1 = curStr->getChar()) == EOF) {
return false;
}
*x = (unsigned int)((c0 << 8) | c1);
return true;
}
bool JBIG2Stream::readULong(unsigned 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 false;
}
*x = (unsigned int)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
return true;
}
bool 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 false;
}
*x = ((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
if (c0 & 0x80) {
*x |= -1 - (int)0xffffffff;
}
return true;
}