blob: 37dcfd5233b9cc1e04cfd15242d19d1459974943 [file] [log] [blame]
//========================================================================
//
// Stream.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================
#include <config.h>
#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <limits.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <string.h>
#include <ctype.h>
#include "goo/gmem.h"
#include "goo/gfile.h"
#include "poppler-config.h"
#include "Error.h"
#include "Object.h"
#ifndef NO_DECRYPTION
#include "Decrypt.h"
#endif
#include "Stream.h"
#include "JBIG2Stream.h"
#include "JPXStream.h"
#include "Stream-CCITT.h"
#ifdef ENABLE_LIBJPEG
#include "DCTStream.h"
#endif
#ifdef ENABLE_ZLIB
#include "FlateStream.h"
#endif
#ifdef __DJGPP__
static GBool setDJSYSFLAGS = gFalse;
#endif
#ifdef VMS
#ifdef __GNUC__
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#endif
#endif
//------------------------------------------------------------------------
// Stream (base class)
//------------------------------------------------------------------------
Stream::Stream() {
ref = 1;
}
Stream::~Stream() {
}
void Stream::close() {
}
int Stream::getRawChar() {
error(-1, "Internal: called getRawChar() on non-predictor stream");
return EOF;
}
char *Stream::getLine(char *buf, int size) {
int i;
int c;
if (lookChar() == EOF)
return NULL;
for (i = 0; i < size - 1; ++i) {
c = getChar();
if (c == EOF || c == '\n')
break;
if (c == '\r') {
if ((c = lookChar()) == '\n')
getChar();
break;
}
buf[i] = c;
}
buf[i] = '\0';
return buf;
}
GooString *Stream::getPSFilter(int psLevel, char *indent) {
return new GooString();
}
Stream *Stream::addFilters(Object *dict) {
Object obj, obj2;
Object params, params2;
Stream *str;
int i;
str = this;
dict->dictLookup("Filter", &obj);
if (obj.isNull()) {
obj.free();
dict->dictLookup("F", &obj);
}
dict->dictLookup("DecodeParms", &params);
if (params.isNull()) {
params.free();
dict->dictLookup("DP", &params);
}
if (obj.isName()) {
str = makeFilter(obj.getName(), str, &params);
} else if (obj.isArray()) {
for (i = 0; i < obj.arrayGetLength(); ++i) {
obj.arrayGet(i, &obj2);
if (params.isArray())
params.arrayGet(i, &params2);
else
params2.initNull();
if (obj2.isName()) {
str = makeFilter(obj2.getName(), str, &params2);
} else {
error(getPos(), "Bad filter name");
str = new EOFStream(str);
}
obj2.free();
params2.free();
}
} else if (!obj.isNull()) {
error(getPos(), "Bad 'Filter' attribute in stream");
}
obj.free();
params.free();
return str;
}
Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
int pred; // parameters
int colors;
int bits;
int early;
int encoding;
GBool endOfLine, byteAlign, endOfBlock, black;
int columns, rows;
Object globals, obj;
if (!strcmp(name, "ASCIIHexDecode") || !strcmp(name, "AHx")) {
str = new ASCIIHexStream(str);
} else if (!strcmp(name, "ASCII85Decode") || !strcmp(name, "A85")) {
str = new ASCII85Stream(str);
} else if (!strcmp(name, "LZWDecode") || !strcmp(name, "LZW")) {
pred = 1;
columns = 1;
colors = 1;
bits = 8;
early = 1;
if (params->isDict()) {
params->dictLookup("Predictor", &obj);
if (obj.isInt())
pred = obj.getInt();
obj.free();
params->dictLookup("Columns", &obj);
if (obj.isInt())
columns = obj.getInt();
obj.free();
params->dictLookup("Colors", &obj);
if (obj.isInt())
colors = obj.getInt();
obj.free();
params->dictLookup("BitsPerComponent", &obj);
if (obj.isInt())
bits = obj.getInt();
obj.free();
params->dictLookup("EarlyChange", &obj);
if (obj.isInt())
early = obj.getInt();
obj.free();
}
str = new LZWStream(str, pred, columns, colors, bits, early);
} else if (!strcmp(name, "RunLengthDecode") || !strcmp(name, "RL")) {
str = new RunLengthStream(str);
} else if (!strcmp(name, "CCITTFaxDecode") || !strcmp(name, "CCF")) {
encoding = 0;
endOfLine = gFalse;
byteAlign = gFalse;
columns = 1728;
rows = 0;
endOfBlock = gTrue;
black = gFalse;
if (params->isDict()) {
params->dictLookup("K", &obj);
if (obj.isInt()) {
encoding = obj.getInt();
}
obj.free();
params->dictLookup("EndOfLine", &obj);
if (obj.isBool()) {
endOfLine = obj.getBool();
}
obj.free();
params->dictLookup("EncodedByteAlign", &obj);
if (obj.isBool()) {
byteAlign = obj.getBool();
}
obj.free();
params->dictLookup("Columns", &obj);
if (obj.isInt()) {
columns = obj.getInt();
}
obj.free();
params->dictLookup("Rows", &obj);
if (obj.isInt()) {
rows = obj.getInt();
}
obj.free();
params->dictLookup("EndOfBlock", &obj);
if (obj.isBool()) {
endOfBlock = obj.getBool();
}
obj.free();
params->dictLookup("BlackIs1", &obj);
if (obj.isBool()) {
black = obj.getBool();
}
obj.free();
}
str = new CCITTFaxStream(str, encoding, endOfLine, byteAlign,
columns, rows, endOfBlock, black);
} else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) {
str = new DCTStream(str);
} else if (!strcmp(name, "FlateDecode") || !strcmp(name, "Fl")) {
pred = 1;
columns = 1;
colors = 1;
bits = 8;
if (params->isDict()) {
params->dictLookup("Predictor", &obj);
if (obj.isInt())
pred = obj.getInt();
obj.free();
params->dictLookup("Columns", &obj);
if (obj.isInt())
columns = obj.getInt();
obj.free();
params->dictLookup("Colors", &obj);
if (obj.isInt())
colors = obj.getInt();
obj.free();
params->dictLookup("BitsPerComponent", &obj);
if (obj.isInt())
bits = obj.getInt();
obj.free();
}
str = new FlateStream(str, pred, columns, colors, bits);
} else if (!strcmp(name, "JBIG2Decode")) {
if (params->isDict()) {
params->dictLookup("JBIG2Globals", &globals);
}
str = new JBIG2Stream(str, &globals);
globals.free();
} else if (!strcmp(name, "JPXDecode")) {
str = new JPXStream(str);
} else {
error(getPos(), "Unknown filter '%s'", name);
str = new EOFStream(str);
}
return str;
}
//------------------------------------------------------------------------
// BaseStream
//------------------------------------------------------------------------
BaseStream::BaseStream(Object *dictA) {
dict = *dictA;
#ifndef NO_DECRYPTION
decrypt = NULL;
#endif
}
BaseStream::~BaseStream() {
dict.free();
#ifndef NO_DECRYPTION
if (decrypt)
delete decrypt;
#endif
}
#ifndef NO_DECRYPTION
void BaseStream::doDecryption(Guchar *fileKey, int keyLength,
int objNum, int objGen) {
decrypt = new Decrypt(fileKey, keyLength, objNum, objGen);
}
#endif
//------------------------------------------------------------------------
// FilterStream
//------------------------------------------------------------------------
FilterStream::FilterStream(Stream *strA) {
str = strA;
}
FilterStream::~FilterStream() {
}
void FilterStream::close() {
str->close();
}
void FilterStream::setPos(Guint pos, int dir) {
error(-1, "Internal: called setPos() on FilterStream");
}
//------------------------------------------------------------------------
// ImageStream
//------------------------------------------------------------------------
ImageStream::ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA) {
int imgLineSize;
str = strA;
width = widthA;
nComps = nCompsA;
nBits = nBitsA;
nVals = width * nComps;
if (nBits == 1) {
imgLineSize = (nVals + 7) & ~7;
} else {
imgLineSize = nVals;
}
imgLine = (Guchar *)gmalloc(imgLineSize * sizeof(Guchar));
imgIdx = nVals;
}
ImageStream::~ImageStream() {
gfree(imgLine);
}
void ImageStream::reset() {
str->reset();
}
GBool ImageStream::getPixel(Guchar *pix) {
int i;
if (imgIdx >= nVals) {
getLine();
imgIdx = 0;
}
for (i = 0; i < nComps; ++i) {
pix[i] = imgLine[imgIdx++];
}
return gTrue;
}
Guchar *ImageStream::getLine() {
Gulong buf, bitMask;
int bits;
int c;
int i;
if (nBits == 1) {
for (i = 0; i < nVals; i += 8) {
c = str->getChar();
imgLine[i+0] = (Guchar)((c >> 7) & 1);
imgLine[i+1] = (Guchar)((c >> 6) & 1);
imgLine[i+2] = (Guchar)((c >> 5) & 1);
imgLine[i+3] = (Guchar)((c >> 4) & 1);
imgLine[i+4] = (Guchar)((c >> 3) & 1);
imgLine[i+5] = (Guchar)((c >> 2) & 1);
imgLine[i+6] = (Guchar)((c >> 1) & 1);
imgLine[i+7] = (Guchar)(c & 1);
}
} else if (nBits == 8) {
for (i = 0; i < nVals; ++i) {
imgLine[i] = str->getChar();
}
} else {
bitMask = (1 << nBits) - 1;
buf = 0;
bits = 0;
for (i = 0; i < nVals; ++i) {
if (bits < nBits) {
buf = (buf << 8) | (str->getChar() & 0xff);
bits += 8;
}
imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask);
bits -= nBits;
}
}
return imgLine;
}
void ImageStream::skipLine() {
int n, i;
n = (nVals * nBits + 7) >> 3;
for (i = 0; i < n; ++i) {
str->getChar();
}
}
//------------------------------------------------------------------------
// StreamPredictor
//------------------------------------------------------------------------
StreamPredictor::StreamPredictor(Stream *strA, int predictorA,
int widthA, int nCompsA, int nBitsA) {
str = strA;
predictor = predictorA;
width = widthA;
nComps = nCompsA;
nBits = nBitsA;
predLine = NULL;
ok = gFalse;
if (width <= 0 || nComps <= 0 || nBits <= 0 ||
nComps >= INT_MAX/nBits ||
width >= INT_MAX/nComps/nBits ||
nVals * nBits + 7 < 0) {
return;
}
nVals = width * nComps;
if (nVals * nBits + 7 <= 0) {
return;
}
pixBytes = (nComps * nBits + 7) >> 3;
rowBytes = ((nVals * nBits + 7) >> 3) + pixBytes;
if (rowBytes < 0) {
return;
}
predLine = (Guchar *)gmalloc(rowBytes);
memset(predLine, 0, rowBytes);
predIdx = rowBytes;
ok = gTrue;
}
StreamPredictor::~StreamPredictor() {
gfree(predLine);
}
int StreamPredictor::lookChar() {
if (predIdx >= rowBytes) {
if (!getNextLine()) {
return EOF;
}
}
return predLine[predIdx];
}
int StreamPredictor::getChar() {
if (predIdx >= rowBytes) {
if (!getNextLine()) {
return EOF;
}
}
return predLine[predIdx++];
}
GBool StreamPredictor::getNextLine() {
int curPred;
Guchar upLeftBuf[4];
int left, up, upLeft, p, pa, pb, pc;
int c;
Gulong inBuf, outBuf, bitMask;
int inBits, outBits;
int i, j, k;
// get PNG optimum predictor number
if (predictor >= 10) {
if ((curPred = str->getRawChar()) == EOF) {
return gFalse;
}
curPred += 10;
} else {
curPred = predictor;
}
// read the raw line, apply PNG (byte) predictor
upLeftBuf[0] = upLeftBuf[1] = upLeftBuf[2] = upLeftBuf[3] = 0;
for (i = pixBytes; i < rowBytes; ++i) {
upLeftBuf[3] = upLeftBuf[2];
upLeftBuf[2] = upLeftBuf[1];
upLeftBuf[1] = upLeftBuf[0];
upLeftBuf[0] = predLine[i];
if ((c = str->getRawChar()) == EOF) {
return gFalse;
}
switch (curPred) {
case 11: // PNG sub
predLine[i] = predLine[i - pixBytes] + (Guchar)c;
break;
case 12: // PNG up
predLine[i] = predLine[i] + (Guchar)c;
break;
case 13: // PNG average
predLine[i] = ((predLine[i - pixBytes] + predLine[i]) >> 1) +
(Guchar)c;
break;
case 14: // PNG Paeth
left = predLine[i - pixBytes];
up = predLine[i];
upLeft = upLeftBuf[pixBytes];
p = left + up - upLeft;
if ((pa = p - left) < 0)
pa = -pa;
if ((pb = p - up) < 0)
pb = -pb;
if ((pc = p - upLeft) < 0)
pc = -pc;
if (pa <= pb && pa <= pc)
predLine[i] = left + (Guchar)c;
else if (pb <= pc)
predLine[i] = up + (Guchar)c;
else
predLine[i] = upLeft + (Guchar)c;
break;
case 10: // PNG none
default: // no predictor or TIFF predictor
predLine[i] = (Guchar)c;
break;
}
}
// apply TIFF (component) predictor
if (predictor == 2) {
if (nBits == 1) {
inBuf = predLine[pixBytes - 1];
for (i = pixBytes; i < rowBytes; i += 8) {
// 1-bit add is just xor
inBuf = (inBuf << 8) | predLine[i];
predLine[i] ^= inBuf >> nComps;
}
} else if (nBits == 8) {
for (i = pixBytes; i < rowBytes; ++i) {
predLine[i] += predLine[i - nComps];
}
} else {
upLeftBuf[0] = upLeftBuf[1] = upLeftBuf[2] = upLeftBuf[3] = 0;
bitMask = (1 << nBits) - 1;
inBuf = outBuf = 0;
inBits = outBits = 0;
j = k = pixBytes;
for (i = 0; i < nVals; ++i) {
if (inBits < nBits) {
inBuf = (inBuf << 8) | (predLine[j++] & 0xff);
inBits += 8;
}
upLeftBuf[3] = upLeftBuf[2];
upLeftBuf[2] = upLeftBuf[1];
upLeftBuf[1] = upLeftBuf[0];
upLeftBuf[0] = (upLeftBuf[nComps] +
(inBuf >> (inBits - nBits))) & bitMask;
outBuf = (outBuf << nBits) | upLeftBuf[0];
inBits -= nBits;
outBits += nBits;
if (outBits > 8) {
predLine[k++] = (Guchar)(outBuf >> (outBits - 8));
}
}
if (outBits > 0) {
predLine[k++] = (Guchar)(outBuf << (8 - outBits));
}
}
}
// reset to start of line
predIdx = pixBytes;
return gTrue;
}
//------------------------------------------------------------------------
// FileStream
//------------------------------------------------------------------------
FileStream::FileStream(FILE *fA, Guint startA, GBool limitedA,
Guint lengthA, Object *dictA):
BaseStream(dictA) {
f = fA;
start = startA;
limited = limitedA;
length = lengthA;
bufPtr = bufEnd = buf;
bufPos = start;
savePos = 0;
saved = gFalse;
}
FileStream::~FileStream() {
close();
}
Stream *FileStream::makeSubStream(Guint startA, GBool limitedA,
Guint lengthA, Object *dictA) {
return new FileStream(f, startA, limitedA, lengthA, dictA);
}
void FileStream::reset() {
#if HAVE_FSEEKO
savePos = (Guint)ftello(f);
fseeko(f, start, SEEK_SET);
#elif HAVE_FSEEK64
savePos = (Guint)ftell64(f);
fseek64(f, start, SEEK_SET);
#else
savePos = (Guint)ftell(f);
fseek(f, start, SEEK_SET);
#endif
saved = gTrue;
bufPtr = bufEnd = buf;
bufPos = start;
#ifndef NO_DECRYPTION
if (decrypt)
decrypt->reset();
#endif
}
void FileStream::close() {
if (saved) {
#if HAVE_FSEEKO
fseeko(f, savePos, SEEK_SET);
#elif HAVE_FSEEK64
fseek64(f, savePos, SEEK_SET);
#else
fseek(f, savePos, SEEK_SET);
#endif
saved = gFalse;
}
}
GBool FileStream::fillBuf() {
int n;
#ifndef NO_DECRYPTION
char *p;
#endif
bufPos += bufEnd - buf;
bufPtr = bufEnd = buf;
if (limited && bufPos >= start + length) {
return gFalse;
}
if (limited && bufPos + fileStreamBufSize > start + length) {
n = start + length - bufPos;
} else {
n = fileStreamBufSize;
}
n = fread(buf, 1, n, f);
bufEnd = buf + n;
if (bufPtr >= bufEnd) {
return gFalse;
}
#ifndef NO_DECRYPTION
if (decrypt) {
for (p = buf; p < bufEnd; ++p) {
*p = (char)decrypt->decryptByte((Guchar)*p);
}
}
#endif
return gTrue;
}
void FileStream::setPos(Guint pos, int dir) {
Guint size;
if (dir >= 0) {
#if HAVE_FSEEKO
fseeko(f, pos, SEEK_SET);
#elif HAVE_FSEEK64
fseek64(f, pos, SEEK_SET);
#else
fseek(f, pos, SEEK_SET);
#endif
bufPos = pos;
} else {
#if HAVE_FSEEKO
fseeko(f, 0, SEEK_END);
size = (Guint)ftello(f);
#elif HAVE_FSEEK64
fseek64(f, 0, SEEK_END);
size = (Guint)ftell64(f);
#else
fseek(f, 0, SEEK_END);
size = (Guint)ftell(f);
#endif
if (pos > size)
pos = (Guint)size;
#ifdef __CYGWIN32__
//~ work around a bug in cygwin's implementation of fseek
rewind(f);
#endif
#if HAVE_FSEEKO
fseeko(f, -(int)pos, SEEK_END);
bufPos = (Guint)ftello(f);
#elif HAVE_FSEEK64
fseek64(f, -(int)pos, SEEK_END);
bufPos = (Guint)ftell64(f);
#else
fseek(f, -(int)pos, SEEK_END);
bufPos = (Guint)ftell(f);
#endif
}
bufPtr = bufEnd = buf;
}
void FileStream::moveStart(int delta) {
start += delta;
bufPtr = bufEnd = buf;
bufPos = start;
}
//------------------------------------------------------------------------
// MemStream
//------------------------------------------------------------------------
MemStream::MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA):
BaseStream(dictA) {
buf = bufA;
start = startA;
length = lengthA;
bufEnd = buf + start + length;
bufPtr = buf + start;
needFree = gFalse;
}
MemStream::~MemStream() {
if (needFree) {
gfree(buf);
}
}
Stream *MemStream::makeSubStream(Guint startA, GBool limited,
Guint lengthA, Object *dictA) {
MemStream *subStr;
Guint newLength;
if (!limited || startA + lengthA > start + length) {
newLength = start + length - startA;
} else {
newLength = lengthA;
}
subStr = new MemStream(buf, startA, newLength, dictA);
return subStr;
}
void MemStream::reset() {
bufPtr = buf + start;
#ifndef NO_DECRYPTION
if (decrypt) {
decrypt->reset();
}
#endif
}
void MemStream::close() {
}
void MemStream::setPos(Guint pos, int dir) {
Guint i;
if (dir >= 0) {
i = pos;
} else {
i = start + length - pos;
}
if (i < start) {
i = start;
} else if (i > start + length) {
i = start + length;
}
bufPtr = buf + i;
}
void MemStream::moveStart(int delta) {
start += delta;
bufPtr = buf + start;
}
#ifndef NO_DECRYPTION
void MemStream::doDecryption(Guchar *fileKey, int keyLength,
int objNum, int objGen) {
char *newBuf;
char *p, *q;
this->BaseStream::doDecryption(fileKey, keyLength, objNum, objGen);
if (decrypt) {
newBuf = (char *)gmalloc(length);
for (p = buf + start, q = newBuf; p < bufEnd; ++p, ++q) {
*q = (char)decrypt->decryptByte((Guchar)*p);
}
bufEnd = newBuf + length;
bufPtr = newBuf + (bufPtr - (buf + start));
start = 0;
buf = newBuf;
needFree = gTrue;
}
}
#endif
//------------------------------------------------------------------------
// EmbedStream
//------------------------------------------------------------------------
EmbedStream::EmbedStream(Stream *strA, Object *dictA,
GBool limitedA, Guint lengthA):
BaseStream(dictA) {
str = strA;
limited = limitedA;
length = lengthA;
}
EmbedStream::~EmbedStream() {
}
Stream *EmbedStream::makeSubStream(Guint start, GBool limitedA,
Guint lengthA, Object *dictA) {
error(-1, "Internal: called makeSubStream() on EmbedStream");
return NULL;
}
int EmbedStream::getChar() {
if (limited && !length) {
return EOF;
}
--length;
return str->getChar();
}
int EmbedStream::lookChar() {
if (limited && !length) {
return EOF;
}
return str->lookChar();
}
void EmbedStream::setPos(Guint pos, int dir) {
error(-1, "Internal: called setPos() on EmbedStream");
}
Guint EmbedStream::getStart() {
error(-1, "Internal: called getStart() on EmbedStream");
return 0;
}
void EmbedStream::moveStart(int delta) {
error(-1, "Internal: called moveStart() on EmbedStream");
}
//------------------------------------------------------------------------
// ASCIIHexStream
//------------------------------------------------------------------------
ASCIIHexStream::ASCIIHexStream(Stream *strA):
FilterStream(strA) {
buf = EOF;
eof = gFalse;
}
ASCIIHexStream::~ASCIIHexStream() {
delete str;
}
void ASCIIHexStream::reset() {
str->reset();
buf = EOF;
eof = gFalse;
}
int ASCIIHexStream::lookChar() {
int c1, c2, x;
if (buf != EOF)
return buf;
if (eof) {
buf = EOF;
return EOF;
}
do {
c1 = str->getChar();
} while (isspace(c1));
if (c1 == '>') {
eof = gTrue;
buf = EOF;
return buf;
}
do {
c2 = str->getChar();
} while (isspace(c2));
if (c2 == '>') {
eof = gTrue;
c2 = '0';
}
if (c1 >= '0' && c1 <= '9') {
x = (c1 - '0') << 4;
} else if (c1 >= 'A' && c1 <= 'F') {
x = (c1 - 'A' + 10) << 4;
} else if (c1 >= 'a' && c1 <= 'f') {
x = (c1 - 'a' + 10) << 4;
} else if (c1 == EOF) {
eof = gTrue;
x = 0;
} else {
error(getPos(), "Illegal character <%02x> in ASCIIHex stream", c1);
x = 0;
}
if (c2 >= '0' && c2 <= '9') {
x += c2 - '0';
} else if (c2 >= 'A' && c2 <= 'F') {
x += c2 - 'A' + 10;
} else if (c2 >= 'a' && c2 <= 'f') {
x += c2 - 'a' + 10;
} else if (c2 == EOF) {
eof = gTrue;
x = 0;
} else {
error(getPos(), "Illegal character <%02x> in ASCIIHex stream", c2);
}
buf = x & 0xff;
return buf;
}
GooString *ASCIIHexStream::getPSFilter(int psLevel, char *indent) {
GooString *s;
if (psLevel < 2) {
return NULL;
}
if (!(s = str->getPSFilter(psLevel, indent))) {
return NULL;
}
s->append(indent)->append("/ASCIIHexDecode filter\n");
return s;
}
GBool ASCIIHexStream::isBinary(GBool last) {
return str->isBinary(gFalse);
}
//------------------------------------------------------------------------
// ASCII85Stream
//------------------------------------------------------------------------
ASCII85Stream::ASCII85Stream(Stream *strA):
FilterStream(strA) {
index = n = 0;
eof = gFalse;
}
ASCII85Stream::~ASCII85Stream() {
delete str;
}
void ASCII85Stream::reset() {
str->reset();
index = n = 0;
eof = gFalse;
}
int ASCII85Stream::lookChar() {
int k;
Gulong t;
if (index >= n) {
if (eof)
return EOF;
index = 0;
do {
c[0] = str->getChar();
} while (c[0] == '\n' || c[0] == '\r');
if (c[0] == '~' || c[0] == EOF) {
eof = gTrue;
n = 0;
return EOF;
} else if (c[0] == 'z') {
b[0] = b[1] = b[2] = b[3] = 0;
n = 4;
} else {
for (k = 1; k < 5; ++k) {
do {
c[k] = str->getChar();
} while (c[k] == '\n' || c[k] == '\r');
if (c[k] == '~' || c[k] == EOF)
break;
}
n = k - 1;
if (k < 5 && (c[k] == '~' || c[k] == EOF)) {
for (++k; k < 5; ++k)
c[k] = 0x21 + 84;
eof = gTrue;
}
t = 0;
for (k = 0; k < 5; ++k)
t = t * 85 + (c[k] - 0x21);
for (k = 3; k >= 0; --k) {
b[k] = (int)(t & 0xff);
t >>= 8;
}
}
}
return b[index];
}
GooString *ASCII85Stream::getPSFilter(int psLevel, char *indent) {
GooString *s;
if (psLevel < 2) {
return NULL;
}
if (!(s = str->getPSFilter(psLevel, indent))) {
return NULL;
}
s->append(indent)->append("/ASCII85Decode filter\n");
return s;
}
GBool ASCII85Stream::isBinary(GBool last) {
return str->isBinary(gFalse);
}
//------------------------------------------------------------------------
// LZWStream
//------------------------------------------------------------------------
LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors,
int bits, int earlyA):
FilterStream(strA) {
if (predictor != 1) {
pred = new StreamPredictor(this, predictor, columns, colors, bits);
if (!pred->isOk()) {
delete pred;
pred = NULL;
}
} else {
pred = NULL;
}
early = earlyA;
eof = gFalse;
inputBits = 0;
clearTable();
}
LZWStream::~LZWStream() {
if (pred) {
delete pred;
}
delete str;
}
int LZWStream::getChar() {
if (pred) {
return pred->getChar();
}
if (eof) {
return EOF;
}
if (seqIndex >= seqLength) {
if (!processNextCode()) {
return EOF;
}
}
return seqBuf[seqIndex++];
}
int LZWStream::lookChar() {
if (pred) {
return pred->lookChar();
}
if (eof) {
return EOF;
}
if (seqIndex >= seqLength) {
if (!processNextCode()) {
return EOF;
}
}
return seqBuf[seqIndex];
}
int LZWStream::getRawChar() {
if (eof) {
return EOF;
}
if (seqIndex >= seqLength) {
if (!processNextCode()) {
return EOF;
}
}
return seqBuf[seqIndex++];
}
void LZWStream::reset() {
str->reset();
eof = gFalse;
inputBits = 0;
clearTable();
}
GBool LZWStream::processNextCode() {
int code;
int nextLength;
int i, j;
// check for EOF
if (eof) {
return gFalse;
}
// check for eod and clear-table codes
start:
code = getCode();
if (code == EOF || code == 257) {
eof = gTrue;
return gFalse;
}
if (code == 256) {
clearTable();
goto start;
}
if (nextCode >= 4097) {
error(getPos(), "Bad LZW stream - expected clear-table code");
clearTable();
}
// process the next code
nextLength = seqLength + 1;
if (code < 256) {
seqBuf[0] = code;
seqLength = 1;
} else if (code < nextCode) {
seqLength = table[code].length;
for (i = seqLength - 1, j = code; i > 0; --i) {
seqBuf[i] = table[j].tail;
j = table[j].head;
}
seqBuf[0] = j;
} else if (code == nextCode) {
seqBuf[seqLength] = newChar;
++seqLength;
} else {
error(getPos(), "Bad LZW stream - unexpected code");
eof = gTrue;
return gFalse;
}
newChar = seqBuf[0];
if (first) {
first = gFalse;
} else {
table[nextCode].length = nextLength;
table[nextCode].head = prevCode;
table[nextCode].tail = newChar;
++nextCode;
if (nextCode + early == 512)
nextBits = 10;
else if (nextCode + early == 1024)
nextBits = 11;
else if (nextCode + early == 2048)
nextBits = 12;
}
prevCode = code;
// reset buffer
seqIndex = 0;
return gTrue;
}
void LZWStream::clearTable() {
nextCode = 258;
nextBits = 9;
seqIndex = seqLength = 0;
first = gTrue;
}
int LZWStream::getCode() {
int c;
int code;
while (inputBits < nextBits) {
if ((c = str->getChar()) == EOF)
return EOF;
inputBuf = (inputBuf << 8) | (c & 0xff);
inputBits += 8;
}
code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1);
inputBits -= nextBits;
return code;
}
GooString *LZWStream::getPSFilter(int psLevel, char *indent) {
GooString *s;
if (psLevel < 2 || pred) {
return NULL;
}
if (!(s = str->getPSFilter(psLevel, indent))) {
return NULL;
}
s->append(indent)->append("/LZWDecode filter\n");
return s;
}
GBool LZWStream::isBinary(GBool last) {
return str->isBinary(gTrue);
}
//------------------------------------------------------------------------
// RunLengthStream
//------------------------------------------------------------------------
RunLengthStream::RunLengthStream(Stream *strA):
FilterStream(strA) {
bufPtr = bufEnd = buf;
eof = gFalse;
}
RunLengthStream::~RunLengthStream() {
delete str;
}
void RunLengthStream::reset() {
str->reset();
bufPtr = bufEnd = buf;
eof = gFalse;
}
GooString *RunLengthStream::getPSFilter(int psLevel, char *indent) {
GooString *s;
if (psLevel < 2) {
return NULL;
}
if (!(s = str->getPSFilter(psLevel, indent))) {
return NULL;
}
s->append(indent)->append("/RunLengthDecode filter\n");
return s;
}
GBool RunLengthStream::isBinary(GBool last) {
return str->isBinary(gTrue);
}
GBool RunLengthStream::fillBuf() {
int c;
int n, i;
if (eof)
return gFalse;
c = str->getChar();
if (c == 0x80 || c == EOF) {
eof = gTrue;
return gFalse;
}
if (c < 0x80) {
n = c + 1;
for (i = 0; i < n; ++i)
buf[i] = (char)str->getChar();
} else {
n = 0x101 - c;
c = str->getChar();
for (i = 0; i < n; ++i)
buf[i] = (char)c;
}
bufPtr = buf;
bufEnd = buf + n;
return gTrue;
}
//------------------------------------------------------------------------
// CCITTFaxStream
//------------------------------------------------------------------------
CCITTFaxStream::CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA,
GBool byteAlignA, int columnsA, int rowsA,
GBool endOfBlockA, GBool blackA):
FilterStream(strA) {
encoding = encodingA;
endOfLine = endOfLineA;
byteAlign = byteAlignA;
columns = columnsA;
if (columns < 1 || columns >= INT_MAX / sizeof(short)) {
error(-1, "invalid number of columns: %d", columns);
exit(1);
}
rows = rowsA;
endOfBlock = endOfBlockA;
black = blackA;
refLine = (short *)gmalloc((columns + 3) * sizeof(short));
codingLine = (short *)gmalloc((columns + 2) * sizeof(short));
eof = gFalse;
row = 0;
nextLine2D = encoding < 0;
inputBits = 0;
codingLine[0] = 0;
codingLine[1] = refLine[2] = columns;
a0 = 1;
buf = EOF;
}
CCITTFaxStream::~CCITTFaxStream() {
delete str;
gfree(refLine);
gfree(codingLine);
}
void CCITTFaxStream::reset() {
short code1;
str->reset();
eof = gFalse;
row = 0;
nextLine2D = encoding < 0;
inputBits = 0;
codingLine[0] = 0;
codingLine[1] = refLine[2] = columns;
a0 = 1;
buf = EOF;
// skip any initial zero bits and end-of-line marker, and get the 2D
// encoding tag
while ((code1 = lookBits(12)) == 0) {
eatBits(1);
}
if (code1 == 0x001) {
eatBits(12);
}
if (encoding > 0) {
nextLine2D = !lookBits(1);
eatBits(1);
}
}
int CCITTFaxStream::lookChar() {
short code1, code2, code3;
int a0New;
GBool err, gotEOL;
int ret;
int bits, i;
// if at eof just return EOF
if (eof && codingLine[a0] >= columns) {
return EOF;
}
// read the next row
err = gFalse;
if (codingLine[a0] >= columns) {
// 2-D encoding
if (nextLine2D) {
for (i = 0; codingLine[i] < columns; ++i)
refLine[i] = codingLine[i];
refLine[i] = refLine[i + 1] = columns;
b1 = 1;
a0New = codingLine[a0 = 0] = 0;
do {
code1 = getTwoDimCode();
switch (code1) {
case twoDimPass:
if (refLine[b1] < columns) {
a0New = refLine[b1 + 1];
b1 += 2;
}
break;
case twoDimHoriz:
if ((a0 & 1) == 0) {
code1 = code2 = 0;
do {
code1 += code3 = getWhiteCode();
} while (code3 >= 64);
do {
code2 += code3 = getBlackCode();
} while (code3 >= 64);
} else {
code1 = code2 = 0;
do {
code1 += code3 = getBlackCode();
} while (code3 >= 64);
do {
code2 += code3 = getWhiteCode();
} while (code3 >= 64);
}
if (code1 > 0 || code2 > 0) {
codingLine[a0 + 1] = a0New + code1;
++a0;
a0New = codingLine[a0 + 1] = codingLine[a0] + code2;
++a0;
while (refLine[b1] <= codingLine[a0] && refLine[b1] < columns)
b1 += 2;
}
break;
case twoDimVert0:
a0New = codingLine[++a0] = refLine[b1];
if (refLine[b1] < columns) {
++b1;
while (refLine[b1] <= codingLine[a0] && refLine[b1] < columns)
b1 += 2;
}
break;
case twoDimVertR1:
a0New = codingLine[++a0] = refLine[b1] + 1;
if (refLine[b1] < columns) {
++b1;
while (refLine[b1] <= codingLine[a0] && refLine[b1] < columns)
b1 += 2;
}
break;
case twoDimVertL1:
a0New = codingLine[++a0] = refLine[b1] - 1;
--b1;
while (refLine[b1] <= codingLine[a0] && refLine[b1] < columns)
b1 += 2;
break;
case twoDimVertR2:
a0New = codingLine[++a0] = refLine[b1] + 2;
if (refLine[b1] < columns) {
++b1;
while (refLine[b1] <= codingLine[a0] && refLine[b1] < columns)
b1 += 2;
}
break;
case twoDimVertL2:
a0New = codingLine[++a0] = refLine[b1] - 2;
--b1;
while (refLine[b1] <= codingLine[a0] && refLine[b1] < columns)
b1 += 2;
break;
case twoDimVertR3:
a0New = codingLine[++a0] = refLine[b1] + 3;
if (refLine[b1] < columns) {
++b1;
while (refLine[b1] <= codingLine[a0] && refLine[b1] < columns)
b1 += 2;
}
break;
case twoDimVertL3:
a0New = codingLine[++a0] = refLine[b1] - 3;
--b1;
while (refLine[b1] <= codingLine[a0] && refLine[b1] < columns)
b1 += 2;
break;
case EOF:
eof = gTrue;
codingLine[a0 = 0] = columns;
return EOF;
default:
error(getPos(), "Bad 2D code %04x in CCITTFax stream", code1);
err = gTrue;
break;
}
} while (codingLine[a0] < columns);
// 1-D encoding
} else {
codingLine[a0 = 0] = 0;
while (1) {
code1 = 0;
do {
code1 += code3 = getWhiteCode();
} while (code3 >= 64);
codingLine[a0+1] = codingLine[a0] + code1;
++a0;
if (codingLine[a0] >= columns)
break;
code2 = 0;
do {
code2 += code3 = getBlackCode();
} while (code3 >= 64);
codingLine[a0+1] = codingLine[a0] + code2;
++a0;
if (codingLine[a0] >= columns)
break;
}
}
if (codingLine[a0] != columns) {
error(getPos(), "CCITTFax row is wrong length (%d)", codingLine[a0]);
// force the row to be the correct length
while (codingLine[a0] > columns) {
--a0;
}
codingLine[++a0] = columns;
err = gTrue;
}
// byte-align the row
if (byteAlign) {
inputBits &= ~7;
}
// check for end-of-line marker, skipping over any extra zero bits
gotEOL = gFalse;
if (!endOfBlock && row == rows - 1) {
eof = gTrue;
} else {
code1 = lookBits(12);
while (code1 == 0) {
eatBits(1);
code1 = lookBits(12);
}
if (code1 == 0x001) {
eatBits(12);
gotEOL = gTrue;
} else if (code1 == EOF) {
eof = gTrue;
}
}
// get 2D encoding tag
if (!eof && encoding > 0) {
nextLine2D = !lookBits(1);
eatBits(1);
}
// check for end-of-block marker
if (endOfBlock && gotEOL) {
code1 = lookBits(12);
if (code1 == 0x001) {
eatBits(12);
if (encoding > 0) {
lookBits(1);
eatBits(1);
}
if (encoding >= 0) {
for (i = 0; i < 4; ++i) {
code1 = lookBits(12);
if (code1 != 0x001) {
error(getPos(), "Bad RTC code in CCITTFax stream");
}
eatBits(12);
if (encoding > 0) {
lookBits(1);
eatBits(1);
}
}
}
eof = gTrue;
}
// look for an end-of-line marker after an error -- we only do
// this if we know the stream contains end-of-line markers because
// the "just plow on" technique tends to work better otherwise
} else if (err && endOfLine) {
do {
if (code1 == EOF) {
eof = gTrue;
return EOF;
}
eatBits(1);
code1 = lookBits(13);
} while ((code1 >> 1) != 0x001);
eatBits(12);
if (encoding > 0) {
eatBits(1);
nextLine2D = !(code1 & 1);
}
}
a0 = 0;
outputBits = codingLine[1] - codingLine[0];
if (outputBits == 0) {
a0 = 1;
outputBits = codingLine[2] - codingLine[1];
}
++row;
}
// get a byte
if (outputBits >= 8) {
ret = ((a0 & 1) == 0) ? 0xff : 0x00;
if ((outputBits -= 8) == 0) {
++a0;
if (codingLine[a0] < columns) {
outputBits = codingLine[a0 + 1] - codingLine[a0];
}
}
} else {
bits = 8;
ret = 0;
do {
if (outputBits > bits) {
i = bits;
bits = 0;
if ((a0 & 1) == 0) {
ret |= 0xff >> (8 - i);
}
outputBits -= i;
} else {
i = outputBits;
bits -= outputBits;
if ((a0 & 1) == 0) {
ret |= (0xff >> (8 - i)) << bits;
}
outputBits = 0;
++a0;
if (codingLine[a0] < columns) {
outputBits = codingLine[a0 + 1] - codingLine[a0];
}
}
} while (bits > 0 && codingLine[a0] < columns);
}
buf = black ? (ret ^ 0xff) : ret;
return buf;
}
short CCITTFaxStream::getTwoDimCode() {
short code;
CCITTCode *p;
int n;
code = 0; // make gcc happy
if (endOfBlock) {
code = lookBits(7);
p = &twoDimTab1[code];
if (p->bits > 0) {
eatBits(p->bits);
return p->n;
}
} else {
for (n = 1; n <= 7; ++n) {
code = lookBits(n);
if (n < 7) {
code <<= 7 - n;
}
p = &twoDimTab1[code];
if (p->bits == n) {
eatBits(n);
return p->n;
}
}
}
error(getPos(), "Bad two dim code (%04x) in CCITTFax stream", code);
return EOF;
}
short CCITTFaxStream::getWhiteCode() {
short code;
CCITTCode *p;
int n;
code = 0; // make gcc happy
if (endOfBlock) {
code = lookBits(12);
if ((code >> 5) == 0) {
p = &whiteTab1[code];
} else {
p = &whiteTab2[code >> 3];
}
if (p->bits > 0) {
eatBits(p->bits);
return p->n;
}
} else {
for (n = 1; n <= 9; ++n) {
code = lookBits(n);
if (n < 9) {
code <<= 9 - n;
}
p = &whiteTab2[code];
if (p->bits == n) {
eatBits(n);
return p->n;
}
}
for (n = 11; n <= 12; ++n) {
code = lookBits(n);
if (n < 12) {
code <<= 12 - n;
}
p = &whiteTab1[code];
if (p->bits == n) {
eatBits(n);
return p->n;
}
}
}
error(getPos(), "Bad white code (%04x) in CCITTFax stream", code);
// eat a bit and return a positive number so that the caller doesn't
// go into an infinite loop
eatBits(1);
return 1;
}
short CCITTFaxStream::getBlackCode() {
short code;
CCITTCode *p;
int n;
code = 0; // make gcc happy
if (endOfBlock) {
code = lookBits(13);
if ((code >> 7) == 0) {
p = &blackTab1[code];
} else if ((code >> 9) == 0) {
p = &blackTab2[(code >> 1) - 64];
} else {
p = &blackTab3[code >> 7];
}
if (p->bits > 0) {
eatBits(p->bits);
return p->n;
}
} else {
for (n = 2; n <= 6; ++n) {
code = lookBits(n);
if (n < 6) {
code <<= 6 - n;
}
p = &blackTab3[code];
if (p->bits == n) {
eatBits(n);
return p->n;
}
}
for (n = 7; n <= 12; ++n) {
code = lookBits(n);
if (n < 12) {
code <<= 12 - n;
}
if (code >= 64) {
p = &blackTab2[code - 64];
if (p->bits == n) {
eatBits(n);
return p->n;
}
}
}
for (n = 10; n <= 13; ++n) {
code = lookBits(n);
if (n < 13) {
code <<= 13 - n;
}
p = &blackTab1[code];
if (p->bits == n) {
eatBits(n);
return p->n;
}
}
}
error(getPos(), "Bad black code (%04x) in CCITTFax stream", code);
// eat a bit and return a positive number so that the caller doesn't
// go into an infinite loop
eatBits(1);
return 1;
}
short CCITTFaxStream::lookBits(int n) {
int c;
while (inputBits < n) {
if ((c = str->getChar()) == EOF) {
if (inputBits == 0) {
return EOF;
}
// near the end of the stream, the caller may ask for more bits
// than are available, but there may still be a valid code in
// however many bits are available -- we need to return correct
// data in this case
return (inputBuf << (n - inputBits)) & (0xffff >> (16 - n));
}
inputBuf = (inputBuf << 8) + c;
inputBits += 8;
}
return (inputBuf >> (inputBits - n)) & (0xffff >> (16 - n));
}
GooString *CCITTFaxStream::getPSFilter(int psLevel, char *indent) {
GooString *s;
char s1[50];
if (psLevel < 2) {
return NULL;
}
if (!(s = str->getPSFilter(psLevel, indent))) {
return NULL;
}
s->append(indent)->append("<< ");
if (encoding != 0) {
sprintf(s1, "/K %d ", encoding);
s->append(s1);
}
if (endOfLine) {
s->append("/EndOfLine true ");
}
if (byteAlign) {
s->append("/EncodedByteAlign true ");
}
sprintf(s1, "/Columns %d ", columns);
s->append(s1);
if (rows != 0) {
sprintf(s1, "/Rows %d ", rows);
s->append(s1);
}
if (!endOfBlock) {
s->append("/EndOfBlock false ");
}
if (black) {
s->append("/BlackIs1 true ");
}
s->append(">> /CCITTFaxDecode filter\n");
return s;
}
GBool CCITTFaxStream::isBinary(GBool last) {
return str->isBinary(gTrue);
}
#ifndef ENABLE_LIBJPEG
//------------------------------------------------------------------------
// DCTStream
//------------------------------------------------------------------------
// IDCT constants (20.12 fixed point format)
#define dctCos1 4017 // cos(pi/16)
#define dctSin1 799 // sin(pi/16)
#define dctCos3 3406 // cos(3*pi/16)
#define dctSin3 2276 // sin(3*pi/16)
#define dctCos6 1567 // cos(6*pi/16)
#define dctSin6 3784 // sin(6*pi/16)
#define dctSqrt2 5793 // sqrt(2)
#define dctSqrt1d2 2896 // sqrt(2) / 2
// color conversion parameters (16.16 fixed point format)
#define dctCrToR 91881 // 1.4020
#define dctCbToG -22553 // -0.3441363
#define dctCrToG -46802 // -0.71413636
#define dctCbToB 116130 // 1.772
// clip [-256,511] --> [0,255]
#define dctClipOffset 256
static Guchar dctClip[768];
static int dctClipInit = 0;
// zig zag decode map
static int dctZigZag[64] = {
0,
1, 8,
16, 9, 2,
3, 10, 17, 24,
32, 25, 18, 11, 4,
5, 12, 19, 26, 33, 40,
48, 41, 34, 27, 20, 13, 6,
7, 14, 21, 28, 35, 42, 49, 56,
57, 50, 43, 36, 29, 22, 15,
23, 30, 37, 44, 51, 58,
59, 52, 45, 38, 31,
39, 46, 53, 60,
61, 54, 47,
55, 62,
63
};
DCTStream::DCTStream(Stream *strA):
FilterStream(strA) {
int i, j;
progressive = interleaved = gFalse;
width = height = 0;
mcuWidth = mcuHeight = 0;
numComps = 0;
comp = 0;
x = y = dy = 0;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 32; ++j) {
rowBuf[i][j] = NULL;
}
frameBuf[i] = NULL;
}
if (!dctClipInit) {
for (i = -256; i < 0; ++i)
dctClip[dctClipOffset + i] = 0;
for (i = 0; i < 256; ++i)
dctClip[dctClipOffset + i] = i;
for (i = 256; i < 512; ++i)
dctClip[dctClipOffset + i] = 255;
dctClipInit = 1;
}
}
DCTStream::~DCTStream() {
int i, j;
delete str;
if (progressive || !interleaved) {
for (i = 0; i < numComps; ++i) {
gfree(frameBuf[i]);
}
} else {
for (i = 0; i < numComps; ++i) {
for (j = 0; j < mcuHeight; ++j) {
gfree(rowBuf[i][j]);
}
}
}
}
void DCTStream::reset() {
int minHSample, minVSample;
int i, j;
str->reset();
progressive = interleaved = gFalse;
width = height = 0;
numComps = 0;
numQuantTables = 0;
numDCHuffTables = 0;
numACHuffTables = 0;
colorXform = 0;
gotJFIFMarker = gFalse;
gotAdobeMarker = gFalse;
restartInterval = 0;
if (!readHeader()) {
y = height;
return;
}
// compute MCU size
mcuWidth = minHSample = compInfo[0].hSample;
mcuHeight = minVSample = compInfo[0].vSample;
for (i = 1; i < numComps; ++i) {
if (compInfo[i].hSample < minHSample)
minHSample = compInfo[i].hSample;
if (compInfo[i].vSample < minVSample)
minVSample = compInfo[i].vSample;
if (compInfo[i].hSample > mcuWidth)
mcuWidth = compInfo[i].hSample;
if (compInfo[i].vSample > mcuHeight)
mcuHeight = compInfo[i].vSample;
}
for (i = 0; i < numComps; ++i) {
compInfo[i].hSample /= minHSample;
compInfo[i].vSample /= minVSample;
}
mcuWidth = (mcuWidth / minHSample) * 8;
mcuHeight = (mcuHeight / minVSample) * 8;
// figure out color transform
if (!gotAdobeMarker && numComps == 3) {
if (gotJFIFMarker) {
colorXform = 1;
} else if (compInfo[0].id == 82 && compInfo[1].id == 71 &&
compInfo[2].id == 66) { // ASCII "RGB"
colorXform = 0;
} else {
colorXform = 1;
}
}
if (progressive || !interleaved) {
// allocate a buffer for the whole image
bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
bufHeight = ((height + mcuHeight - 1) / mcuHeight) * mcuHeight;
for (i = 0; i < numComps; ++i) {
frameBuf[i] = (int *)gmalloc(bufWidth * bufHeight * sizeof(int));
memset(frameBuf[i], 0, bufWidth * bufHeight * sizeof(int));
}
// read the image data
do {
restartMarker = 0xd0;
restart();
readScan();
} while (readHeader());
// decode
decodeImage();
// initialize counters
comp = 0;
x = 0;
y = 0;
} else {
// allocate a buffer for one row of MCUs
bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
for (i = 0; i < numComps; ++i) {
for (j = 0; j < mcuHeight; ++j) {
rowBuf[i][j] = (Guchar *)gmalloc(bufWidth * sizeof(Guchar));
}
}
// initialize counters
comp = 0;
x = 0;
y = 0;
dy = mcuHeight;
restartMarker = 0xd0;
restart();
}
}
int DCTStream::getChar() {
int c;
if (y >= height) {
return EOF;
}
if (progressive || !interleaved) {
c = frameBuf[comp][y * bufWidth + x];
if (++comp == numComps) {
comp = 0;
if (++x == width) {
x = 0;
++y;
}
}
} else {
if (dy >= mcuHeight) {
if (!readMCURow()) {
y = height;
return EOF;
}
comp = 0;
x = 0;
dy = 0;
}
c = rowBuf[comp][dy][x];
if (++comp == numComps) {
comp = 0;
if (++x == width) {
x = 0;
++y;
++dy;
if (y == height) {
readTrailer();
}
}
}
}
return c;
}
int DCTStream::lookChar() {
if (y >= height) {
return EOF;
}
if (progressive || !interleaved) {
return frameBuf[comp][y * bufWidth + x];
} else {
if (dy >= mcuHeight) {
if (!readMCURow()) {
y = height;
return EOF;
}
comp = 0;
x = 0;
dy = 0;
}
return rowBuf[comp][dy][x];
}
}
void DCTStream::restart() {
int i;
inputBits = 0;
restartCtr = restartInterval;
for (i = 0; i < numComps; ++i) {
compInfo[i].prevDC = 0;
}
eobRun = 0;
}
// Read one row of MCUs from a sequential JPEG stream.
GBool DCTStream::readMCURow() {
int data1[64];
Guchar data2[64];
Guchar *p1, *p2;
int pY, pCb, pCr, pR, pG, pB;
int h, v, horiz, vert, hSub, vSub;
int x1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i;
int c;
for (x1 = 0; x1 < width; x1 += mcuWidth) {
// deal with restart marker
if (restartInterval > 0 && restartCtr == 0) {
c = readMarker();
if (c != restartMarker) {
error(getPos(), "Bad DCT data: incorrect restart marker");
return gFalse;
}
if (++restartMarker == 0xd8)
restartMarker = 0xd0;
restart();
}
// read one MCU
for (cc = 0; cc < numComps; ++cc) {
h = compInfo[cc].hSample;
v = compInfo[cc].vSample;
horiz = mcuWidth / h;
vert = mcuHeight / v;
hSub = horiz / 8;
vSub = vert / 8;
for (y2 = 0; y2 < mcuHeight; y2 += vert) {
for (x2 = 0; x2 < mcuWidth; x2 += horiz) {
if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]],
&acHuffTables[scanInfo.acHuffTable[cc]],
&compInfo[cc].prevDC,
data1)) {
return gFalse;
}
transformDataUnit(quantTables[compInfo[cc].quantTable],
data1, data2);
if (hSub == 1 && vSub == 1) {
for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
p1 = &rowBuf[cc][y2+y3][x1+x2];
p1[0] = data2[i];
p1[1] = data2[i+1];
p1[2] = data2[i+2];
p1[3] = data2[i+3];
p1[4] = data2[i+4];
p1[5] = data2[i+5];
p1[6] = data2[i+6];
p1[7] = data2[i+7];
}
} else if (hSub == 2 && vSub == 2) {
for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {
p1 = &rowBuf[cc][y2+y3][x1+x2];
p2 = &rowBuf[cc][y2+y3+1][x1+x2];
p1[0] = p1[1] = p2[0] = p2[1] = data2[i];
p1[2] = p1[3] = p2[2] = p2[3] = data2[i+1];
p1[4] = p1[5] = p2[4] = p2[5] = data2[i+2];
p1[6] = p1[7] = p2[6] = p2[7] = data2[i+3];
p1[8] = p1[9] = p2[8] = p2[9] = data2[i+4];
p1[10] = p1[11] = p2[10] = p2[11] = data2[i+5];
p1[12] = p1[13] = p2[12] = p2[13] = data2[i+6];
p1[14] = p1[15] = p2[14] = p2[15] = data2[i+7];
}
} else {
i = 0;
for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) {
for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) {
for (y5 = 0; y5 < vSub; ++y5)
for (x5 = 0; x5 < hSub; ++x5)
rowBuf[cc][y2+y4+y5][x1+x2+x4+x5] = data2[i];
++i;
}
}
}
}
}
}
--restartCtr;
// color space conversion
if (colorXform) {
// convert YCbCr to RGB
if (numComps == 3) {
for (y2 = 0; y2 < mcuHeight; ++y2) {
for (x2 = 0; x2 < mcuWidth; ++x2) {
pY = rowBuf[0][y2][x1+x2];
pCb = rowBuf[1][y2][x1+x2] - 128;
pCr = rowBuf[2][y2][x1+x2] - 128;
pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
rowBuf[0][y2][x1+x2] = dctClip[dctClipOffset + pR];
pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
rowBuf[1][y2][x1+x2] = dctClip[dctClipOffset + pG];
pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
rowBuf[2][y2][x1+x2] = dctClip[dctClipOffset + pB];
}
}
// convert YCbCrK to CMYK (K is passed through unchanged)
} else if (numComps == 4) {
for (y2 = 0; y2 < mcuHeight; ++y2) {
for (x2 = 0; x2 < mcuWidth; ++x2) {
pY = rowBuf[0][y2][x1+x2];
pCb = rowBuf[1][y2][x1+x2] - 128;
pCr = rowBuf[2][y2][x1+x2] - 128;
pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
rowBuf[0][y2][x1+x2] = 255 - dctClip[dctClipOffset + pR];
pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
rowBuf[1][y2][x1+x2] = 255 - dctClip[dctClipOffset + pG];
pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
rowBuf[2][y2][x1+x2] = 255 - dctClip[dctClipOffset + pB];
}
}
}
}
}
return gTrue;
}
// Read one scan from a progressive or non-interleaved JPEG stream.
void DCTStream::readScan() {
int data[64];
int x1, y1, dx1, dy1, x2, y2, y3, cc, i;
int h, v, horiz, vert, vSub;
int *p1;
int c;
if (scanInfo.numComps == 1) {
for (cc = 0; cc < numComps; ++cc) {
if (scanInfo.comp[cc]) {
break;
}
}
dx1 = mcuWidth / compInfo[cc].hSample;
dy1 = mcuHeight / compInfo[cc].vSample;
} else {
dx1 = mcuWidth;
dy1 = mcuHeight;
}
for (y1 = 0; y1 < height; y1 += dy1) {
for (x1 = 0; x1 < width; x1 += dx1) {
// deal with restart marker
if (restartInterval > 0 && restartCtr == 0) {
c = readMarker();
if (c != restartMarker) {
error(getPos(), "Bad DCT data: incorrect restart marker");
return;
}
if (++restartMarker == 0xd8) {
restartMarker = 0xd0;
}
restart();
}
// read one MCU
for (cc = 0; cc < numComps; ++cc) {
if (!scanInfo.comp[cc]) {
continue;
}
h = compInfo[cc].hSample;
v = compInfo[cc].vSample;
horiz = mcuWidth / h;
vert = mcuHeight / v;
vSub = vert / 8;
for (y2 = 0; y2 < dy1; y2 += vert) {
for (x2 = 0; x2 < dx1; x2 += horiz) {
// pull out the current values
p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
data[i] = p1[0];
data[i+1] = p1[1];
data[i+2] = p1[2];
data[i+3] = p1[3];
data[i+4] = p1[4];
data[i+5] = p1[5];
data[i+6] = p1[6];
data[i+7] = p1[7];
p1 += bufWidth * vSub;
}
// read one data unit
if (progressive) {
if (!readProgressiveDataUnit(
&dcHuffTables[scanInfo.dcHuffTable[cc]],
&acHuffTables[scanInfo.acHuffTable[cc]],
&compInfo[cc].prevDC,
data)) {
return;
}
} else {
if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]],
&acHuffTables[scanInfo.acHuffTable[cc]],
&compInfo[cc].prevDC,
data)) {
return;
}
}
// add the data unit into frameBuf
p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
p1[0] = data[i];
p1[1] = data[i+1];
p1[2] = data[i+2];
p1[3] = data[i+3];
p1[4] = data[i+4];
p1[5] = data[i+5];
p1[6] = data[i+6];
p1[7] = data[i+7];
p1 += bufWidth * vSub;
}
}
}
}
--restartCtr;
}
}
}
// Read one data unit from a sequential JPEG stream.
GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
DCTHuffTable *acHuffTable,
int *prevDC, int data[64]) {
int run, size, amp;
int c;
int i, j;
if ((size = readHuffSym(dcHuffTable)) == 9999) {
return gFalse;
}
if (size > 0) {
if ((amp = readAmp(size)) == 9999) {
return gFalse;
}
} else {
amp = 0;
}
data[0] = *prevDC += amp;
for (i = 1; i < 64; ++i) {
data[i] = 0;
}
i = 1;
while (i < 64) {
run = 0;
while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30) {
run += 0x10;
}
if (c == 9999) {
return gFalse;
}
if (c == 0x00) {
break;
} else {
run += (c >> 4) & 0x0f;
size = c & 0x0f;
amp = readAmp(size);
if (amp == 9999) {
return gFalse;
}
i += run;
if (i < 64) {
j = dctZigZag[i++];
data[j] = amp;
}
}
}
return gTrue;
}
// Read one data unit from a sequential JPEG stream.
GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
DCTHuffTable *acHuffTable,
int *prevDC, int data[64]) {
int run, size, amp, bit, c;
int i, j, k;
// get the DC coefficient
i = scanInfo.firstCoeff;
if (i == 0) {
if (scanInfo.ah == 0) {
if ((size = readHuffSym(dcHuffTable)) == 9999) {
return gFalse;
}
if (size > 0) {
if ((amp = readAmp(size)) == 9999) {
return gFalse;
}
} else {
amp = 0;
}
data[0] += (*prevDC += amp) << scanInfo.al;
} else {
if ((bit = readBit()) == 9999) {
return gFalse;
}
data[0] += bit << scanInfo.al;
}
++i;
}
if (scanInfo.lastCoeff == 0) {
return gTrue;
}
// check for an EOB run
if (eobRun > 0) {
while (i <= scanInfo.lastCoeff) {
j = dctZigZag[i++];
if (data[j] != 0) {
if ((bit = readBit()) == EOF) {
return gFalse;
}
if (bit) {
data[j] += 1 << scanInfo.al;
}
}
}
--eobRun;
return gTrue;
}
// read the AC coefficients
while (i <= scanInfo.lastCoeff) {
if ((c = readHuffSym(acHuffTable)) == 9999) {
return gFalse;
}
// ZRL
if (c == 0xf0) {
k = 0;
while (k < 16) {
j = dctZigZag[i++];
if (data[j] == 0) {
++k;
} else {
if ((bit = readBit()) == EOF) {
return gFalse;
}
if (bit) {
data[j] += 1 << scanInfo.al;
}
}
}
// EOB run
} else if ((c & 0x0f) == 0x00) {
j = c >> 4;
eobRun = 0;
for (k = 0; k < j; ++k) {
if ((bit = readBit()) == EOF) {
return gFalse;
}
eobRun = (eobRun << 1) | bit;
}
eobRun += 1 << j;
while (i <= scanInfo.lastCoeff) {
j = dctZigZag[i++];
if (data[j] != 0) {
if ((bit = readBit()) == EOF) {
return gFalse;
}
if (bit) {
data[j] += 1 << scanInfo.al;
}
}
}
--eobRun;
break;
// zero run and one AC coefficient
} else {
run = (c >> 4) & 0x0f;
size = c & 0x0f;
if ((amp = readAmp(size)) == 9999) {
return gFalse;
}
k = 0;
do {
j = dctZigZag[i++];
while (data[j] != 0) {
if ((bit = readBit()) == EOF) {
return gFalse;
}
if (bit) {
data[j] += 1 << scanInfo.al;
}
j = dctZigZag[i++];
}
++k;
} while (k <= run);
data[j] = amp << scanInfo.al;
}
}
return gTrue;
}
// Decode a progressive JPEG image.
void DCTStream::decodeImage() {
int dataIn[64];
Guchar dataOut[64];
Guchar *quantTable;
int pY, pCb, pCr, pR, pG, pB;
int x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i;
int h, v, horiz, vert, hSub, vSub;
int *p0, *p1, *p2;
for (y1 = 0; y1 < bufHeight; y1 += mcuHeight) {
for (x1 = 0; x1 < bufWidth; x1 += mcuWidth) {
for (cc = 0; cc < numComps; ++cc) {
quantTable = quantTables[compInfo[cc].quantTable];
h = compInfo[cc].hSample;
v = compInfo[cc].vSample;
horiz = mcuWidth / h;
vert = mcuHeight / v;
hSub = horiz / 8;
vSub = vert / 8;
for (y2 = 0; y2 < mcuHeight; y2 += vert) {
for (x2 = 0; x2 < mcuWidth; x2 += horiz) {
// pull out the coded data unit
p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
dataIn[i] = p1[0];
dataIn[i+1] = p1[1];
dataIn[i+2] = p1[2];
dataIn[i+3] = p1[3];
dataIn[i+4] = p1[4];
dataIn[i+5] = p1[5];
dataIn[i+6] = p1[6];
dataIn[i+7] = p1[7];
p1 += bufWidth * vSub;