From bace4ea18c03bfcaadab55300bc15290f87540c7 Mon Sep 17 00:00:00 2001 From: Martin Kretzschmar Date: Mon, 17 May 2004 18:12:38 +0000 Subject: Initial revision --- (limited to 'pdf/xpdf') diff --git a/pdf/xpdf/JArithmeticDecoder.cc b/pdf/xpdf/JArithmeticDecoder.cc new file mode 100644 index 0000000..fd29744 --- /dev/null +++ b/pdf/xpdf/JArithmeticDecoder.cc @@ -0,0 +1,300 @@ +//======================================================================== +// +// JArithmeticDecoder.cc +// +// Copyright 2002-2004 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include "Object.h" +#include "Stream.h" +#include "JArithmeticDecoder.h" + +//------------------------------------------------------------------------ +// JArithmeticDecoderStates +//------------------------------------------------------------------------ + +JArithmeticDecoderStats::JArithmeticDecoderStats(int contextSizeA) { + contextSize = contextSizeA; + cxTab = (Guchar *)gmalloc(contextSize * sizeof(Guchar)); + reset(); +} + +JArithmeticDecoderStats::~JArithmeticDecoderStats() { + gfree(cxTab); +} + +JArithmeticDecoderStats *JArithmeticDecoderStats::copy() { + JArithmeticDecoderStats *stats; + + stats = new JArithmeticDecoderStats(contextSize); + memcpy(stats->cxTab, cxTab, contextSize); + return stats; +} + +void JArithmeticDecoderStats::reset() { + memset(cxTab, 0, contextSize); +} + +void JArithmeticDecoderStats::copyFrom(JArithmeticDecoderStats *stats) { + memcpy(cxTab, stats->cxTab, contextSize); +} + +void JArithmeticDecoderStats::setEntry(Guint cx, int i, int mps) { + cxTab[cx] = (i << 1) + mps; +} + +//------------------------------------------------------------------------ +// JArithmeticDecoder +//------------------------------------------------------------------------ + +Guint JArithmeticDecoder::qeTab[47] = { + 0x56010000, 0x34010000, 0x18010000, 0x0AC10000, + 0x05210000, 0x02210000, 0x56010000, 0x54010000, + 0x48010000, 0x38010000, 0x30010000, 0x24010000, + 0x1C010000, 0x16010000, 0x56010000, 0x54010000, + 0x51010000, 0x48010000, 0x38010000, 0x34010000, + 0x30010000, 0x28010000, 0x24010000, 0x22010000, + 0x1C010000, 0x18010000, 0x16010000, 0x14010000, + 0x12010000, 0x11010000, 0x0AC10000, 0x09C10000, + 0x08A10000, 0x05210000, 0x04410000, 0x02A10000, + 0x02210000, 0x01410000, 0x01110000, 0x00850000, + 0x00490000, 0x00250000, 0x00150000, 0x00090000, + 0x00050000, 0x00010000, 0x56010000 +}; + +int JArithmeticDecoder::nmpsTab[47] = { + 1, 2, 3, 4, 5, 38, 7, 8, 9, 10, 11, 12, 13, 29, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 45, 46 +}; + +int JArithmeticDecoder::nlpsTab[47] = { + 1, 6, 9, 12, 29, 33, 6, 14, 14, 14, 17, 18, 20, 21, 14, 14, + 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 46 +}; + +int JArithmeticDecoder::switchTab[47] = { + 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +JArithmeticDecoder::JArithmeticDecoder() { + str = NULL; +} + +JArithmeticDecoder::~JArithmeticDecoder() { + while (dataLen > 0) { + readByte(); + } +} + +inline Guint JArithmeticDecoder::readByte() { + if (dataLen == 0) { + return 0xff; + } + if (dataLen > 0) { + --dataLen; + } + return (Guint)str->getChar() & 0xff; +} + +void JArithmeticDecoder::start() { + buf0 = readByte(); + buf1 = readByte(); + + // INITDEC + c = (buf0 ^ 0xff) << 16; + byteIn(); + c <<= 7; + ct -= 7; + a = 0x80000000; +} + +int JArithmeticDecoder::decodeBit(Guint context, + JArithmeticDecoderStats *stats) { + int bit; + Guint qe; + int iCX, mpsCX; + + iCX = stats->cxTab[context] >> 1; + mpsCX = stats->cxTab[context] & 1; + qe = qeTab[iCX]; + a -= qe; + if (c < a) { + if (a & 0x80000000) { + bit = mpsCX; + } else { + // MPS_EXCHANGE + if (a < qe) { + bit = 1 - mpsCX; + if (switchTab[iCX]) { + stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX); + } else { + stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX; + } + } else { + bit = mpsCX; + stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX; + } + // RENORMD + do { + if (ct == 0) { + byteIn(); + } + a <<= 1; + c <<= 1; + --ct; + } while (!(a & 0x80000000)); + } + } else { + c -= a; + // LPS_EXCHANGE + if (a < qe) { + bit = mpsCX; + stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX; + } else { + bit = 1 - mpsCX; + if (switchTab[iCX]) { + stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX); + } else { + stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX; + } + } + a = qe; + // RENORMD + do { + if (ct == 0) { + byteIn(); + } + a <<= 1; + c <<= 1; + --ct; + } while (!(a & 0x80000000)); + } + return bit; +} + +int JArithmeticDecoder::decodeByte(Guint context, + JArithmeticDecoderStats *stats) { + int byte; + int i; + + byte = 0; + for (i = 0; i < 8; ++i) { + byte = (byte << 1) | decodeBit(context, stats); + } + return byte; +} + +GBool JArithmeticDecoder::decodeInt(int *x, JArithmeticDecoderStats *stats) { + int s; + Guint v; + int i; + + prev = 1; + s = decodeIntBit(stats); + if (decodeIntBit(stats)) { + if (decodeIntBit(stats)) { + if (decodeIntBit(stats)) { + if (decodeIntBit(stats)) { + if (decodeIntBit(stats)) { + v = 0; + for (i = 0; i < 32; ++i) { + v = (v << 1) | decodeIntBit(stats); + } + v += 4436; + } else { + v = 0; + for (i = 0; i < 12; ++i) { + v = (v << 1) | decodeIntBit(stats); + } + v += 340; + } + } else { + v = 0; + for (i = 0; i < 8; ++i) { + v = (v << 1) | decodeIntBit(stats); + } + v += 84; + } + } else { + v = 0; + for (i = 0; i < 6; ++i) { + v = (v << 1) | decodeIntBit(stats); + } + v += 20; + } + } else { + v = decodeIntBit(stats); + v = (v << 1) | decodeIntBit(stats); + v = (v << 1) | decodeIntBit(stats); + v = (v << 1) | decodeIntBit(stats); + v += 4; + } + } else { + v = decodeIntBit(stats); + v = (v << 1) | decodeIntBit(stats); + } + + if (s) { + if (v == 0) { + return gFalse; + } + *x = -(int)v; + } else { + *x = (int)v; + } + return gTrue; +} + +int JArithmeticDecoder::decodeIntBit(JArithmeticDecoderStats *stats) { + int bit; + + bit = decodeBit(prev, stats); + if (prev < 0x100) { + prev = (prev << 1) | bit; + } else { + prev = (((prev << 1) | bit) & 0x1ff) | 0x100; + } + return bit; +} + +Guint JArithmeticDecoder::decodeIAID(Guint codeLen, + JArithmeticDecoderStats *stats) { + Guint i; + int bit; + + prev = 1; + for (i = 0; i < codeLen; ++i) { + bit = decodeBit(prev, stats); + prev = (prev << 1) | bit; + } + return prev - (1 << codeLen); +} + +void JArithmeticDecoder::byteIn() { + if (buf0 == 0xff) { + if (buf1 > 0x8f) { + ct = 8; + } else { + buf0 = buf1; + buf1 = readByte(); + c = c + 0xfe00 - (buf0 << 9); + ct = 7; + } + } else { + buf0 = buf1; + buf1 = readByte(); + c = c + 0xff00 - (buf0 << 8); + ct = 8; + } +} diff --git a/pdf/xpdf/JArithmeticDecoder.h b/pdf/xpdf/JArithmeticDecoder.h new file mode 100644 index 0000000..a348017 --- /dev/null +++ b/pdf/xpdf/JArithmeticDecoder.h @@ -0,0 +1,91 @@ +//======================================================================== +// +// JArithmeticDecoder.h +// +// Arithmetic decoder used by the JBIG2 and JPEG2000 decoders. +// +// Copyright 2002-2004 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef JARITHMETICDECODER_H +#define JARITHMETICDECODER_H + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +#include "gtypes.h" + +class Stream; + +//------------------------------------------------------------------------ +// JArithmeticDecoderStats +//------------------------------------------------------------------------ + +class JArithmeticDecoderStats { +public: + + JArithmeticDecoderStats(int contextSizeA); + ~JArithmeticDecoderStats(); + JArithmeticDecoderStats *copy(); + void reset(); + int getContextSize() { return contextSize; } + void copyFrom(JArithmeticDecoderStats *stats); + void setEntry(Guint cx, int i, int mps); + +private: + + Guchar *cxTab; // cxTab[cx] = (i[cx] << 1) + mps[cx] + int contextSize; + + friend class JArithmeticDecoder; +}; + +//------------------------------------------------------------------------ +// JArithmeticDecoder +//------------------------------------------------------------------------ + +class JArithmeticDecoder { +public: + + JArithmeticDecoder(); + ~JArithmeticDecoder(); + void setStream(Stream *strA) + { str = strA; dataLen = -1; } + void setStream(Stream *strA, int dataLenA) + { str = strA; dataLen = dataLenA; } + void start(); + int decodeBit(Guint context, JArithmeticDecoderStats *stats); + int decodeByte(Guint context, JArithmeticDecoderStats *stats); + + // Returns false for OOB, otherwise sets * and returns true. + GBool decodeInt(int *x, JArithmeticDecoderStats *stats); + + Guint decodeIAID(Guint codeLen, + JArithmeticDecoderStats *stats); + +private: + + Guint readByte(); + int decodeIntBit(JArithmeticDecoderStats *stats); + void byteIn(); + + static Guint qeTab[47]; + static int nmpsTab[47]; + static int nlpsTab[47]; + static int switchTab[47]; + + Guint buf0, buf1; + Guint c, a; + int ct; + + Guint prev; // for the integer decoder + + Stream *str; + int dataLen; +}; + +#endif diff --git a/pdf/xpdf/JPXStream.cc b/pdf/xpdf/JPXStream.cc new file mode 100644 index 0000000..defa7d2 --- /dev/null +++ b/pdf/xpdf/JPXStream.cc @@ -0,0 +1,2822 @@ +//======================================================================== +// +// JPXStream.cc +// +// Copyright 2002-2003 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include "gmem.h" +#include "Error.h" +#include "JArithmeticDecoder.h" +#include "JPXStream.h" + +//~ to do: +// - precincts +// - ROI +// - progression order changes +// - packed packet headers +// - support for palettes, channel maps, etc. +// - make sure all needed JP2/JPX subboxes are parsed (readBoxes) +// - can we assume that QCC segments must come after the QCD segment? +// - skip EPH markers (readTilePartData) +// - handle tilePartToEOC in readTilePartData +// - deal with multiple codeword segments (readTilePartData, +// readCodeBlockData) +// - progression orders 2, 3, and 4 +// - in coefficient decoding (readCodeBlockData): +// - termination pattern: terminate after every coding pass +// - error resilience segmentation symbol +// - selective arithmetic coding bypass +// - vertically causal context formation +// - coeffs longer than 31 bits (should just ignore the extra bits?) +// - handle boxes larger than 2^32 bytes +// - the fixed-point arithmetic won't handle 16-bit pixels + +//------------------------------------------------------------------------ + +// number of contexts for the arithmetic decoder +#define jpxNContexts 19 + +#define jpxContextSigProp 0 // 0 - 8: significance prop and cleanup +#define jpxContextSign 9 // 9 - 13: sign +#define jpxContextMagRef 14 // 14 -16: magnitude refinement +#define jpxContextRunLength 17 // cleanup: run length +#define jpxContextUniform 18 // cleanup: first signif coeff + +//------------------------------------------------------------------------ + +#define jpxPassSigProp 0 +#define jpxPassMagRef 1 +#define jpxPassCleanup 2 + +//------------------------------------------------------------------------ + +// arithmetic decoder context for the significance propagation and +// cleanup passes: +// [horiz][vert][diag][subband] +// where subband = 0 for HL +// = 1 for LH and LL +// = 2 for HH +static Guint sigPropContext[3][3][5][3] = { + {{{ 0, 0, 0 }, // horiz=0, vert=0, diag=0 + { 1, 1, 3 }, // horiz=0, vert=0, diag=1 + { 2, 2, 6 }, // horiz=0, vert=0, diag=2 + { 2, 2, 8 }, // horiz=0, vert=0, diag=3 + { 2, 2, 8 }}, // horiz=0, vert=0, diag=4 + {{ 5, 3, 1 }, // horiz=0, vert=1, diag=0 + { 6, 3, 4 }, // horiz=0, vert=1, diag=1 + { 6, 3, 7 }, // horiz=0, vert=1, diag=2 + { 6, 3, 8 }, // horiz=0, vert=1, diag=3 + { 6, 3, 8 }}, // horiz=0, vert=1, diag=4 + {{ 8, 4, 2 }, // horiz=0, vert=2, diag=0 + { 8, 4, 5 }, // horiz=0, vert=2, diag=1 + { 8, 4, 7 }, // horiz=0, vert=2, diag=2 + { 8, 4, 8 }, // horiz=0, vert=2, diag=3 + { 8, 4, 8 }}}, // horiz=0, vert=2, diag=4 + {{{ 3, 5, 1 }, // horiz=1, vert=0, diag=0 + { 3, 6, 4 }, // horiz=1, vert=0, diag=1 + { 3, 6, 7 }, // horiz=1, vert=0, diag=2 + { 3, 6, 8 }, // horiz=1, vert=0, diag=3 + { 3, 6, 8 }}, // horiz=1, vert=0, diag=4 + {{ 7, 7, 2 }, // horiz=1, vert=1, diag=0 + { 7, 7, 5 }, // horiz=1, vert=1, diag=1 + { 7, 7, 7 }, // horiz=1, vert=1, diag=2 + { 7, 7, 8 }, // horiz=1, vert=1, diag=3 + { 7, 7, 8 }}, // horiz=1, vert=1, diag=4 + {{ 8, 7, 2 }, // horiz=1, vert=2, diag=0 + { 8, 7, 5 }, // horiz=1, vert=2, diag=1 + { 8, 7, 7 }, // horiz=1, vert=2, diag=2 + { 8, 7, 8 }, // horiz=1, vert=2, diag=3 + { 8, 7, 8 }}}, // horiz=1, vert=2, diag=4 + {{{ 4, 8, 2 }, // horiz=2, vert=0, diag=0 + { 4, 8, 5 }, // horiz=2, vert=0, diag=1 + { 4, 8, 7 }, // horiz=2, vert=0, diag=2 + { 4, 8, 8 }, // horiz=2, vert=0, diag=3 + { 4, 8, 8 }}, // horiz=2, vert=0, diag=4 + {{ 7, 8, 2 }, // horiz=2, vert=1, diag=0 + { 7, 8, 5 }, // horiz=2, vert=1, diag=1 + { 7, 8, 7 }, // horiz=2, vert=1, diag=2 + { 7, 8, 8 }, // horiz=2, vert=1, diag=3 + { 7, 8, 8 }}, // horiz=2, vert=1, diag=4 + {{ 8, 8, 2 }, // horiz=2, vert=2, diag=0 + { 8, 8, 5 }, // horiz=2, vert=2, diag=1 + { 8, 8, 7 }, // horiz=2, vert=2, diag=2 + { 8, 8, 8 }, // horiz=2, vert=2, diag=3 + { 8, 8, 8 }}} // horiz=2, vert=2, diag=4 +}; + +// arithmetic decoder context and xor bit for the sign bit in the +// significance propagation pass: +// [horiz][vert][k] +// where horiz/vert are offset by 2 (i.e., range is -2 .. 2) +// and k = 0 for the context +// = 1 for the xor bit +static Guint signContext[5][5][2] = { + {{ 13, 1 }, // horiz=-2, vert=-2 + { 13, 1 }, // horiz=-2, vert=-1 + { 12, 1 }, // horiz=-2, vert= 0 + { 11, 1 }, // horiz=-2, vert=+1 + { 11, 1 }}, // horiz=-2, vert=+2 + {{ 13, 1 }, // horiz=-1, vert=-2 + { 13, 1 }, // horiz=-1, vert=-1 + { 12, 1 }, // horiz=-1, vert= 0 + { 11, 1 }, // horiz=-1, vert=+1 + { 11, 1 }}, // horiz=-1, vert=+2 + {{ 10, 1 }, // horiz= 0, vert=-2 + { 10, 1 }, // horiz= 0, vert=-1 + { 9, 0 }, // horiz= 0, vert= 0 + { 10, 0 }, // horiz= 0, vert=+1 + { 10, 0 }}, // horiz= 0, vert=+2 + {{ 11, 0 }, // horiz=+1, vert=-2 + { 11, 0 }, // horiz=+1, vert=-1 + { 12, 0 }, // horiz=+1, vert= 0 + { 13, 0 }, // horiz=+1, vert=+1 + { 13, 0 }}, // horiz=+1, vert=+2 + {{ 11, 0 }, // horiz=+2, vert=-2 + { 11, 0 }, // horiz=+2, vert=-1 + { 12, 0 }, // horiz=+2, vert= 0 + { 13, 0 }, // horiz=+2, vert=+1 + { 13, 0 }}, // horiz=+2, vert=+2 +}; + +//------------------------------------------------------------------------ + +// constants used in the IDWT +#define idwtAlpha -1.586134342059924 +#define idwtBeta -0.052980118572961 +#define idwtGamma 0.882911075530934 +#define idwtDelta 0.443506852043971 +#define idwtKappa 1.230174104914001 +#define idwtIKappa (1.0 / idwtKappa) + +// number of bits to the right of the decimal point for the fixed +// point arithmetic used in the IDWT +#define fracBits 16 + +//------------------------------------------------------------------------ + +// floor(x / y) +#define jpxFloorDiv(x, y) ((x) / (y)) + +// floor(x / 2^y) +#define jpxFloorDivPow2(x, y) ((x) >> (y)) + +// ceil(x / y) +#define jpxCeilDiv(x, y) (((x) + (y) - 1) / (y)) + +// ceil(x / 2^y) +#define jpxCeilDivPow2(x, y) (((x) + (1 << (y)) - 1) >> (y)) + +//------------------------------------------------------------------------ + +JPXStream::JPXStream(Stream *strA): + FilterStream(strA) +{ + nComps = 0; + bpc = NULL; + width = height = 0; + haveCS = gFalse; + havePalette = gFalse; + haveCompMap = gFalse; + haveChannelDefn = gFalse; + + img.tiles = NULL; + bitBuf = 0; + bitBufLen = 0; + bitBufSkip = gFalse; + byteCount = 0; +} + +JPXStream::~JPXStream() { + JPXTile *tile; + JPXTileComp *tileComp; + JPXResLevel *resLevel; + JPXPrecinct *precinct; + JPXSubband *subband; + JPXCodeBlock *cb; + Guint comp, i, k, r, pre, sb; + + gfree(bpc); + if (havePalette) { + gfree(palette.bpc); + gfree(palette.c); + } + if (haveCompMap) { + gfree(compMap.comp); + gfree(compMap.type); + gfree(compMap.pComp); + } + if (haveChannelDefn) { + gfree(channelDefn.idx); + gfree(channelDefn.type); + gfree(channelDefn.assoc); + } + + if (img.tiles) { + for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { + tile = &img.tiles[i]; + if (tile->tileComps) { + for (comp = 0; comp < img.nComps; ++comp) { + tileComp = &tile->tileComps[comp]; + gfree(tileComp->quantSteps); + gfree(tileComp->data); + gfree(tileComp->buf); + if (tileComp->resLevels) { + for (r = 0; r <= tileComp->nDecompLevels; ++r) { + resLevel = &tileComp->resLevels[r]; + if (resLevel->precincts) { + for (pre = 0; pre < 1; ++pre) { + precinct = &resLevel->precincts[pre]; + if (precinct->subbands) { + for (sb = 0; sb < (r == 0 ? 1 : 3); ++sb) { + subband = &precinct->subbands[sb]; + gfree(subband->inclusion); + gfree(subband->zeroBitPlane); + if (subband->cbs) { + for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) { + cb = &subband->cbs[k]; + gfree(cb->coeffs); + if (cb->stats) { + delete cb->stats; + } + } + gfree(subband->cbs); + } + } + gfree(precinct->subbands); + } + } + gfree(img.tiles[i].tileComps[comp].resLevels[r].precincts); + } + } + gfree(img.tiles[i].tileComps[comp].resLevels); + } + } + gfree(img.tiles[i].tileComps); + } + } + gfree(img.tiles); + } + delete str; +} + +void JPXStream::reset() { + str->reset(); + if (readBoxes()) { + curY = img.yOffset; + } else { + // readBoxes reported an error, so we go immediately to EOF + curY = img.ySize; + } + curX = img.xOffset; + curComp = 0; + readBufLen = 0; +} + +int JPXStream::getChar() { + int c; + + if (readBufLen < 8) { + fillReadBuf(); + } + if (readBufLen == 8) { + c = readBuf & 0xff; + readBufLen = 0; + } else if (readBufLen > 8) { + c = (readBuf >> (readBufLen - 8)) & 0xff; + readBufLen -= 8; + } else if (readBufLen == 0) { + c = EOF; + } else { + c = (readBuf << (8 - readBufLen)) & 0xff; + readBufLen = 0; + } + return c; +} + +int JPXStream::lookChar() { + int c; + + if (readBufLen < 8) { + fillReadBuf(); + } + if (readBufLen == 8) { + c = readBuf & 0xff; + } else if (readBufLen > 8) { + c = (readBuf >> (readBufLen - 8)) & 0xff; + } else if (readBufLen == 0) { + c = EOF; + } else { + c = (readBuf << (8 - readBufLen)) & 0xff; + } + return c; +} + +void JPXStream::fillReadBuf() { + JPXTileComp *tileComp; + Guint tileIdx, tx, ty; + int pix, pixBits; + + do { + if (curY >= img.ySize) { + return; + } + tileIdx = ((curY - img.yTileOffset) / img.yTileSize) * img.nXTiles + + (curX - img.xTileOffset) / img.xTileSize; +#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid + tileComp = &img.tiles[tileIdx].tileComps[curComp]; +#else + tileComp = &img.tiles[tileIdx].tileComps[havePalette ? 0 : curComp]; +#endif + tx = jpxCeilDiv((curX - img.xTileOffset) % img.xTileSize, tileComp->hSep); + ty = jpxCeilDiv((curY - img.yTileOffset) % img.yTileSize, tileComp->vSep); + pix = (int)tileComp->data[ty * (tileComp->x1 - tileComp->x0) + tx]; + pixBits = tileComp->prec; +#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid + if (++curComp == img.nComps) { +#else + if (havePalette) { + if (pix >= 0 && pix < palette.nEntries) { + pix = palette.c[pix * palette.nComps + curComp]; + } else { + pix = + pixBits = palette.bpc[curComp]; + } + if (++curComp == (Guint)(havePalette ? palette.nComps : img.nComps)) { +#endif + curComp = 0; + if (++curX == img.xSize) { + curX = img.xOffset; + ++curY; + } + } + if (pixBits == 8) { + readBuf = (readBuf << 8) | (pix & 0xff); + } else { + readBuf = (readBuf << pixBits) | (pix & ((1 << pixBits) - 1)); + } + readBufLen += pixBits; + } while (readBufLen < 8); +} + +GString *JPXStream::getPSFilter(int psLevel, char *indent) { + return NULL; +} + +GBool JPXStream::isBinary(GBool last) { + return str->isBinary(gTrue); +} + +GBool JPXStream::readBoxes() { + Guint boxType, boxLen, dataLen; + Guint bpc1, compression, unknownColorspace, ipr; + Guint i, j; + + haveImgHdr = gFalse; + + // check for a naked JPEG 2000 codestream (without the JP2/JPX + // wrapper) -- this appears to be a violation of the PDF spec, but + // Acrobat allows it + if (str->lookChar() == 0xff) { + error(getPos(), "Naked JPEG 2000 codestream, missing JP2/JPX wrapper"); + readCodestream(0); + nComps = img.nComps; + bpc = (Guint *)gmalloc(nComps * sizeof(Guint)); + for (i = 0; i < nComps; ++i) { + bpc[i] = img.tiles[0].tileComps[i].prec; + } + width = img.xSize - img.xOffset; + height = img.ySize - img.yOffset; + return gTrue; + } + + while (readBoxHdr(&boxType, &boxLen, &dataLen)) { + switch (boxType) { + case 0x6a703268: // JP2 header + // this is a grouping box ('superbox') which has no real + // contents and doesn't appear to be used consistently, i.e., + // some things which should be subboxes of the JP2 header box + // show up outside of it - so we simply ignore the JP2 header + // box + break; + case 0x69686472: // image header + if (!readULong(&height) || + !readULong(&width) || + !readUWord(&nComps) || + !readUByte(&bpc1) || + !readUByte(&compression) || + !readUByte(&unknownColorspace) || + !readUByte(&ipr)) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + if (compression != 7) { + error(getPos(), "Unknown compression type in JPX stream"); + return gFalse; + } + bpc = (Guint *)gmalloc(nComps * sizeof(Guint)); + for (i = 0; i < nComps; ++i) { + bpc[i] = bpc1; + } + haveImgHdr = gTrue; + break; + case 0x62706363: // bits per component + if (!haveImgHdr) { + error(getPos(), "Found bits per component box before image header box in JPX stream"); + return gFalse; + } + if (dataLen != nComps) { + error(getPos(), "Invalid bits per component box in JPX stream"); + return gFalse; + } + for (i = 0; i < nComps; ++i) { + if (!readUByte(&bpc[i])) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + } + break; + case 0x636F6C72: // color specification + if (!readColorSpecBox(dataLen)) { + return gFalse; + } + break; + case 0x70636c72: // palette + if (!readUWord(&palette.nEntries) || + !readUByte(&palette.nComps)) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + palette.bpc = (Guint *)gmalloc(palette.nComps * sizeof(Guint)); + palette.c = + (int *)gmalloc(palette.nEntries * palette.nComps * sizeof(int)); + for (i = 0; i < palette.nComps; ++i) { + if (!readUByte(&palette.bpc[i])) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + ++palette.bpc[i]; + } + for (i = 0; i < palette.nEntries; ++i) { + for (j = 0; j < palette.nComps; ++j) { + if (!readNBytes(((palette.bpc[j] & 0x7f) + 7) >> 3, + (palette.bpc[j] & 0x80) ? gTrue : gFalse, + &palette.c[i * palette.nComps + j])) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + } + } + havePalette = gTrue; + break; + case 0x636d6170: // component mapping + compMap.nChannels = dataLen / 4; + compMap.comp = (Guint *)gmalloc(compMap.nChannels * sizeof(Guint)); + compMap.type = (Guint *)gmalloc(compMap.nChannels * sizeof(Guint)); + compMap.pComp = (Guint *)gmalloc(compMap.nChannels * sizeof(Guint)); + for (i = 0; i < compMap.nChannels; ++i) { + if (!readUWord(&compMap.comp[i]) || + !readUByte(&compMap.type[i]) || + !readUByte(&compMap.pComp[i])) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + } + haveCompMap = gTrue; + break; + case 0x63646566: // channel definition + if (!readUWord(&channelDefn.nChannels)) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + channelDefn.idx = + (Guint *)gmalloc(channelDefn.nChannels * sizeof(Guint)); + channelDefn.type = + (Guint *)gmalloc(channelDefn.nChannels * sizeof(Guint)); + channelDefn.assoc = + (Guint *)gmalloc(channelDefn.nChannels * sizeof(Guint)); + for (i = 0; i < channelDefn.nChannels; ++i) { + if (!readUWord(&channelDefn.idx[i]) || + !readUWord(&channelDefn.type[i]) || + !readUWord(&channelDefn.assoc[i])) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + } + haveChannelDefn = gTrue; + break; + case 0x6A703263: // contiguous codestream + if (!bpc) { + error(getPos(), "JPX stream is missing the image header box"); + return gFalse; + } + if (!haveCS) { + error(getPos(), "JPX stream has no supported color spec"); + return gFalse; + } + if (!readCodestream(dataLen)) { + return gFalse; + } + break; + default: + for (i = 0; i < dataLen; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + } + break; + } + } + return gTrue; +} + +GBool JPXStream::readColorSpecBox(Guint dataLen) { + JPXColorSpec newCS; + Guint csApprox, csEnum; + Guint i; + GBool ok; + + ok = gFalse; + if (!readUByte(&newCS.meth) || + !readByte(&newCS.prec) || + !readUByte(&csApprox)) { + goto err; + } + switch (newCS.meth) { + case 1: // enumerated colorspace + if (!readULong(&csEnum)) { + goto err; + } + newCS.enumerated.type = (JPXColorSpaceType)csEnum; + switch (newCS.enumerated.type) { + case jpxCSBiLevel: + ok = gTrue; + break; + case jpxCSYCbCr1: + ok = gTrue; + break; + case jpxCSYCbCr2: + ok = gTrue; + break; + case jpxCSYCBCr3: + ok = gTrue; + break; + case jpxCSPhotoYCC: + ok = gTrue; + break; + case jpxCSCMY: + ok = gTrue; + break; + case jpxCSCMYK: + ok = gTrue; + break; + case jpxCSYCCK: + ok = gTrue; + break; + case jpxCSCIELab: + if (dataLen == 3 + 7*4) { + if (!readULong(&newCS.enumerated.cieLab.rl) || + !readULong(&newCS.enumerated.cieLab.ol) || + !readULong(&newCS.enumerated.cieLab.ra) || + !readULong(&newCS.enumerated.cieLab.oa) || + !readULong(&newCS.enumerated.cieLab.rb) || + !readULong(&newCS.enumerated.cieLab.ob) || + !readULong(&newCS.enumerated.cieLab.il)) { + goto err; + } + } else if (dataLen == 3) { + //~ this assumes the 8-bit case + newCS.enumerated.cieLab.rl = 100; + newCS.enumerated.cieLab.ol = 0; + newCS.enumerated.cieLab.ra = 255; + newCS.enumerated.cieLab.oa = 128; + newCS.enumerated.cieLab.rb = 255; + newCS.enumerated.cieLab.ob = 96; + newCS.enumerated.cieLab.il = 0x00443530; + } else { + goto err; + } + ok = gTrue; + break; + case jpxCSsRGB: + ok = gTrue; + break; + case jpxCSGrayscale: + ok = gTrue; + break; + case jpxCSBiLevel2: + ok = gTrue; + break; + case jpxCSCIEJab: + // not allowed in PDF + goto err; + case jpxCSCISesRGB: + ok = gTrue; + break; + case jpxCSROMMRGB: + ok = gTrue; + break; + case jpxCSsRGBYCbCr: + ok = gTrue; + break; + case jpxCSYPbPr1125: + ok = gTrue; + break; + case jpxCSYPbPr1250: + ok = gTrue; + break; + default: + goto err; + } + break; + case 2: // restricted ICC profile + case 3: // any ICC profile (JPX) + case 4: // vendor color (JPX) + for (i = 0; i < dataLen - 3; ++i) { + if (str->getChar() == EOF) { + goto err; + } + } + break; + } + + if (ok && (!haveCS || newCS.prec > cs.prec)) { + cs = newCS; + haveCS = gTrue; + } + + return gTrue; + + err: + error(getPos(), "Error in JPX color spec"); + return gFalse; +} + +GBool JPXStream::readCodestream(Guint len) { + JPXTile *tile; + JPXTileComp *tileComp; + int segType; + GBool haveSIZ, haveCOD, haveQCD, haveSOT; + Guint precinctSize, style; + Guint segLen, capabilities, comp, i, j, r; + + //----- main header + haveSIZ = haveCOD = haveQCD = haveSOT = gFalse; + do { + if (!readMarkerHdr(&segType, &segLen)) { + error(getPos(), "Error in JPX codestream"); + return gFalse; + } + switch (segType) { + case 0x4f: // SOC - start of codestream + // marker only + break; + case 0x51: // SIZ - image and tile size + if (!readUWord(&capabilities) || + !readULong(&img.xSize) || + !readULong(&img.ySize) || + !readULong(&img.xOffset) || + !readULong(&img.yOffset) || + !readULong(&img.xTileSize) || + !readULong(&img.yTileSize) || + !readULong(&img.xTileOffset) || + !readULong(&img.yTileOffset) || + !readUWord(&img.nComps)) { + error(getPos(), "Error in JPX SIZ marker segment"); + return gFalse; + } + if (haveImgHdr && img.nComps != nComps) { + error(getPos(), "Different number of components in JPX SIZ marker segment"); + return gFalse; + } + img.nXTiles = (img.xSize - img.xTileOffset + img.xTileSize - 1) + / img.xTileSize; + img.nYTiles = (img.ySize - img.yTileOffset + img.yTileSize - 1) + / img.yTileSize; + img.tiles = (JPXTile *)gmalloc(img.nXTiles * img.nYTiles * + sizeof(JPXTile)); + for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { + img.tiles[i].tileComps = (JPXTileComp *)gmalloc(img.nComps * + sizeof(JPXTileComp)); + for (comp = 0; comp < img.nComps; ++comp) { + img.tiles[i].tileComps[comp].quantSteps = NULL; + img.tiles[i].tileComps[comp].data = NULL; + img.tiles[i].tileComps[comp].buf = NULL; + img.tiles[i].tileComps[comp].resLevels = NULL; + } + } + for (comp = 0; comp < img.nComps; ++comp) { + if (!readUByte(&img.tiles[0].tileComps[comp].prec) || + !readUByte(&img.tiles[0].tileComps[comp].hSep) || + !readUByte(&img.tiles[0].tileComps[comp].vSep)) { + error(getPos(), "Error in JPX SIZ marker segment"); + return gFalse; + } + img.tiles[0].tileComps[comp].sgned = + (img.tiles[0].tileComps[comp].prec & 0x80) ? gTrue : gFalse; + img.tiles[0].tileComps[comp].prec = + (img.tiles[0].tileComps[comp].prec & 0x7f) + 1; + for (i = 1; i < img.nXTiles * img.nYTiles; ++i) { + img.tiles[i].tileComps[comp] = img.tiles[0].tileComps[comp]; + } + } + haveSIZ = gTrue; + break; + case 0x52: // COD - coding style default + if (!readUByte(&img.tiles[0].tileComps[0].style) || + !readUByte(&img.tiles[0].progOrder) || + !readUWord(&img.tiles[0].nLayers) || + !readUByte(&img.tiles[0].multiComp) || + !readUByte(&img.tiles[0].tileComps[0].nDecompLevels) || + !readUByte(&img.tiles[0].tileComps[0].codeBlockW) || + !readUByte(&img.tiles[0].tileComps[0].codeBlockH) || + !readUByte(&img.tiles[0].tileComps[0].codeBlockStyle) || + !readUByte(&img.tiles[0].tileComps[0].transform)) { + error(getPos(), "Error in JPX COD marker segment"); + return gFalse; + } + img.tiles[0].tileComps[0].codeBlockW += 2; + img.tiles[0].tileComps[0].codeBlockH += 2; + for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { + if (i != 0) { + img.tiles[i].progOrder = img.tiles[0].progOrder; + img.tiles[i].nLayers = img.tiles[0].nLayers; + img.tiles[i].multiComp = img.tiles[0].multiComp; + } + for (comp = 0; comp < img.nComps; ++comp) { + if (!(i == 0 && comp == 0)) { + img.tiles[i].tileComps[comp].style = + img.tiles[0].tileComps[0].style; + img.tiles[i].tileComps[comp].nDecompLevels = + img.tiles[0].tileComps[0].nDecompLevels; + img.tiles[i].tileComps[comp].codeBlockW = + img.tiles[0].tileComps[0].codeBlockW; + img.tiles[i].tileComps[comp].codeBlockH = + img.tiles[0].tileComps[0].codeBlockH; + img.tiles[i].tileComps[comp].codeBlockStyle = + img.tiles[0].tileComps[0].codeBlockStyle; + img.tiles[i].tileComps[comp].transform = + img.tiles[0].tileComps[0].transform; + } + img.tiles[i].tileComps[comp].resLevels = + (JPXResLevel *)gmalloc( + (img.tiles[i].tileComps[comp].nDecompLevels + 1) * + sizeof(JPXResLevel)); + for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { + img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL; + } + } + } + for (r = 0; r <= img.tiles[0].tileComps[0].nDecompLevels; ++r) { + if (img.tiles[0].tileComps[0].style & 0x01) { + if (!readUByte(&precinctSize)) { + error(getPos(), "Error in JPX COD marker segment"); + return gFalse; + } + img.tiles[0].tileComps[0].resLevels[r].precinctWidth = + precinctSize & 0x0f; + img.tiles[0].tileComps[0].resLevels[r].precinctHeight = + (precinctSize >> 4) & 0x0f; + } else { + img.tiles[0].tileComps[0].resLevels[r].precinctWidth = 15; + img.tiles[0].tileComps[0].resLevels[r].precinctHeight = 15; + } + } + for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { + for (comp = 0; comp < img.nComps; ++comp) { + if (!(i == 0 && comp == 0)) { + for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { + img.tiles[i].tileComps[comp].resLevels[r].precinctWidth = + img.tiles[0].tileComps[0].resLevels[r].precinctWidth; + img.tiles[i].tileComps[comp].resLevels[r].precinctHeight = + img.tiles[0].tileComps[0].resLevels[r].precinctHeight; + } + } + } + } + haveCOD = gTrue; + break; + case 0x53: // COC - coding style component + if (!haveCOD) { + error(getPos(), "JPX COC marker segment before COD segment"); + return gFalse; + } + if ((img.nComps > 256 && !readUWord(&comp)) || + (img.nComps <= 256 && !readUByte(&comp)) || + comp >= img.nComps || + !readUByte(&style) || + !readUByte(&img.tiles[0].tileComps[comp].nDecompLevels) || + !readUByte(&img.tiles[0].tileComps[comp].codeBlockW) || + !readUByte(&img.tiles[0].tileComps[comp].codeBlockH) || + !readUByte(&img.tiles[0].tileComps[comp].codeBlockStyle) || + !readUByte(&img.tiles[0].tileComps[comp].transform)) { + error(getPos(), "Error in JPX COC marker segment"); + return gFalse; + } + img.tiles[0].tileComps[comp].style = + (img.tiles[0].tileComps[comp].style & ~1) | (style & 1); + img.tiles[0].tileComps[comp].codeBlockW += 2; + img.tiles[0].tileComps[comp].codeBlockH += 2; + for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { + if (i != 0) { + img.tiles[i].tileComps[comp].style = + img.tiles[0].tileComps[comp].style; + img.tiles[i].tileComps[comp].nDecompLevels = + img.tiles[0].tileComps[comp].nDecompLevels; + img.tiles[i].tileComps[comp].codeBlockW = + img.tiles[0].tileComps[comp].codeBlockW; + img.tiles[i].tileComps[comp].codeBlockH = + img.tiles[0].tileComps[comp].codeBlockH; + img.tiles[i].tileComps[comp].codeBlockStyle = + img.tiles[0].tileComps[comp].codeBlockStyle; + img.tiles[i].tileComps[comp].transform = + img.tiles[0].tileComps[comp].transform; + } + img.tiles[i].tileComps[comp].resLevels = + (JPXResLevel *)grealloc( + img.tiles[i].tileComps[comp].resLevels, + (img.tiles[i].tileComps[comp].nDecompLevels + 1) * + sizeof(JPXResLevel)); + for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { + img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL; + } + } + for (r = 0; r <= img.tiles[0].tileComps[comp].nDecompLevels; ++r) { + if (img.tiles[0].tileComps[comp].style & 0x01) { + if (!readUByte(&precinctSize)) { + error(getPos(), "Error in JPX COD marker segment"); + return gFalse; + } + img.tiles[0].tileComps[comp].resLevels[r].precinctWidth = + precinctSize & 0x0f; + img.tiles[0].tileComps[comp].resLevels[r].precinctHeight = + (precinctSize >> 4) & 0x0f; + } else { + img.tiles[0].tileComps[comp].resLevels[r].precinctWidth = 15; + img.tiles[0].tileComps[comp].resLevels[r].precinctHeight = 15; + } + } + for (i = 1; i < img.nXTiles * img.nYTiles; ++i) { + for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { + img.tiles[i].tileComps[comp].resLevels[r].precinctWidth = + img.tiles[0].tileComps[comp].resLevels[r].precinctWidth; + img.tiles[i].tileComps[comp].resLevels[r].precinctHeight = + img.tiles[0].tileComps[comp].resLevels[r].precinctHeight; + } + } + break; + case 0x5c: // QCD - quantization default + if (!readUByte(&img.tiles[0].tileComps[0].quantStyle)) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x00) { + img.tiles[0].tileComps[0].nQuantSteps = segLen - 3; + img.tiles[0].tileComps[0].quantSteps = + (Guint *)grealloc(img.tiles[0].tileComps[0].quantSteps, + img.tiles[0].tileComps[0].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) { + if (!readUByte(&img.tiles[0].tileComps[0].quantSteps[i])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } + } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x01) { + img.tiles[0].tileComps[0].nQuantSteps = 1; + img.tiles[0].tileComps[0].quantSteps = + (Guint *)grealloc(img.tiles[0].tileComps[0].quantSteps, + img.tiles[0].tileComps[0].nQuantSteps * + sizeof(Guint)); + if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[0])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x02) { + img.tiles[0].tileComps[0].nQuantSteps = (segLen - 3) / 2; + img.tiles[0].tileComps[0].quantSteps = + (Guint *)grealloc(img.tiles[0].tileComps[0].quantSteps, + img.tiles[0].tileComps[0].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) { + if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[i])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } + } else { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { + for (comp = 0; comp < img.nComps; ++comp) { + if (!(i == 0 && comp == 0)) { + img.tiles[i].tileComps[comp].quantStyle = + img.tiles[0].tileComps[0].quantStyle; + img.tiles[i].tileComps[comp].nQuantSteps = + img.tiles[0].tileComps[0].nQuantSteps; + img.tiles[i].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[i].tileComps[comp].quantSteps, + img.tiles[0].tileComps[0].nQuantSteps * + sizeof(Guint)); + for (j = 0; j < img.tiles[0].tileComps[0].nQuantSteps; ++j) { + img.tiles[i].tileComps[comp].quantSteps[j] = + img.tiles[0].tileComps[0].quantSteps[j]; + } + } + } + } + haveQCD = gTrue; + break; + case 0x5d: // QCC - quantization component + if (!haveQCD) { + error(getPos(), "JPX QCC marker segment before QCD segment"); + return gFalse; + } + if ((img.nComps > 256 && !readUWord(&comp)) || + (img.nComps <= 256 && !readUByte(&comp)) || + comp >= img.nComps || + !readUByte(&img.tiles[0].tileComps[comp].quantStyle)) { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x00) { + img.tiles[0].tileComps[comp].nQuantSteps = + segLen - (img.nComps > 256 ? 5 : 4); + img.tiles[0].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[0].tileComps[comp].quantSteps, + img.tiles[0].tileComps[comp].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) { + if (!readUByte(&img.tiles[0].tileComps[comp].quantSteps[i])) { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + } + } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x01) { + img.tiles[0].tileComps[comp].nQuantSteps = 1; + img.tiles[0].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[0].tileComps[comp].quantSteps, + img.tiles[0].tileComps[comp].nQuantSteps * + sizeof(Guint)); + if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[0])) { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x02) { + img.tiles[0].tileComps[comp].nQuantSteps = + (segLen - (img.nComps > 256 ? 5 : 4)) / 2; + img.tiles[0].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[0].tileComps[comp].quantSteps, + img.tiles[0].tileComps[comp].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) { + if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[i])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } + } else { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + for (i = 1; i < img.nXTiles * img.nYTiles; ++i) { + img.tiles[i].tileComps[comp].quantStyle = + img.tiles[0].tileComps[comp].quantStyle; + img.tiles[i].tileComps[comp].nQuantSteps = + img.tiles[0].tileComps[comp].nQuantSteps; + img.tiles[i].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[i].tileComps[comp].quantSteps, + img.tiles[0].tileComps[comp].nQuantSteps * + sizeof(Guint)); + for (j = 0; j < img.tiles[0].tileComps[comp].nQuantSteps; ++j) { + img.tiles[i].tileComps[comp].quantSteps[j] = + img.tiles[0].tileComps[comp].quantSteps[j]; + } + } + break; + case 0x5e: // RGN - region of interest +#if 1 //~ ROI is unimplemented + fprintf(stderr, "RGN\n"); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PPM marker segment"); + return gFalse; + } + } +#else + if ((img.nComps > 256 && !readUWord(&comp)) || + (img.nComps <= 256 && !readUByte(&comp)) || + comp >= img.nComps || + !readUByte(&compInfo[comp].defROI.style) || + !readUByte(&compInfo[comp].defROI.shift)) { + error(getPos(), "Error in JPX RGN marker segment"); + return gFalse; + } +#endif + break; + case 0x5f: // POC - progression order change +#if 1 //~ progression order changes are unimplemented + fprintf(stderr, "POC\n"); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PPM marker segment"); + return gFalse; + } + } +#else + nProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7); + progs = (JPXProgOrder *)gmalloc(nProgs * sizeof(JPXProgOrder)); + for (i = 0; i < nProgs; ++i) { + if (!readUByte(&progs[i].startRes) || + !(img.nComps > 256 && readUWord(&progs[i].startComp)) || + !(img.nComps <= 256 && readUByte(&progs[i].startComp)) || + !readUWord(&progs[i].endLayer) || + !readUByte(&progs[i].endRes) || + !(img.nComps > 256 && readUWord(&progs[i].endComp)) || + !(img.nComps <= 256 && readUByte(&progs[i].endComp)) || + !readUByte(&progs[i].progOrder)) { + error(getPos(), "Error in JPX POC marker segment"); + return gFalse; + } + } +#endif + break; + case 0x60: // PPM - packed packet headers, main header +#if 1 //~ packed packet headers are unimplemented + fprintf(stderr, "PPM\n"); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PPM marker segment"); + return gFalse; + } + } +#endif + break; + case 0x55: // TLM - tile-part lengths + // skipped + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX TLM marker segment"); + return gFalse; + } + } + break; + case 0x57: // PLM - packet length, main header + // skipped + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PLM marker segment"); + return gFalse; + } + } + break; + case 0x63: // CRG - component registration + // skipped + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX CRG marker segment"); + return gFalse; + } + } + break; + case 0x64: // COM - comment + // skipped + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX COM marker segment"); + return gFalse; + } + } + break; + case 0x90: // SOT - start of tile + haveSOT = gTrue; + break; + default: + error(getPos(), "Unknown marker segment %02x in JPX stream", segType); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + break; + } + } + break; + } + } while (!haveSOT); + + if (!haveSIZ) { + error(getPos(), "Missing SIZ marker segment in JPX stream"); + return gFalse; + } + if (!haveCOD) { + error(getPos(), "Missing COD marker segment in JPX stream"); + return gFalse; + } + if (!haveQCD) { + error(getPos(), "Missing QCD marker segment in JPX stream"); + return gFalse; + } + + //----- read the tile-parts + while (1) { + if (!readTilePart()) { + return gFalse; + } + if (!readMarkerHdr(&segType, &segLen)) { + error(getPos(), "Error in JPX codestream"); + return gFalse; + } + if (segType != 0x90) { // SOT - start of tile + break; + } + } + + if (segType != 0xd9) { // EOC - end of codestream + error(getPos(), "Missing EOC marker in JPX codestream"); + return gFalse; + } + + //----- finish decoding the image + for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { + tile = &img.tiles[i]; + for (comp = 0; comp < img.nComps; ++comp) { + tileComp = &tile->tileComps[comp]; + inverseTransform(tileComp); + } + if (!inverseMultiCompAndDC(tile)) { + return gFalse; + } + } + + //~ can free memory below tileComps here, and also tileComp.buf + + return gTrue; +} + +GBool JPXStream::readTilePart() { + JPXTile *tile; + JPXTileComp *tileComp; + JPXResLevel *resLevel; + JPXPrecinct *precinct; + JPXSubband *subband; + JPXCodeBlock *cb; + GBool haveSOD; + Guint tileIdx, tilePartLen, tilePartIdx, nTileParts; + GBool tilePartToEOC; + Guint precinctSize, style; + Guint n, nSBs, nx, ny, sbx0, sby0, comp, segLen; + Guint i, j, k, cbX, cbY, r, pre, sb, cbi; + int segType, level; + + // process the SOT marker segment + if (!readUWord(&tileIdx) || + !readULong(&tilePartLen) || + !readUByte(&tilePartIdx) || + !readUByte(&nTileParts)) { + error(getPos(), "Error in JPX SOT marker segment"); + return gFalse; + } + + if (tileIdx >= img.nXTiles * img.nYTiles) { + error(getPos(), "Weird tile index in JPX stream"); + return gFalse; + } + + tilePartToEOC = tilePartLen == 0; + tilePartLen -= 12; // subtract size of SOT segment + + haveSOD = gFalse; + do { + if (!readMarkerHdr(&segType, &segLen)) { + error(getPos(), "Error in JPX tile-part codestream"); + return gFalse; + } + tilePartLen -= 2 + segLen; + switch (segType) { + case 0x52: // COD - coding style default + if (!readUByte(&img.tiles[tileIdx].tileComps[0].style) || + !readUByte(&img.tiles[tileIdx].progOrder) || + !readUWord(&img.tiles[tileIdx].nLayers) || + !readUByte(&img.tiles[tileIdx].multiComp) || + !readUByte(&img.tiles[tileIdx].tileComps[0].nDecompLevels) || + !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockW) || + !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockH) || + !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockStyle) || + !readUByte(&img.tiles[tileIdx].tileComps[0].transform)) { + error(getPos(), "Error in JPX COD marker segment"); + return gFalse; + } + img.tiles[tileIdx].tileComps[0].codeBlockW += 2; + img.tiles[tileIdx].tileComps[0].codeBlockH += 2; + for (comp = 0; comp < img.nComps; ++comp) { + if (comp != 0) { + img.tiles[tileIdx].tileComps[comp].style = + img.tiles[tileIdx].tileComps[0].style; + img.tiles[tileIdx].tileComps[comp].nDecompLevels = + img.tiles[tileIdx].tileComps[0].nDecompLevels; + img.tiles[tileIdx].tileComps[comp].codeBlockW = + img.tiles[tileIdx].tileComps[0].codeBlockW; + img.tiles[tileIdx].tileComps[comp].codeBlockH = + img.tiles[tileIdx].tileComps[0].codeBlockH; + img.tiles[tileIdx].tileComps[comp].codeBlockStyle = + img.tiles[tileIdx].tileComps[0].codeBlockStyle; + img.tiles[tileIdx].tileComps[comp].transform = + img.tiles[tileIdx].tileComps[0].transform; + } + img.tiles[tileIdx].tileComps[comp].resLevels = + (JPXResLevel *)grealloc( + img.tiles[tileIdx].tileComps[comp].resLevels, + (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1) * + sizeof(JPXResLevel)); + for (r = 0; + r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; + ++r) { + img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL; + } + } + for (r = 0; r <= img.tiles[tileIdx].tileComps[0].nDecompLevels; ++r) { + if (img.tiles[tileIdx].tileComps[0].style & 0x01) { + if (!readUByte(&precinctSize)) { + error(getPos(), "Error in JPX COD marker segment"); + return gFalse; + } + img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth = + precinctSize & 0x0f; + img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight = + (precinctSize >> 4) & 0x0f; + } else { + img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth = 15; + img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight = 15; + } + } + for (comp = 1; comp < img.nComps; ++comp) { + for (r = 0; + r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; + ++r) { + img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = + img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth; + img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = + img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight; + } + } + break; + case 0x53: // COC - coding style component + if ((img.nComps > 256 && !readUWord(&comp)) || + (img.nComps <= 256 && !readUByte(&comp)) || + comp >= img.nComps || + !readUByte(&style) || + !readUByte(&img.tiles[tileIdx].tileComps[comp].nDecompLevels) || + !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockW) || + !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockH) || + !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockStyle) || + !readUByte(&img.tiles[tileIdx].tileComps[comp].transform)) { + error(getPos(), "Error in JPX COC marker segment"); + return gFalse; + } + img.tiles[tileIdx].tileComps[comp].style = + (img.tiles[tileIdx].tileComps[comp].style & ~1) | (style & 1); + img.tiles[tileIdx].tileComps[comp].codeBlockW += 2; + img.tiles[tileIdx].tileComps[comp].codeBlockH += 2; + img.tiles[tileIdx].tileComps[comp].resLevels = + (JPXResLevel *)grealloc( + img.tiles[tileIdx].tileComps[comp].resLevels, + (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1) * + sizeof(JPXResLevel)); + for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) { + img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL; + } + for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) { + if (img.tiles[tileIdx].tileComps[comp].style & 0x01) { + if (!readUByte(&precinctSize)) { + error(getPos(), "Error in JPX COD marker segment"); + return gFalse; + } + img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = + precinctSize & 0x0f; + img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = + (precinctSize >> 4) & 0x0f; + } else { + img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = 15; + img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = 15; + } + } + break; + case 0x5c: // QCD - quantization default + if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantStyle)) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x00) { + img.tiles[tileIdx].tileComps[0].nQuantSteps = + segLen - 3; + img.tiles[tileIdx].tileComps[0].quantSteps = + (Guint *)grealloc(img.tiles[tileIdx].tileComps[0].quantSteps, + img.tiles[tileIdx].tileComps[0].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) { + if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } + } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x01) { + img.tiles[tileIdx].tileComps[0].nQuantSteps = 1; + img.tiles[tileIdx].tileComps[0].quantSteps = + (Guint *)grealloc(img.tiles[tileIdx].tileComps[0].quantSteps, + img.tiles[tileIdx].tileComps[0].nQuantSteps * + sizeof(Guint)); + if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[0])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x02) { + img.tiles[tileIdx].tileComps[0].nQuantSteps = (segLen - 3) / 2; + img.tiles[tileIdx].tileComps[0].quantSteps = + (Guint *)grealloc(img.tiles[tileIdx].tileComps[0].quantSteps, + img.tiles[tileIdx].tileComps[0].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) { + if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } + } else { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + for (comp = 1; comp < img.nComps; ++comp) { + img.tiles[tileIdx].tileComps[comp].quantStyle = + img.tiles[tileIdx].tileComps[0].quantStyle; + img.tiles[tileIdx].tileComps[comp].nQuantSteps = + img.tiles[tileIdx].tileComps[0].nQuantSteps; + img.tiles[tileIdx].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps, + img.tiles[tileIdx].tileComps[0].nQuantSteps * + sizeof(Guint)); + for (j = 0; j < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++j) { + img.tiles[tileIdx].tileComps[comp].quantSteps[j] = + img.tiles[tileIdx].tileComps[0].quantSteps[j]; + } + } + break; + case 0x5d: // QCC - quantization component + if ((img.nComps > 256 && !readUWord(&comp)) || + (img.nComps <= 256 && !readUByte(&comp)) || + comp >= img.nComps || + !readUByte(&img.tiles[tileIdx].tileComps[comp].quantStyle)) { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) == 0x00) { + img.tiles[tileIdx].tileComps[comp].nQuantSteps = + segLen - (img.nComps > 256 ? 5 : 4); + img.tiles[tileIdx].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps, + img.tiles[tileIdx].tileComps[comp].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) { + if (!readUByte(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + } + } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) + == 0x01) { + img.tiles[tileIdx].tileComps[comp].nQuantSteps = 1; + img.tiles[tileIdx].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps, + img.tiles[tileIdx].tileComps[comp].nQuantSteps * + sizeof(Guint)); + if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[0])) { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) + == 0x02) { + img.tiles[tileIdx].tileComps[comp].nQuantSteps = + (segLen - (img.nComps > 256 ? 5 : 4)) / 2; + img.tiles[tileIdx].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps, + img.tiles[tileIdx].tileComps[comp].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) { + if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } + } else { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + break; + case 0x5e: // RGN - region of interest +#if 1 //~ ROI is unimplemented + fprintf(stderr, "RGN\n"); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PPM marker segment"); + return gFalse; + } + } +#else + if ((img.nComps > 256 && !readUWord(&comp)) || + (img.nComps <= 256 && !readUByte(&comp)) || + comp >= img.nComps || + !readUByte(&compInfo[comp].roi.style) || + !readUByte(&compInfo[comp].roi.shift)) { + error(getPos(), "Error in JPX RGN marker segment"); + return gFalse; + } +#endif + break; + case 0x5f: // POC - progression order change +#if 1 //~ progression order changes are unimplemented + fprintf(stderr, "POC\n"); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PPM marker segment"); + return gFalse; + } + } +#else + nTileProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7); + tileProgs = (JPXProgOrder *)gmalloc(nTileProgs * sizeof(JPXProgOrder)); + for (i = 0; i < nTileProgs; ++i) { + if (!readUByte(&tileProgs[i].startRes) || + !(img.nComps > 256 && readUWord(&tileProgs[i].startComp)) || + !(img.nComps <= 256 && readUByte(&tileProgs[i].startComp)) || + !readUWord(&tileProgs[i].endLayer) || + !readUByte(&tileProgs[i].endRes) || + !(img.nComps > 256 && readUWord(&tileProgs[i].endComp)) || + !(img.nComps <= 256 && readUByte(&tileProgs[i].endComp)) || + !readUByte(&tileProgs[i].progOrder)) { + error(getPos(), "Error in JPX POC marker segment"); + return gFalse; + } + } +#endif + break; + case 0x61: // PPT - packed packet headers, tile-part hdr +#if 1 //~ packed packet headers are unimplemented + fprintf(stderr, "PPT\n"); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PPT marker segment"); + return gFalse; + } + } +#endif + case 0x58: // PLT - packet length, tile-part header + // skipped + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PLT marker segment"); + return gFalse; + } + } + break; + case 0x64: // COM - comment + // skipped + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX COM marker segment"); + return gFalse; + } + } + break; + case 0x93: // SOD - start of data + haveSOD = gTrue; + break; + default: + error(getPos(), "Unknown marker segment %02x in JPX tile-part stream", + segType); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + break; + } + } + break; + } + } while (!haveSOD); + + //----- initialize the tile, precincts, and code-blocks + if (tilePartIdx == 0) { + tile = &img.tiles[tileIdx]; + i = tileIdx / img.nXTiles; + j = tileIdx % img.nXTiles; + if ((tile->x0 = img.xTileOffset + j * img.xTileSize) < img.xOffset) { + tile->x0 = img.xOffset; + } + if ((tile->y0 = img.yTileOffset + i * img.yTileSize) < img.yOffset) { + tile->y0 = img.yOffset; + } + if ((tile->x1 = img.xTileOffset + (j + 1) * img.xTileSize) > img.xSize) { + tile->x1 = img.xSize; + } + if ((tile->y1 = img.yTileOffset + (i + 1) * img.yTileSize) > img.ySize) { + tile->y1 = img.ySize; + } + tile->comp = 0; + tile->res = 0; + tile->precinct = 0; + tile->layer = 0; + tile->maxNDecompLevels = 0; + for (comp = 0; comp < img.nComps; ++comp) { + tileComp = &tile->tileComps[comp]; + if (tileComp->nDecompLevels > tile->maxNDecompLevels) { + tile->maxNDecompLevels = tileComp->nDecompLevels; + } + tileComp->x0 = jpxCeilDiv(tile->x0, tileComp->hSep); + tileComp->y0 = jpxCeilDiv(tile->y0, tileComp->hSep); + tileComp->x1 = jpxCeilDiv(tile->x1, tileComp->hSep); + tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->hSep); + tileComp->cbW = 1 << tileComp->codeBlockW; + tileComp->cbH = 1 << tileComp->codeBlockH; + tileComp->data = (int *)gmalloc((tileComp->x1 - tileComp->x0) * + (tileComp->y1 - tileComp->y0) * + sizeof(int)); + if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) { + n = tileComp->x1 - tileComp->x0; + } else { + n = tileComp->y1 - tileComp->y0; + } + tileComp->buf = (int *)gmalloc((n + 8) * sizeof(int)); + for (r = 0; r <= tileComp->nDecompLevels; ++r) { + resLevel = &tileComp->resLevels[r]; + k = r == 0 ? tileComp->nDecompLevels + : tileComp->nDecompLevels - r + 1; + resLevel->x0 = jpxCeilDivPow2(tileComp->x0, k); + resLevel->y0 = jpxCeilDivPow2(tileComp->y0, k); + resLevel->x1 = jpxCeilDivPow2(tileComp->x1, k); + resLevel->y1 = jpxCeilDivPow2(tileComp->y1, k); + if (r == 0) { + resLevel->bx0[0] = resLevel->x0; + resLevel->by0[0] = resLevel->y0; + resLevel->bx1[0] = resLevel->x1; + resLevel->by1[0] = resLevel->y1; + } else { + resLevel->bx0[0] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k); + resLevel->by0[0] = resLevel->y0; + resLevel->bx1[0] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k); + resLevel->by1[0] = resLevel->y1; + resLevel->bx0[1] = resLevel->x0; + resLevel->by0[1] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k); + resLevel->bx1[1] = resLevel->x1; + resLevel->by1[1] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k); + resLevel->bx0[2] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k); + resLevel->by0[2] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k); + resLevel->bx1[2] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k); + resLevel->by1[2] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k); + } + resLevel->precincts = (JPXPrecinct *)gmalloc(1 * sizeof(JPXPrecinct)); + for (pre = 0; pre < 1; ++pre) { + precinct = &resLevel->precincts[pre]; + precinct->x0 = resLevel->x0; + precinct->y0 = resLevel->y0; + precinct->x1 = resLevel->x1; + precinct->y1 = resLevel->y1; + nSBs = r == 0 ? 1 : 3; + precinct->subbands = + (JPXSubband *)gmalloc(nSBs * sizeof(JPXSubband)); + for (sb = 0; sb < nSBs; ++sb) { + subband = &precinct->subbands[sb]; + subband->x0 = resLevel->bx0[sb]; + subband->y0 = resLevel->by0[sb]; + subband->x1 = resLevel->bx1[sb]; + subband->y1 = resLevel->by1[sb]; + subband->nXCBs = jpxCeilDivPow2(subband->x1, + tileComp->codeBlockW) + - jpxFloorDivPow2(subband->x0, + tileComp->codeBlockW); + subband->nYCBs = jpxCeilDivPow2(subband->y1, + tileComp->codeBlockH) + - jpxFloorDivPow2(subband->y0, + tileComp->codeBlockH); + n = subband->nXCBs > subband->nYCBs ? subband->nXCBs + : subband->nYCBs; + for (subband->maxTTLevel = 0, --n; + n; + ++subband->maxTTLevel, n >>= 1) ; + n = 0; + for (level = subband->maxTTLevel; level >= 0; --level) { + nx = jpxCeilDivPow2(subband->nXCBs, level); + ny = jpxCeilDivPow2(subband->nYCBs, level); + n += nx * ny; + } + subband->inclusion = + (JPXTagTreeNode *)gmalloc(n * sizeof(JPXTagTreeNode)); + subband->zeroBitPlane = + (JPXTagTreeNode *)gmalloc(n * sizeof(JPXTagTreeNode)); + for (k = 0; k < n; ++k) { + subband->inclusion[k].finished = gFalse; + subband->inclusion[k].val = 0; + subband->zeroBitPlane[k].finished = gFalse; + subband->zeroBitPlane[k].val = 0; + } + subband->cbs = (JPXCodeBlock *)gmalloc(subband->nXCBs * + subband->nYCBs * + sizeof(JPXCodeBlock)); + sbx0 = jpxFloorDivPow2(subband->x0, tileComp->codeBlockW); + sby0 = jpxFloorDivPow2(subband->y0, tileComp->codeBlockH); + cb = subband->cbs; + for (cbY = 0; cbY < subband->nYCBs; ++cbY) { + for (cbX = 0; cbX < subband->nXCBs; ++cbX) { + cb->x0 = (sbx0 + cbX) << tileComp->codeBlockW; + cb->x1 = cb->x0 + tileComp->cbW; + if (subband->x0 > cb->x0) { + cb->x0 = subband->x0; + } + if (subband->x1 < cb->x1) { + cb->x1 = subband->x1; + } + cb->y0 = (sby0 + cbY) << tileComp->codeBlockH; + cb->y1 = cb->y0 + tileComp->cbH; + if (subband->y0 > cb->y0) { + cb->y0 = subband->y0; + } + if (subband->y1 < cb->y1) { + cb->y1 = subband->y1; + } + cb->seen = gFalse; + cb->lBlock = 3; + cb->nextPass = jpxPassCleanup; + cb->nZeroBitPlanes = 0; + cb->coeffs = + (JPXCoeff *)gmalloc((1 << (tileComp->codeBlockW + + tileComp->codeBlockH)) + * sizeof(JPXCoeff)); + for (cbi = 0; + cbi < (Guint)(1 << (tileComp->codeBlockW + + tileComp->codeBlockH)); + ++cbi) { + cb->coeffs[cbi].flags = 0; + cb->coeffs[cbi].len = 0; + cb->coeffs[cbi].mag = 0; + } + cb->stats = new JArithmeticDecoderStats(jpxNContexts); + cb->stats->setEntry(jpxContextSigProp, 4, 0); + cb->stats->setEntry(jpxContextRunLength, 3, 0); + cb->stats->setEntry(jpxContextUniform, 46, 0); + ++cb; + } + } + } + } + } + } + } + + return readTilePartData(tileIdx, tilePartLen, tilePartToEOC); +} + +GBool JPXStream::readTilePartData(Guint tileIdx, + Guint tilePartLen, GBool tilePartToEOC) { + JPXTile *tile; + JPXTileComp *tileComp; + JPXResLevel *resLevel; + JPXPrecinct *precinct; + JPXSubband *subband; + JPXCodeBlock *cb; + Guint ttVal; + Guint bits, cbX, cbY, nx, ny, i, j, n, sb; + int level; + + tile = &img.tiles[tileIdx]; + + // read all packets from this tile-part + while (1) { + if (tilePartToEOC) { + //~ peek for an EOC marker + } else if (tilePartLen == 0) { + break; + } + + tileComp = &tile->tileComps[tile->comp]; + resLevel = &tileComp->resLevels[tile->res]; + precinct = &resLevel->precincts[tile->precinct]; + + //----- packet header + + // zero-length flag + if (!readBits(1, &bits)) { + goto err; + } + if (!bits) { + // packet is empty -- clear all code-block inclusion flags + for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) { + subband = &precinct->subbands[sb]; + for (cbY = 0; cbY < subband->nYCBs; ++cbY) { + for (cbX = 0; cbX < subband->nXCBs; ++cbX) { + cb = &subband->cbs[cbY * subband->nXCBs + cbX]; + cb->included = gFalse; + } + } + } + } else { + + for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) { + subband = &precinct->subbands[sb]; + for (cbY = 0; cbY < subband->nYCBs; ++cbY) { + for (cbX = 0; cbX < subband->nXCBs; ++cbX) { + cb = &subband->cbs[cbY * subband->nXCBs + cbX]; + + // skip code-blocks with no coefficients + if (cb->x0 >= cb->x1 || cb->y0 >= cb->y1) { + cb->included = gFalse; + continue; + } + + // code-block inclusion + if (cb->seen) { + if (!readBits(1, &cb->included)) { + goto err; + } + } else { + ttVal = 0; + i = 0; + for (level = subband->maxTTLevel; level >= 0; --level) { + nx = jpxCeilDivPow2(subband->nXCBs, level); + ny = jpxCeilDivPow2(subband->nYCBs, level); + j = i + (cbY >> level) * nx + (cbX >> level); + if (!subband->inclusion[j].finished && + !subband->inclusion[j].val) { + subband->inclusion[j].val = ttVal; + } else { + ttVal = subband->inclusion[j].val; + } + while (!subband->inclusion[j].finished && + ttVal <= tile->layer) { + if (!readBits(1, &bits)) { + goto err; + } + if (bits == 1) { + subband->inclusion[j].finished = gTrue; + } else { + ++ttVal; + } + } + subband->inclusion[j].val = ttVal; + if (ttVal > tile->layer) { + break; + } + i += nx * ny; + } + cb->included = level < 0; + } + + if (cb->included) { + + // zero bit-plane count + if (!cb->seen) { + ttVal = 0; + i = 0; + for (level = subband->maxTTLevel; level >= 0; --level) { + nx = jpxCeilDivPow2(subband->nXCBs, level); + ny = jpxCeilDivPow2(subband->nYCBs, level); + j = i + (cbY >> level) * nx + (cbX >> level); + if (!subband->zeroBitPlane[j].finished && + !subband->zeroBitPlane[j].val) { + subband->zeroBitPlane[j].val = ttVal; + } else { + ttVal = subband->zeroBitPlane[j].val; + } + while (!subband->zeroBitPlane[j].finished) { + if (!readBits(1, &bits)) { + goto err; + } + if (bits == 1) { + subband->zeroBitPlane[j].finished = gTrue; + } else { + ++ttVal; + } + } + subband->zeroBitPlane[j].val = ttVal; + i += nx * ny; + } + cb->nZeroBitPlanes = ttVal; + } + + // number of coding passes + if (!readBits(1, &bits)) { + goto err; + } + if (bits == 0) { + cb->nCodingPasses = 1; + } else { + if (!readBits(1, &bits)) { + goto err; + } + if (bits == 0) { + cb->nCodingPasses = 2; + } else { + if (!readBits(2, &bits)) { + goto err; + } + if (bits < 3) { + cb->nCodingPasses = 3 + bits; + } else { + if (!readBits(5, &bits)) { + goto err; + } + if (bits < 31) { + cb->nCodingPasses = 6 + bits; + } else { + if (!readBits(7, &bits)) { + goto err; + } + cb->nCodingPasses = 37 + bits; + } + } + } + } + + // update Lblock + while (1) { + if (!readBits(1, &bits)) { + goto err; + } + if (!bits) { + break; + } + ++cb->lBlock; + } + + // length of compressed data + //~ deal with multiple codeword segments + for (n = cb->lBlock, i = cb->nCodingPasses >> 1; + i; + ++n, i >>= 1) ; + if (!readBits(n, &cb->dataLen)) { + goto err; + } + } + } + } + } + } + tilePartLen -= byteCount; + clearBitBuf(); + + //----- packet data + + for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) { + subband = &precinct->subbands[sb]; + for (cbY = 0; cbY < subband->nYCBs; ++cbY) { + for (cbX = 0; cbX < subband->nXCBs; ++cbX) { + cb = &subband->cbs[cbY * subband->nXCBs + cbX]; + if (cb->included) { + if (!readCodeBlockData(tileComp, resLevel, precinct, subband, + tile->res, sb, cb)) { + return gFalse; + } + tilePartLen -= cb->dataLen; + cb->seen = gTrue; + } + } + } + } + + //----- next packet + + switch (tile->progOrder) { + case 0: // layer, resolution level, component, precinct + if (++tile->comp == img.nComps) { + tile->comp = 0; + if (++tile->res == tile->maxNDecompLevels + 1) { + tile->res = 0; + if (++tile->layer == tile->nLayers) { + tile->layer = 0; + } + } + } + break; + case 1: // resolution level, layer, component, precinct + if (++tile->comp == img.nComps) { + tile->comp = 0; + if (++tile->layer == tile->nLayers) { + tile->layer = 0; + if (++tile->res == tile->maxNDecompLevels + 1) { + tile->res = 0; + } + } + } + break; + case 2: // resolution level, precinct, component, layer + //~ this isn't correct -- see B.12.1.3 + if (++tile->layer == tile->nLayers) { + tile->layer = 0; + if (++tile->comp == img.nComps) { + tile->comp = 0; + if (++tile->res == tile->maxNDecompLevels + 1) { + tile->res = 0; + } + } + } + break; + case 3: // precinct, component, resolution level, layer + //~ this isn't correct -- see B.12.1.4 + if (++tile->layer == tile->nLayers) { + tile->layer = 0; + if (++tile->res == tile->maxNDecompLevels + 1) { + tile->res = 0; + if (++tile->comp == img.nComps) { + tile->comp = 0; + } + } + } + break; + case 4: // component, precinct, resolution level, layer + //~ this isn't correct -- see B.12.1.5 + if (++tile->layer == tile->nLayers) { + tile->layer = 0; + if (++tile->res == tile->maxNDecompLevels + 1) { + tile->res = 0; + if (++tile->comp == img.nComps) { + tile->comp = 0; + } + } + } + break; + } + } + + return gTrue; + + err: + error(getPos(), "Error in JPX stream"); + return gFalse; +} + +GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp, + JPXResLevel *resLevel, + JPXPrecinct *precinct, + JPXSubband *subband, + Guint res, Guint sb, + JPXCodeBlock *cb) { + JPXCoeff *coeff0, *coeff1, *coeff; + JArithmeticDecoder *arithDecoder; + Guint horiz, vert, diag, all, cx, xorBit; + int horizSign, vertSign; + Guint i, x, y0, y1, y2; + + arithDecoder = new JArithmeticDecoder(); + arithDecoder->setStream(str, cb->dataLen); + arithDecoder->start(); + + for (i = 0; i < cb->nCodingPasses; ++i) { + switch (cb->nextPass) { + + //----- significance propagation pass + case jpxPassSigProp: + for (y0 = cb->y0, coeff0 = cb->coeffs; + y0 < cb->y1; + y0 += 4, coeff0 += 4 << tileComp->codeBlockW) { + for (x = cb->x0, coeff1 = coeff0; + x < cb->x1; + ++x, ++coeff1) { + for (y1 = 0, coeff = coeff1; + y1 < 4 && y0+y1 < cb->y1; + ++y1, coeff += tileComp->cbW) { + if (!(coeff->flags & jpxCoeffSignificant)) { + horiz = vert = diag = 0; + horizSign = vertSign = 2; + if (x > cb->x0) { + if (coeff[-1].flags & jpxCoeffSignificant) { + ++horiz; + horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1; + } + if (y0+y1 > cb->y0) { + diag += (coeff[-tileComp->cbW - 1].flags + >> jpxCoeffSignificantB) & 1; + } + if (y0+y1 < cb->y1 - 1) { + diag += (coeff[tileComp->cbW - 1].flags + >> jpxCoeffSignificantB) & 1; + } + } + if (x < cb->x1 - 1) { + if (coeff[1].flags & jpxCoeffSignificant) { + ++horiz; + horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1; + } + if (y0+y1 > cb->y0) { + diag += (coeff[-tileComp->cbW + 1].flags + >> jpxCoeffSignificantB) & 1; + } + if (y0+y1 < cb->y1 - 1) { + diag += (coeff[tileComp->cbW + 1].flags + >> jpxCoeffSignificantB) & 1; + } + } + if (y0+y1 > cb->y0) { + if (coeff[-tileComp->cbW].flags & jpxCoeffSignificant) { + ++vert; + vertSign += (coeff[-tileComp->cbW].flags & jpxCoeffSign) + ? -1 : 1; + } + } + if (y0+y1 < cb->y1 - 1) { + if (coeff[tileComp->cbW].flags & jpxCoeffSignificant) { + ++vert; + vertSign += (coeff[tileComp->cbW].flags & jpxCoeffSign) + ? -1 : 1; + } + } + cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb]; + if (cx != 0) { + if (arithDecoder->decodeBit(cx, cb->stats)) { + coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef; + coeff->mag = (coeff->mag << 1) | 1; + cx = signContext[horizSign][vertSign][0]; + xorBit = signContext[horizSign][vertSign][1]; + if (arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) { + coeff->flags |= jpxCoeffSign; + } + } + ++coeff->len; + coeff->flags |= jpxCoeffTouched; + } + } + } + } + } + ++cb->nextPass; + break; + + //----- magnitude refinement pass + case jpxPassMagRef: + for (y0 = cb->y0, coeff0 = cb->coeffs; + y0 < cb->y1; + y0 += 4, coeff0 += 4 << tileComp->codeBlockW) { + for (x = cb->x0, coeff1 = coeff0; + x < cb->x1; + ++x, ++coeff1) { + for (y1 = 0, coeff = coeff1; + y1 < 4 && y0+y1 < cb->y1; + ++y1, coeff += tileComp->cbW) { + if ((coeff->flags & jpxCoeffSignificant) && + !(coeff->flags & jpxCoeffTouched)) { + if (coeff->flags & jpxCoeffFirstMagRef) { + all = 0; + if (x > cb->x0) { + all += (coeff[-1].flags >> jpxCoeffSignificantB) & 1; + if (y0+y1 > cb->y0) { + all += (coeff[-tileComp->cbW - 1].flags + >> jpxCoeffSignificantB) & 1; + } + if (y0+y1 < cb->y1 - 1) { + all += (coeff[tileComp->cbW - 1].flags + >> jpxCoeffSignificantB) & 1; + } + } + if (x < cb->x1 - 1) { + all += (coeff[1].flags >> jpxCoeffSignificantB) & 1; + if (y0+y1 > cb->y0) { + all += (coeff[-tileComp->cbW + 1].flags + >> jpxCoeffSignificantB) & 1; + } + if (y0+y1 < cb->y1 - 1) { + all += (coeff[tileComp->cbW + 1].flags + >> jpxCoeffSignificantB) & 1; + } + } + if (y0+y1 > cb->y0) { + all += (coeff[-tileComp->cbW].flags + >> jpxCoeffSignificantB) & 1; + } + if (y0+y1 < cb->y1 - 1) { + all += (coeff[tileComp->cbW].flags + >> jpxCoeffSignificantB) & 1; + } + cx = all ? 15 : 14; + } else { + cx = 16; + } + coeff->mag = (coeff->mag << 1) | + arithDecoder->decodeBit(cx, cb->stats); + ++coeff->len; + coeff->flags |= jpxCoeffTouched; + coeff->flags &= ~jpxCoeffFirstMagRef; + } + } + } + } + ++cb->nextPass; + break; + + //----- cleanup pass + case jpxPassCleanup: + for (y0 = cb->y0, coeff0 = cb->coeffs; + y0 < cb->y1; + y0 += 4, coeff0 += 4 << tileComp->codeBlockW) { + for (x = cb->x0, coeff1 = coeff0; + x < cb->x1; + ++x, ++coeff1) { + y1 = 0; + if (y0 + 3 < cb->y1 && + !(coeff1->flags & jpxCoeffTouched) && + !(coeff1[tileComp->cbW].flags & jpxCoeffTouched) && + !(coeff1[2 * tileComp->cbW].flags & jpxCoeffTouched) && + !(coeff1[3 * tileComp->cbW].flags & jpxCoeffTouched) && + (x == cb->x0 || y0 == cb->y0 || + !(coeff1[-tileComp->cbW - 1].flags + & jpxCoeffSignificant)) && + (y0 == cb->y0 || + !(coeff1[-tileComp->cbW].flags & jpxCoeffSignificant)) && + (x == cb->x1 - 1 || y0 == cb->y0 || + !(coeff1[-tileComp->cbW + 1].flags & jpxCoeffSignificant)) && + (x == cb->x0 || + (!(coeff1[-1].flags & jpxCoeffSignificant) && + !(coeff1[tileComp->cbW - 1].flags + & jpxCoeffSignificant) && + !(coeff1[2 * tileComp->cbW - 1].flags + & jpxCoeffSignificant) && + !(coeff1[3 * tileComp->cbW - 1].flags + & jpxCoeffSignificant))) && + (x == cb->x1 - 1 || + (!(coeff1[1].flags & jpxCoeffSignificant) && + !(coeff1[tileComp->cbW + 1].flags + & jpxCoeffSignificant) && + !(coeff1[2 * tileComp->cbW + 1].flags + & jpxCoeffSignificant) && + !(coeff1[3 * tileComp->cbW + 1].flags + & jpxCoeffSignificant))) && + (x == cb->x0 || y0+4 == cb->y1 || + !(coeff1[4 * tileComp->cbW - 1].flags & jpxCoeffSignificant)) && + (y0+4 == cb->y1 || + !(coeff1[4 * tileComp->cbW].flags & jpxCoeffSignificant)) && + (x == cb->x1 - 1 || y0+4 == cb->y1 || + !(coeff1[4 * tileComp->cbW + 1].flags + & jpxCoeffSignificant))) { + if (arithDecoder->decodeBit(jpxContextRunLength, cb->stats)) { + y1 = arithDecoder->decodeBit(jpxContextUniform, cb->stats); + y1 = (y1 << 1) | + arithDecoder->decodeBit(jpxContextUniform, cb->stats); + for (y2 = 0, coeff = coeff1; + y2 < y1; + ++y2, coeff += tileComp->cbW) { + ++coeff->len; + } + coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef; + coeff->mag = (coeff->mag << 1) | 1; + ++coeff->len; + cx = signContext[2][2][0]; + xorBit = signContext[2][2][1]; + if (arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) { + coeff->flags |= jpxCoeffSign; + } + ++y1; + } else { + for (y1 = 0, coeff = coeff1; + y1 < 4; + ++y1, coeff += tileComp->cbW) { + ++coeff->len; + } + y1 = 4; + } + } + for (coeff = &coeff1[y1 << tileComp->codeBlockW]; + y1 < 4 && y0 + y1 < cb->y1; + ++y1, coeff += tileComp->cbW) { + if (!(coeff->flags & jpxCoeffTouched)) { + horiz = vert = diag = 0; + horizSign = vertSign = 2; + if (x > cb->x0) { + if (coeff[-1].flags & jpxCoeffSignificant) { + ++horiz; + horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1; + } + if (y0+y1 > cb->y0) { + diag += (coeff[-tileComp->cbW - 1].flags + >> jpxCoeffSignificantB) & 1; + } + if (y0+y1 < cb->y1 - 1) { + diag += (coeff[tileComp->cbW - 1].flags + >> jpxCoeffSignificantB) & 1; + } + } + if (x < cb->x1 - 1) { + if (coeff[1].flags & jpxCoeffSignificant) { + ++horiz; + horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1; + } + if (y0+y1 > cb->y0) { + diag += (coeff[-tileComp->cbW + 1].flags + >> jpxCoeffSignificantB) & 1; + } + if (y0+y1 < cb->y1 - 1) { + diag += (coeff[tileComp->cbW + 1].flags + >> jpxCoeffSignificantB) & 1; + } + } + if (y0+y1 > cb->y0) { + if (coeff[-tileComp->cbW].flags & jpxCoeffSignificant) { + ++vert; + vertSign += (coeff[-tileComp->cbW].flags & jpxCoeffSign) + ? -1 : 1; + } + } + if (y0+y1 < cb->y1 - 1) { + if (coeff[tileComp->cbW].flags & jpxCoeffSignificant) { + ++vert; + vertSign += (coeff[tileComp->cbW].flags & jpxCoeffSign) + ? -1 : 1; + } + } + cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb]; + if (arithDecoder->decodeBit(cx, cb->stats)) { + coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef; + coeff->mag = (coeff->mag << 1) | 1; + cx = signContext[horizSign][vertSign][0]; + xorBit = signContext[horizSign][vertSign][1]; + if (arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) { + coeff->flags |= jpxCoeffSign; + } + } + ++coeff->len; + } else { + coeff->flags &= ~jpxCoeffTouched; + } + } + } + } + cb->nextPass = jpxPassSigProp; + break; + } + } + + delete arithDecoder; + return gTrue; +} + +// Inverse quantization, and wavelet transform (IDWT). This also does +// the initial shift to convert to fixed point format. +void JPXStream::inverseTransform(JPXTileComp *tileComp) { + JPXResLevel *resLevel; + JPXPrecinct *precinct; + JPXSubband *subband; + JPXCodeBlock *cb; + JPXCoeff *coeff0, *coeff; + Guint qStyle, guard, eps, shift, shift2; + double mu; + int val; + int *dataPtr; + Guint nx0, ny0, nx1, ny1; + Guint r, cbX, cbY, x, y; + + //----- (NL)LL subband (resolution level 0) + + resLevel = &tileComp->resLevels[0]; + precinct = &resLevel->precincts[0]; + subband = &precinct->subbands[0]; + + // i-quant parameters + qStyle = tileComp->quantStyle & 0x1f; + guard = (tileComp->quantStyle >> 5) & 7; + if (qStyle == 0) { + eps = (tileComp->quantSteps[0] >> 3) & 0x1f; + shift = guard + eps - 1; + mu = 0; // make gcc happy + } else { + shift = guard - 1 + tileComp->prec; + mu = (double)(0x800 + (tileComp->quantSteps[0] & 0x7ff)) / 2048.0; + } + if (tileComp->transform == 0) { + shift += fracBits; + } + + // copy (NL)LL into the upper-left corner of the data array, doing + // the fixed point adjustment and dequantization along the way + cb = subband->cbs; + for (cbY = 0; cbY < subband->nYCBs; ++cbY) { + for (cbX = 0; cbX < subband->nXCBs; ++cbX) { + for (y = cb->y0, coeff0 = cb->coeffs; + y < cb->y1; + ++y, coeff0 += tileComp->cbW) { + dataPtr = &tileComp->data[(y - subband->y0) + * (tileComp->x1 - tileComp->x0) + + (cb->x0 - subband->x0)]; + for (x = cb->x0, coeff = coeff0; x < cb->x1; ++x, ++coeff) { + val = (int)coeff->mag; + if (val != 0) { + shift2 = shift - (cb->nZeroBitPlanes + coeff->len); + if (shift2 > 0) { + val = (val << shift2) + (1 << (shift2 - 1)); + } else { + val >>= -shift2; + } + if (qStyle == 0) { + if (tileComp->transform == 0) { + val &= -1 << fracBits; + } + } else { + val = (int)((double)val * mu); + } + if (coeff->flags & jpxCoeffSign) { + val = -val; + } + } + *dataPtr++ = val; + } + } + ++cb; + } + } + + //----- IDWT for each level + + for (r = 1; r <= tileComp->nDecompLevels; ++r) { + resLevel = &tileComp->resLevels[r]; + + // (n)LL is already in the upper-left corner of the + // tile-component data array -- interleave with (n)HL/LH/HH + // and inverse transform to get (n-1)LL, which will be stored + // in the upper-left corner of the tile-component data array + if (r == tileComp->nDecompLevels) { + nx0 = tileComp->x0; + ny0 = tileComp->y0; + nx1 = tileComp->x1; + ny1 = tileComp->y1; + } else { + nx0 = tileComp->resLevels[r+1].x0; + ny0 = tileComp->resLevels[r+1].y0; + nx1 = tileComp->resLevels[r+1].x1; + ny1 = tileComp->resLevels[r+1].y1; + } + inverseTransformLevel(tileComp, r, resLevel, nx0, ny0, nx1, ny1); + } +} + +// Do one level of the inverse transform: +// - take (n)LL from the tile-component data array +// - take (n)HL/LH/HH from +// - leave the resulting (n-1)LL in the tile-component data array +void JPXStream::inverseTransformLevel(JPXTileComp *tileComp, + Guint r, JPXResLevel *resLevel, + Guint nx0, Guint ny0, + Guint nx1, Guint ny1) { + JPXPrecinct *precinct; + JPXSubband *subband; + JPXCodeBlock *cb; + JPXCoeff *coeff0, *coeff; + Guint qStyle, guard, eps, shift, shift2, t; + double mu; + int val; + int *dataPtr; + Guint xo, yo; + Guint x, y, sb, cbX, cbY; + int xx, yy; + + //----- interleave + + // spread out LL + for (yy = resLevel->y1 - 1; yy >= (int)resLevel->y0; --yy) { + for (xx = resLevel->x1 - 1; xx >= (int)resLevel->x0; --xx) { + tileComp->data[(2 * yy - ny0) * (tileComp->x1 - tileComp->x0) + + (2 * xx - nx0)] = + tileComp->data[(yy - resLevel->y0) * (tileComp->x1 - tileComp->x0) + + (xx - resLevel->x0)]; + } + } + + // i-quant parameters + qStyle = tileComp->quantStyle & 0x1f; + guard = (tileComp->quantStyle >> 5) & 7; + + // interleave HL/LH/HH + precinct = &resLevel->precincts[0]; + for (sb = 0; sb < 3; ++sb) { + + // i-quant parameters + if (qStyle == 0) { + eps = (tileComp->quantSteps[3*r - 2 + sb] >> 3) & 0x1f; + shift = guard + eps - 1; + mu = 0; // make gcc happy + } else { + shift = guard + tileComp->prec; + if (sb == 2) { + ++shift; + } + t = tileComp->quantSteps[qStyle == 1 ? 0 : (3*r - 2 + sb)]; + mu = (double)(0x800 + (t & 0x7ff)) / 2048.0; + } + if (tileComp->transform == 0) { + shift += fracBits; + } + + // copy the subband coefficients into the data array, doing the + // fixed point adjustment and dequantization along the way + xo = (sb & 1) ? 0 : 1; + yo = (sb > 0) ? 1 : 0; + subband = &precinct->subbands[sb]; + cb = subband->cbs; + for (cbY = 0; cbY < subband->nYCBs; ++cbY) { + for (cbX = 0; cbX < subband->nXCBs; ++cbX) { + for (y = cb->y0, coeff0 = cb->coeffs; + y < cb->y1; + ++y, coeff0 += tileComp->cbW) { + dataPtr = &tileComp->data[(2 * y + yo - ny0) + * (tileComp->x1 - tileComp->x0) + + (2 * cb->x0 + xo - nx0)]; + for (x = cb->x0, coeff = coeff0; x < cb->x1; ++x, ++coeff) { + val = (int)coeff->mag; + if (val != 0) { + shift2 = shift - (cb->nZeroBitPlanes + coeff->len); + if (shift2 > 0) { + val = (val << shift2) + (1 << (shift2 - 1)); + } else { + val >>= -shift2; + } + if (qStyle == 0) { + if (tileComp->transform == 0) { + val &= -1 << fracBits; + } + } else { + val = (int)((double)val * mu); + } + if (coeff->flags & jpxCoeffSign) { + val = -val; + } + } + *dataPtr = val; + dataPtr += 2; + } + } + ++cb; + } + } + } + + //----- horizontal (row) transforms + dataPtr = tileComp->data; + for (y = 0; y < ny1 - ny0; ++y) { + inverseTransform1D(tileComp, dataPtr, 1, nx0, nx1); + dataPtr += tileComp->x1 - tileComp->x0; + } + + //----- vertical (column) transforms + dataPtr = tileComp->data; + for (x = 0; x < nx1 - nx0; ++x) { + inverseTransform1D(tileComp, dataPtr, + tileComp->x1 - tileComp->x0, ny0, ny1); + ++dataPtr; + } +} + +void JPXStream::inverseTransform1D(JPXTileComp *tileComp, + int *data, Guint stride, + Guint i0, Guint i1) { + int *buf; + Guint offset, end, i; + + //----- special case for length = 1 + if (i1 - i0 == 1) { + if (i0 & 1) { + *data >>= 1; + } + + } else { + + // choose an offset: this makes even buf[] indexes correspond to + // odd values of i, and vice versa + offset = 3 + (i0 & 1); + end = offset + i1 - i0; + + //----- gather + buf = tileComp->buf; + for (i = 0; i < i1 - i0; ++i) { + buf[offset + i] = data[i * stride]; + } + + //----- extend right + buf[end] = buf[end - 2]; + if (i1 - i0 == 2) { + buf[end+1] = buf[offset + 1]; + buf[end+2] = buf[offset]; + buf[end+3] = buf[offset + 1]; + } else { + buf[end+1] = buf[end - 3]; + if (i1 - i0 == 3) { + buf[end+2] = buf[offset + 1]; + buf[end+3] = buf[offset + 2]; + } else { + buf[end+2] = buf[end - 4]; + if (i1 - i0 == 4) { + buf[end+3] = buf[offset + 1]; + } else { + buf[end+3] = buf[end - 5]; + } + } + } + + //----- extend left + buf[offset - 1] = buf[offset + 1]; + buf[offset - 2] = buf[offset + 2]; + buf[offset - 3] = buf[offset + 3]; + if (offset == 4) { + buf[0] = buf[offset + 4]; + } + + //----- 9-7 irreversible filter + + if (tileComp->transform == 0) { + // step 1 (even) + for (i = 1; i <= end + 2; i += 2) { + buf[i] = (int)(idwtKappa * buf[i]); + } + // step 2 (odd) + for (i = 0; i <= end + 3; i += 2) { + buf[i] = (int)(idwtIKappa * buf[i]); + } + // step 3 (even) + for (i = 1; i <= end + 2; i += 2) { + buf[i] = (int)(buf[i] - idwtDelta * (buf[i-1] + buf[i+1])); + } + // step 4 (odd) + for (i = 2; i <= end + 1; i += 2) { + buf[i] = (int)(buf[i] - idwtGamma * (buf[i-1] + buf[i+1])); + } + // step 5 (even) + for (i = 3; i <= end; i += 2) { + buf[i] = (int)(buf[i] - idwtBeta * (buf[i-1] + buf[i+1])); + } + // step 6 (odd) + for (i = 4; i <= end - 1; i += 2) { + buf[i] = (int)(buf[i] - idwtAlpha * (buf[i-1] + buf[i+1])); + } + + //----- 5-3 reversible filter + + } else { + // step 1 (even) + for (i = 3; i <= end; i += 2) { + buf[i] -= (buf[i-1] + buf[i+1] + 2) >> 2; + } + // step 2 (odd) + for (i = 4; i < end; i += 2) { + buf[i] += (buf[i-1] + buf[i+1]) >> 1; + } + } + + //----- scatter + for (i = 0; i < i1 - i0; ++i) { + data[i * stride] = buf[offset + i]; + } + } +} + +// Inverse multi-component transform and DC level shift. This also +// converts fixed point samples back to integers. +GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) { + JPXTileComp *tileComp; + int coeff, d0, d1, d2, minVal, maxVal, zeroVal; + int *dataPtr; + Guint j, comp, x, y; + + //----- inverse multi-component transform + + if (tile->multiComp == 1) { + if (img.nComps < 3 || + tile->tileComps[0].hSep != tile->tileComps[1].hSep || + tile->tileComps[0].vSep != tile->tileComps[1].vSep || + tile->tileComps[1].hSep != tile->tileComps[2].hSep || + tile->tileComps[1].vSep != tile->tileComps[2].vSep) { + return gFalse; + } + + // inverse irreversible multiple component transform + if (tile->tileComps[0].transform == 0) { + j = 0; + for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) { + for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) { + d0 = tile->tileComps[0].data[j]; + d1 = tile->tileComps[1].data[j]; + d2 = tile->tileComps[2].data[j]; + tile->tileComps[0].data[j] = (int)(d0 + 1.402 * d2 + 0.5); + tile->tileComps[1].data[j] = + (int)(d0 - 0.34413 * d1 - 0.71414 * d2 + 0.5); + tile->tileComps[2].data[j] = (int)(d0 + 1.772 * d1 + 0.5); + ++j; + } + } + + // inverse reversible multiple component transform + } else { + j = 0; + for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) { + for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) { + d0 = tile->tileComps[0].data[j]; + d1 = tile->tileComps[1].data[j]; + d2 = tile->tileComps[2].data[j]; + tile->tileComps[0].data[j] = d0 - ((d2 + d1) >> 2); + tile->tileComps[1].data[j] = d2 - d1; + tile->tileComps[2].data[j] = d0 - d1; + ++j; + } + } + } + } + + //----- DC level shift + for (comp = 0; comp < img.nComps; ++comp) { + tileComp = &tile->tileComps[comp]; + + // signed: clip + if (tileComp->sgned) { + minVal = -(1 << (tileComp->prec - 1)); + maxVal = (1 << (tileComp->prec - 1)) - 1; + dataPtr = tileComp->data; + for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) { + for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) { + coeff = *dataPtr; + if (tileComp->transform == 0) { + coeff >>= fracBits; + } + if (coeff < minVal) { + coeff = minVal; + } else if (coeff > maxVal) { + coeff = maxVal; + } + *dataPtr++ = coeff; + } + } + + // unsigned: inverse DC level shift and clip + } else { + maxVal = (1 << tileComp->prec) - 1; + zeroVal = 1 << (tileComp->prec - 1); + dataPtr = tileComp->data; + for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) { + for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) { + coeff = *dataPtr; + if (tileComp->transform == 0) { + coeff >>= fracBits; + } + coeff += zeroVal; + if (coeff < 0) { + coeff = 0; + } else if (coeff > maxVal) { + coeff = maxVal; + } + *dataPtr++ = coeff; + } + } + } + } + + return gTrue; +} + +GBool JPXStream::readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen) { + Guint len, lenH; + + if (!readULong(&len) || + !readULong(boxType)) { + return gFalse; + } + if (len == 1) { + if (!readULong(&lenH) || !readULong(&len)) { + return gFalse; + } + if (lenH) { + error(getPos(), "JPX stream contains a box larger than 2^32 bytes"); + return gFalse; + } + *boxLen = len; + *dataLen = len - 16; + } else if (len == 0) { + *boxLen = 0; + *dataLen = 0; + } else { + *boxLen = len; + *dataLen = len - 8; + } + return gTrue; +} + +int JPXStream::readMarkerHdr(int *segType, Guint *segLen) { + int c; + + do { + do { + if ((c = str->getChar()) == EOF) { + return gFalse; + } + } while (c != 0xff); + do { + if ((c = str->getChar()) == EOF) { + return gFalse; + } + } while (c == 0xff); + } while (c == 0x00); + *segType = c; + if ((c >= 0x30 && c <= 0x3f) || + c == 0x4f || c == 0x92 || c == 0x93 || c == 0xd9) { + *segLen = 0; + return gTrue; + } + return readUWord(segLen); +} + +GBool JPXStream::readUByte(Guint *x) { + int c0; + + if ((c0 = str->getChar()) == EOF) { + return gFalse; + } + *x = (Guint)c0; + return gTrue; +} + +GBool JPXStream::readByte(int *x) { + int c0; + + if ((c0 = str->getChar()) == EOF) { + return gFalse; + } + *x = c0; + if (c0 & 0x80) { + *x |= -1 - 0xff; + } + return gTrue; +} + +GBool JPXStream::readUWord(Guint *x) { + int c0, c1; + + if ((c0 = str->getChar()) == EOF || + (c1 = str->getChar()) == EOF) { + return gFalse; + } + *x = (Guint)((c0 << 8) | c1); + return gTrue; +} + +GBool JPXStream::readULong(Guint *x) { + int c0, c1, c2, c3; + + if ((c0 = str->getChar()) == EOF || + (c1 = str->getChar()) == EOF || + (c2 = str->getChar()) == EOF || + (c3 = str->getChar()) == EOF) { + return gFalse; + } + *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3); + return gTrue; +} + +GBool JPXStream::readNBytes(int nBytes, GBool signd, int *x) { + int y, c, i; + + y = 0; + for (i = 0; i < nBytes; ++i) { + if ((c = str->getChar()) == EOF) { + return gFalse; + } + y = (y << 8) + c; + } + if (signd) { + if (y & (1 << (8 * nBytes - 1))) { + y |= -1 << (8 * nBytes); + } + } + *x = y; + return gTrue; +} + +GBool JPXStream::readBits(int nBits, Guint *x) { + int c; + + while (bitBufLen < nBits) { + if ((c = str->getChar()) == EOF) { + return gFalse; + } + ++byteCount; + if (bitBufSkip) { + bitBuf = (bitBuf << 7) | (c & 0x7f); + bitBufLen += 7; + } else { + bitBuf = (bitBuf << 8) | (c & 0xff); + bitBufLen += 8; + } + bitBufSkip = c == 0xff; + } + *x = (bitBuf >> (bitBufLen - nBits)) & ((1 << nBits) - 1); + bitBufLen -= nBits; + return gTrue; +} + +void JPXStream::clearBitBuf() { + bitBufLen = 0; + bitBufSkip = gFalse; + byteCount = 0; +} diff --git a/pdf/xpdf/JPXStream.h b/pdf/xpdf/JPXStream.h new file mode 100644 index 0000000..eb84fe6 --- /dev/null +++ b/pdf/xpdf/JPXStream.h @@ -0,0 +1,340 @@ +//======================================================================== +// +// JPXStream.h +// +// Copyright 2002-2003 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef JPXSTREAM_H +#define JPXSTREAM_H + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +#include "gtypes.h" +#include "Object.h" +#include "Stream.h" + +class JArithmeticDecoderStats; + +//------------------------------------------------------------------------ + +enum JPXColorSpaceType { + jpxCSBiLevel = 0, + jpxCSYCbCr1 = 1, + jpxCSYCbCr2 = 3, + jpxCSYCBCr3 = 4, + jpxCSPhotoYCC = 9, + jpxCSCMY = 11, + jpxCSCMYK = 12, + jpxCSYCCK = 13, + jpxCSCIELab = 14, + jpxCSsRGB = 16, + jpxCSGrayscale = 17, + jpxCSBiLevel2 = 18, + jpxCSCIEJab = 19, + jpxCSCISesRGB = 20, + jpxCSROMMRGB = 21, + jpxCSsRGBYCbCr = 22, + jpxCSYPbPr1125 = 23, + jpxCSYPbPr1250 = 24 +}; + +struct JPXColorSpec { + Guint meth; // method + int prec; // precedence + union { + struct { + JPXColorSpaceType type; // color space type + union { + struct { + Guint rl, ol, ra, oa, rb, ob, il; + } cieLab; + }; + } enumerated; + }; +}; + +//------------------------------------------------------------------------ + +struct JPXPalette { + Guint nEntries; // number of entries in the palette + Guint nComps; // number of components in each entry + Guint *bpc; // bits per component, for each component + int *c; // color data: + // c[i*nComps+j] = entry i, component j +}; + +//------------------------------------------------------------------------ + +struct JPXCompMap { + Guint nChannels; // number of channels + Guint *comp; // codestream components mapped to each channel + Guint *type; // 0 for direct use, 1 for palette mapping + Guint *pComp; // palette components to use +}; + +//------------------------------------------------------------------------ + +struct JPXChannelDefn { + Guint nChannels; // number of channels + Guint *idx; // channel indexes + Guint *type; // channel types + Guint *assoc; // channel associations +}; + +//------------------------------------------------------------------------ + +struct JPXTagTreeNode { + GBool finished; // true if this node is finished + Guint val; // current value +}; + +//------------------------------------------------------------------------ + +struct JPXCoeff { + Gushort flags; // flag bits + Gushort len; // number of significant bits in mag + Guint mag; // magnitude value +}; + +// coefficient flags +#define jpxCoeffSignificantB 0 +#define jpxCoeffTouchedB 1 +#define jpxCoeffFirstMagRefB 2 +#define jpxCoeffSignB 7 +#define jpxCoeffSignificant (1 << jpxCoeffSignificantB) +#define jpxCoeffTouched (1 << jpxCoeffTouchedB) +#define jpxCoeffFirstMagRef (1 << jpxCoeffFirstMagRefB) +#define jpxCoeffSign (1 << jpxCoeffSignB) + +//------------------------------------------------------------------------ + +struct JPXCodeBlock { + //----- size + Guint x0, y0, x1, y1; // bounds + + //----- persistent state + GBool seen; // true if this code-block has already + // been seen + Guint lBlock; // base number of bits used for pkt data length + Guint nextPass; // next coding pass + + //---- info from first packet + Guint nZeroBitPlanes; // number of zero bit planes + + //----- info for the current packet + Guint included; // code-block inclusion in this packet: + // 0=not included, 1=included + Guint nCodingPasses; // number of coding passes in this pkt + Guint dataLen; // pkt data length + + //----- coefficient data + JPXCoeff *coeffs; // the coefficients + JArithmeticDecoderStats // arithmetic decoder stats + *stats; +}; + +//------------------------------------------------------------------------ + +struct JPXSubband { + //----- computed + Guint x0, y0, x1, y1; // bounds + Guint nXCBs, nYCBs; // number of code-blocks in the x and y + // directions + + //----- tag trees + Guint maxTTLevel; // max tag tree level + JPXTagTreeNode *inclusion; // inclusion tag tree for each subband + JPXTagTreeNode *zeroBitPlane; // zero-bit plane tag tree for each + // subband + + //----- children + JPXCodeBlock *cbs; // the code-blocks (len = nXCBs * nYCBs) +}; + +//------------------------------------------------------------------------ + +struct JPXPrecinct { + //----- computed + Guint x0, y0, x1, y1; // bounds of the precinct + + //----- children + JPXSubband *subbands; // the subbands +}; + +//------------------------------------------------------------------------ + +struct JPXResLevel { + //----- from the COD and COC segments (main and tile) + Guint precinctWidth; // log2(precinct width) + Guint precinctHeight; // log2(precinct height) + + //----- computed + Guint x0, y0, x1, y1; // bounds of the tile-comp (for this res level) + Guint bx0[3], by0[3], // subband bounds + bx1[3], by1[3]; + + //---- children + JPXPrecinct *precincts; // the precincts +}; + +//------------------------------------------------------------------------ + +struct JPXTileComp { + //----- from the SIZ segment + GBool sgned; // 1 for signed, 0 for unsigned + Guint prec; // precision, in bits + Guint hSep; // horizontal separation of samples + Guint vSep; // vertical separation of samples + + //----- from the COD and COC segments (main and tile) + Guint style; // coding style parameter (Scod / Scoc) + Guint nDecompLevels; // number of decomposition levels + Guint codeBlockW; // log2(code-block width) + Guint codeBlockH; // log2(code-block height) + Guint codeBlockStyle; // code-block style + Guint transform; // wavelet transformation + + //----- from the QCD and QCC segments (main and tile) + Guint quantStyle; // quantization style + Guint *quantSteps; // quantization step size for each subband + Guint nQuantSteps; // number of entries in quantSteps + + //----- computed + Guint x0, y0, x1, y1; // bounds of the tile-comp, in ref coords + Guint cbW; // code-block width + Guint cbH; // code-block height + + //----- image data + int *data; // the decoded image data + int *buf; // intermediate buffer for the inverse + // transform + + //----- children + JPXResLevel *resLevels; // the resolution levels + // (len = nDecompLevels + 1) +}; + +//------------------------------------------------------------------------ + +struct JPXTile { + //----- from the COD segments (main and tile) + Guint progOrder; // progression order + Guint nLayers; // number of layers + Guint multiComp; // multiple component transformation + + //----- computed + Guint x0, y0, x1, y1; // bounds of the tile, in ref coords + Guint maxNDecompLevels; // max number of decomposition levels used + // in any component in this tile + + //----- progression order loop counters + Guint comp; // component + Guint res; // resolution level + Guint precinct; // precinct + Guint layer; // layer + + //----- children + JPXTileComp *tileComps; // the tile-components (len = JPXImage.nComps) +}; + +//------------------------------------------------------------------------ + +struct JPXImage { + //----- from the SIZ segment + Guint xSize, ySize; // size of reference grid + Guint xOffset, yOffset; // image offset + Guint xTileSize, yTileSize; // size of tiles + Guint xTileOffset, // offset of first tile + yTileOffset; + Guint nComps; // number of components + + //----- computed + Guint nXTiles; // number of tiles in x direction + Guint nYTiles; // number of tiles in y direction + + //----- children + JPXTile *tiles; // the tiles (len = nXTiles * nYTiles) +}; + +//------------------------------------------------------------------------ + +class JPXStream: public FilterStream { +public: + + JPXStream(Stream *strA); + virtual ~JPXStream(); + virtual StreamKind getKind() { return strJPX; } + virtual void reset(); + virtual int getChar(); + virtual int lookChar(); + virtual GString *getPSFilter(int psLevel, char *indent); + virtual GBool isBinary(GBool last = gTrue); + +private: + + void fillReadBuf(); + GBool readBoxes(); + GBool readColorSpecBox(Guint dataLen); + GBool readCodestream(Guint len); + GBool readTilePart(); + GBool readTilePartData(Guint tileIdx, + Guint tilePartLen, GBool tilePartToEOC); + GBool readCodeBlockData(JPXTileComp *tileComp, + JPXResLevel *resLevel, + JPXPrecinct *precinct, + JPXSubband *subband, + Guint res, Guint sb, + JPXCodeBlock *cb); + void inverseTransform(JPXTileComp *tileComp); + void inverseTransformLevel(JPXTileComp *tileComp, + Guint r, JPXResLevel *resLevel, + Guint nx0, Guint ny0, + Guint nx1, Guint ny1); + void inverseTransform1D(JPXTileComp *tileComp, + int *data, Guint stride, + Guint i0, Guint i1); + GBool inverseMultiCompAndDC(JPXTile *tile); + GBool readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen); + int readMarkerHdr(int *segType, Guint *segLen); + GBool readUByte(Guint *x); + GBool readByte(int *x); + GBool readUWord(Guint *x); + GBool readULong(Guint *x); + GBool readNBytes(int nBytes, GBool signd, int *x); + GBool readBits(int nBits, Guint *x); + void clearBitBuf(); + + Guint nComps; // number of components + Guint *bpc; // bits per component, for each component + Guint width, height; // image size + GBool haveImgHdr; // set if a JP2/JPX image header has been + // found + JPXColorSpec cs; // color specification + GBool haveCS; // set if a color spec has been found + JPXPalette palette; // the palette + GBool havePalette; // set if a palette has been found + JPXCompMap compMap; // the component mapping + GBool haveCompMap; // set if a component mapping has been found + JPXChannelDefn channelDefn; // channel definition + GBool haveChannelDefn; // set if a channel defn has been found + + JPXImage img; // JPEG2000 decoder data + Guint bitBuf; // buffer for bit reads + int bitBufLen; // number of bits in bitBuf + GBool bitBufSkip; // true if next bit should be skipped + // (for bit stuffing) + Guint byteCount; // number of bytes read since last call + // to clearBitBuf + + Guint curX, curY, curComp; // current position for lookChar/getChar + Guint readBuf; // read buffer + Guint readBufLen; // number of valid bits in readBuf +}; + +#endif diff --git a/pdf/xpdf/SplashOutputDev.cc b/pdf/xpdf/SplashOutputDev.cc new file mode 100644 index 0000000..0284d82 --- /dev/null +++ b/pdf/xpdf/SplashOutputDev.cc @@ -0,0 +1,1345 @@ +//======================================================================== +// +// SplashOutputDev.cc +// +// Copyright 2003 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include +#include +#include "gfile.h" +#include "GlobalParams.h" +#include "Error.h" +#include "Object.h" +#include "GfxState.h" +#include "GfxFont.h" +#include "Link.h" +#include "CharCodeToUnicode.h" +#include "FontEncodingTables.h" +#include "FoFiTrueType.h" +#include "SplashBitmap.h" +#include "SplashGlyphBitmap.h" +#include "SplashPattern.h" +#include "SplashScreen.h" +#include "SplashPath.h" +#include "SplashState.h" +#include "SplashErrorCodes.h" +#include "SplashFontEngine.h" +#include "SplashFont.h" +#include "SplashFontFile.h" +#include "SplashFontFileID.h" +#include "Splash.h" +#include "SplashOutputDev.h" + +//------------------------------------------------------------------------ +// Font substitutions +//------------------------------------------------------------------------ + +struct SplashOutFontSubst { + char *name; + double mWidth; +}; + +// index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic +static SplashOutFontSubst splashOutSubstFonts[16] = { + {"Helvetica", 0.833}, + {"Helvetica-Oblique", 0.833}, + {"Helvetica-Bold", 0.889}, + {"Helvetica-BoldOblique", 0.889}, + {"Times-Roman", 0.788}, + {"Times-Italic", 0.722}, + {"Times-Bold", 0.833}, + {"Times-BoldItalic", 0.778}, + {"Courier", 0.600}, + {"Courier-Oblique", 0.600}, + {"Courier-Bold", 0.600}, + {"Courier-BoldOblique", 0.600}, + {"Symbol", 0.576}, + {"Symbol", 0.576}, + {"Symbol", 0.576}, + {"Symbol", 0.576} +}; + +//------------------------------------------------------------------------ + +#define soutRound(x) ((int)(x + 0.5)) + +//------------------------------------------------------------------------ +// SplashOutFontFileID +//------------------------------------------------------------------------ + +class SplashOutFontFileID: public SplashFontFileID { +public: + + SplashOutFontFileID(Ref *rA) { r = *rA; substIdx = -1; } + + ~SplashOutFontFileID() {} + + GBool matches(SplashFontFileID *id) { + return ((SplashOutFontFileID *)id)->r.num == r.num && + ((SplashOutFontFileID *)id)->r.gen == r.gen; + } + + void setSubstIdx(int substIdxA) { substIdx = substIdxA; } + int getSubstIdx() { return substIdx; } + +private: + + Ref r; + int substIdx; +}; + +//------------------------------------------------------------------------ +// T3FontCache +//------------------------------------------------------------------------ + +struct T3FontCacheTag { + Gushort code; + Gushort mru; // valid bit (0x8000) and MRU index +}; + +class T3FontCache { +public: + + T3FontCache(Ref *fontID, double m11A, double m12A, + double m21A, double m22A, + int glyphXA, int glyphYA, int glyphWA, int glyphHA, + GBool aa); + ~T3FontCache(); + GBool matches(Ref *idA, double m11A, double m12A, + double m21A, double m22A) + { return fontID.num == idA->num && fontID.gen == idA->gen && + m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; } + + Ref fontID; // PDF font ID + double m11, m12, m21, m22; // transform matrix + int glyphX, glyphY; // pixel offset of glyph bitmaps + int glyphW, glyphH; // size of glyph bitmaps, in pixels + int glyphSize; // size of glyph bitmaps, in bytes + int cacheSets; // number of sets in cache + int cacheAssoc; // cache associativity (glyphs per set) + Guchar *cacheData; // glyph pixmap cache + T3FontCacheTag *cacheTags; // cache tags, i.e., char codes +}; + +T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A, + double m21A, double m22A, + int glyphXA, int glyphYA, int glyphWA, int glyphHA, + GBool aa) { + int i; + + fontID = *fontIDA; + m11 = m11A; + m12 = m12A; + m21 = m21A; + m22 = m22A; + glyphX = glyphXA; + glyphY = glyphYA; + glyphW = glyphWA; + glyphH = glyphHA; + if (aa) { + glyphSize = glyphW * glyphH; + } else { + glyphSize = ((glyphW + 7) >> 3) * glyphH; + } + cacheAssoc = 8; + if (glyphSize <= 256) { + cacheSets = 8; + } else if (glyphSize <= 512) { + cacheSets = 4; + } else if (glyphSize <= 1024) { + cacheSets = 2; + } else { + cacheSets = 1; + } + cacheData = (Guchar *)gmalloc(cacheSets * cacheAssoc * glyphSize); + cacheTags = (T3FontCacheTag *)gmalloc(cacheSets * cacheAssoc * + sizeof(T3FontCacheTag)); + for (i = 0; i < cacheSets * cacheAssoc; ++i) { + cacheTags[i].mru = i & (cacheAssoc - 1); + } +} + +T3FontCache::~T3FontCache() { + gfree(cacheData); + gfree(cacheTags); +} + +struct T3GlyphStack { + Gushort code; // character code + double x, y; // position to draw the glyph + + //----- cache info + T3FontCache *cache; // font cache for the current font + T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph + Guchar *cacheData; // pointer to cache data for the glyph + + //----- saved state + SplashBitmap *origBitmap; + Splash *origSplash; + double origCTM4, origCTM5; + + T3GlyphStack *next; // next object on stack +}; + +//------------------------------------------------------------------------ +// SplashOutputDev +//------------------------------------------------------------------------ + +SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, + GBool reverseVideoA, + SplashColor paperColorA) { + colorMode = colorModeA; + reverseVideo = reverseVideoA; + paperColor = paperColorA; + + xref = NULL; + + bitmap = new SplashBitmap(1, 1, colorMode); + splash = new Splash(bitmap); + splash->clear(paperColor); + + fontEngine = NULL; + + nT3Fonts = 0; + t3GlyphStack = NULL; + + font = NULL; + needFontUpdate = gFalse; + textClipPath = NULL; + + underlayCbk = NULL; + underlayCbkData = NULL; +} + +SplashOutputDev::~SplashOutputDev() { + int i; + + for (i = 0; i < nT3Fonts; ++i) { + delete t3FontCache[i]; + } + if (fontEngine) { + delete fontEngine; + } + if (splash) { + delete splash; + } + if (bitmap) { + delete bitmap; + } +} + +void SplashOutputDev::startDoc(XRef *xrefA) { + int i; + + xref = xrefA; + if (fontEngine) { + delete fontEngine; + } + fontEngine = new SplashFontEngine( +#if HAVE_T1LIB_H + globalParams->getEnableT1lib(), +#endif +#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H + globalParams->getEnableFreeType(), +#endif + globalParams->getAntialias()); + for (i = 0; i < nT3Fonts; ++i) { + delete t3FontCache[i]; + } + nT3Fonts = 0; +} + +void SplashOutputDev::startPage(int pageNum, GfxState *state) { + int w, h; + SplashColor color; + + w = state ? (int)(state->getPageWidth() + 0.5) : 1; + h = state ? (int)(state->getPageHeight() + 0.5) : 1; + if (splash) { + delete splash; + } + if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) { + if (bitmap) { + delete bitmap; + } + bitmap = new SplashBitmap(w, h, colorMode); + } + splash = new Splash(bitmap); + switch (colorMode) { + case splashModeMono1: color.mono1 = 0; break; + case splashModeMono8: color.mono8 = 0; break; + case splashModeRGB8: color.rgb8 = splashMakeRGB8(0, 0, 0); break; + case splashModeBGR8Packed: color.bgr8 = splashMakeBGR8(0, 0, 0); break; + } + splash->setStrokePattern(new SplashSolidColor(color)); + splash->setFillPattern(new SplashSolidColor(color)); + splash->setLineCap(splashLineCapButt); + splash->setLineJoin(splashLineJoinMiter); + splash->setLineDash(NULL, 0, 0); + splash->setMiterLimit(10); + splash->setFlatness(1); + splash->clear(paperColor); + + if (underlayCbk) { + (*underlayCbk)(underlayCbkData); + } +} + +void SplashOutputDev::endPage() { +} + +void SplashOutputDev::drawLink(Link *link, Catalog *catalog) { + double x1, y1, x2, y2; + LinkBorderStyle *borderStyle; + GfxRGB rgb; + double gray; + double *dash; + int dashLength; + SplashCoord dashList[20]; + SplashPath *path; + int x, y, i; + + link->getRect(&x1, &y1, &x2, &y2); + borderStyle = link->getBorderStyle(); + if (borderStyle->getWidth() > 0) { + borderStyle->getColor(&rgb.r, &rgb.g, &rgb.b); + gray = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b; + if (gray > 1) { + gray = 1; + } + splash->setStrokePattern(getColor(gray, &rgb)); + splash->setLineWidth((SplashCoord)borderStyle->getWidth()); + borderStyle->getDash(&dash, &dashLength); + if (borderStyle->getType() == linkBorderDashed && dashLength > 0) { + if (dashLength > 20) { + dashLength = 20; + } + for (i = 0; i < dashLength; ++i) { + dashList[i] = (SplashCoord)dash[i]; + } + splash->setLineDash(dashList, dashLength, 0); + } + path = new SplashPath(); + if (borderStyle->getType() == linkBorderUnderlined) { + cvtUserToDev(x1, y1, &x, &y); + path->moveTo((SplashCoord)x, (SplashCoord)y); + cvtUserToDev(x2, y1, &x, &y); + path->lineTo((SplashCoord)x, (SplashCoord)y); + } else { + cvtUserToDev(x1, y1, &x, &y); + path->moveTo((SplashCoord)x, (SplashCoord)y); + cvtUserToDev(x2, y1, &x, &y); + path->lineTo((SplashCoord)x, (SplashCoord)y); + cvtUserToDev(x2, y2, &x, &y); + path->lineTo((SplashCoord)x, (SplashCoord)y); + cvtUserToDev(x1, y2, &x, &y); + path->lineTo((SplashCoord)x, (SplashCoord)y); + path->close(); + } + splash->stroke(path); + delete path; + } +} + +void SplashOutputDev::saveState(GfxState *state) { + splash->saveState(); +} + +void SplashOutputDev::restoreState(GfxState *state) { + splash->restoreState(); + needFontUpdate = gTrue; +} + +void SplashOutputDev::updateAll(GfxState *state) { + updateLineDash(state); + updateLineJoin(state); + updateLineCap(state); + updateLineWidth(state); + updateFlatness(state); + updateMiterLimit(state); + updateFillColor(state); + updateStrokeColor(state); + needFontUpdate = gTrue; +} + +void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12, + double m21, double m22, + double m31, double m32) { + updateLineDash(state); + updateLineJoin(state); + updateLineCap(state); + updateLineWidth(state); +} + +void SplashOutputDev::updateLineDash(GfxState *state) { + double *dashPattern; + int dashLength; + double dashStart; + SplashCoord dash[20]; + SplashCoord phase; + int i; + + state->getLineDash(&dashPattern, &dashLength, &dashStart); + if (dashLength > 20) { + dashLength = 20; + } + for (i = 0; i < dashLength; ++i) { + dash[i] = (SplashCoord)state->transformWidth(dashPattern[i]); + if (dash[i] < 1) { + dash[i] = 1; + } + } + phase = (SplashCoord)state->transformWidth(dashStart); + splash->setLineDash(dash, dashLength, phase); +} + +void SplashOutputDev::updateFlatness(GfxState *state) { + splash->setFlatness(state->getFlatness()); +} + +void SplashOutputDev::updateLineJoin(GfxState *state) { + splash->setLineJoin(state->getLineJoin()); +} + +void SplashOutputDev::updateLineCap(GfxState *state) { + splash->setLineCap(state->getLineCap()); +} + +void SplashOutputDev::updateMiterLimit(GfxState *state) { + splash->setMiterLimit(state->getMiterLimit()); +} + +void SplashOutputDev::updateLineWidth(GfxState *state) { + splash->setLineWidth(state->getTransformedLineWidth()); +} + +void SplashOutputDev::updateFillColor(GfxState *state) { + double gray; + GfxRGB rgb; + + state->getFillGray(&gray); + state->getFillRGB(&rgb); + splash->setFillPattern(getColor(gray, &rgb)); +} + +void SplashOutputDev::updateStrokeColor(GfxState *state) { + double gray; + GfxRGB rgb; + + state->getStrokeGray(&gray); + state->getStrokeRGB(&rgb); + splash->setStrokePattern(getColor(gray, &rgb)); +} + +SplashPattern *SplashOutputDev::getColor(double gray, GfxRGB *rgb) { + SplashPattern *pattern; + SplashColor color0, color1; + double r, g, b; + + if (reverseVideo) { + gray = 1 - gray; + r = 1 - rgb->r; + g = 1 - rgb->g; + b = 1 - rgb->b; + } else { + r = rgb->r; + g = rgb->g; + b = rgb->b; + } + + pattern = NULL; // make gcc happy + switch (colorMode) { + case splashModeMono1: + color0.mono1 = 0; + color1.mono1 = 1; + pattern = new SplashHalftone(color0, color1, + splash->getScreen()->copy(), + (SplashCoord)gray); + break; + case splashModeMono8: + color1.mono8 = soutRound(255 * gray); + pattern = new SplashSolidColor(color1); + break; + case splashModeRGB8: + color1.rgb8 = splashMakeRGB8(soutRound(255 * r), + soutRound(255 * g), + soutRound(255 * b)); + pattern = new SplashSolidColor(color1); + break; + case splashModeBGR8Packed: + color1.bgr8 = splashMakeBGR8(soutRound(255 * r), + soutRound(255 * g), + soutRound(255 * b)); + pattern = new SplashSolidColor(color1); + break; + } + + return pattern; +} + +void SplashOutputDev::updateFont(GfxState *state) { + GfxFont *gfxFont; + GfxFontType fontType; + SplashOutFontFileID *id; + SplashFontFile *fontFile; + FoFiTrueType *ff; + Ref embRef; + Object refObj, strObj; + GString *tmpFileName, *fileName, *substName; + FILE *tmpFile; + Gushort *codeToGID; + DisplayFontParam *dfp; + double m11, m12, m21, m22, w1, w2; + SplashCoord mat[4]; + char *name; + int c, substIdx, n, code; + + needFontUpdate = gFalse; + font = NULL; + tmpFileName = NULL; + substIdx = -1; + + if (!(gfxFont = state->getFont())) { + goto err1; + } + fontType = gfxFont->getType(); + if (fontType == fontType3) { + goto err1; + } + + // check the font file cache + id = new SplashOutFontFileID(gfxFont->getID()); + if ((fontFile = fontEngine->getFontFile(id))) { + delete id; + + } else { + + // if there is an embedded font, write it to disk + if (gfxFont->getEmbeddedFontID(&embRef)) { + if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) { + error(-1, "Couldn't create temporary font file"); + goto err2; + } + refObj.initRef(embRef.num, embRef.gen); + refObj.fetch(xref, &strObj); + refObj.free(); + strObj.streamReset(); + while ((c = strObj.streamGetChar()) != EOF) { + fputc(c, tmpFile); + } + strObj.streamClose(); + strObj.free(); + fclose(tmpFile); + fileName = tmpFileName; + + // if there is an external font file, use it + } else if (!(fileName = gfxFont->getExtFontFile())) { + + // look for a display font mapping or a substitute font + dfp = NULL; + if (gfxFont->isCIDFont()) { + if (((GfxCIDFont *)gfxFont)->getCollection()) { + dfp = globalParams-> + getDisplayCIDFont(gfxFont->getName(), + ((GfxCIDFont *)gfxFont)->getCollection()); + } + } else { + if (gfxFont->getName()) { + dfp = globalParams->getDisplayFont(gfxFont->getName()); + } + if (!dfp) { + // 8-bit font substitution + if (gfxFont->isFixedWidth()) { + substIdx = 8; + } else if (gfxFont->isSerif()) { + substIdx = 4; + } else { + substIdx = 0; + } + if (gfxFont->isBold()) { + substIdx += 2; + } + if (gfxFont->isItalic()) { + substIdx += 1; + } + substName = new GString(splashOutSubstFonts[substIdx].name); + dfp = globalParams->getDisplayFont(substName); + delete substName; + id->setSubstIdx(substIdx); + } + } + if (!dfp) { + error(-1, "Couldn't find a font for '%s'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + goto err2; + } + switch (dfp->kind) { + case displayFontT1: + fileName = dfp->t1.fileName; + fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1; + break; + case displayFontTT: + fileName = dfp->tt.fileName; + fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType; + break; + } + } + + // load the font file + switch (fontType) { + case fontType1: + if (!(fontFile = fontEngine->loadType1Font( + id, + fileName->getCString(), + fileName == tmpFileName, + ((Gfx8BitFont *)gfxFont)->getEncoding()))) { + error(-1, "Couldn't create a font for '%s'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + goto err2; + } + break; + case fontType1C: + if (!(fontFile = fontEngine->loadType1CFont( + id, + fileName->getCString(), + fileName == tmpFileName, + ((Gfx8BitFont *)gfxFont)->getEncoding()))) { + error(-1, "Couldn't create a font for '%s'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + goto err2; + } + break; + case fontTrueType: + if (!(ff = FoFiTrueType::load(fileName->getCString()))) { + goto err2; + } + codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff); + delete ff; + if (!(fontFile = fontEngine->loadTrueTypeFont( + id, + fileName->getCString(), + fileName == tmpFileName, + codeToGID, 256))) { + error(-1, "Couldn't create a font for '%s'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + goto err2; + } + break; + case fontCIDType0: + case fontCIDType0C: + if (!(fontFile = fontEngine->loadCIDFont( + id, + fileName->getCString(), + fileName == tmpFileName))) { + error(-1, "Couldn't create a font for '%s'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + goto err2; + } + break; + case fontCIDType2: + n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); + codeToGID = (Gushort *)gmalloc(n * sizeof(Gushort)); + memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), + n * sizeof(Gushort)); + if (!(fontFile = fontEngine->loadTrueTypeFont( + id, + fileName->getCString(), + fileName == tmpFileName, + codeToGID, n))) { + error(-1, "Couldn't create a font for '%s'", + gfxFont->getName() ? gfxFont->getName()->getCString() + : "(unnamed)"); + goto err2; + } + break; + default: + // this shouldn't happen + goto err2; + } + } + + // get the font matrix + state->getFontTransMat(&m11, &m12, &m21, &m22); + m11 *= state->getHorizScaling(); + m12 *= state->getHorizScaling(); + + // for substituted fonts: adjust the font matrix -- compare the + // width of 'm' in the original font and the substituted font + substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx(); + if (substIdx >= 0) { + for (code = 0; code < 256; ++code) { + if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) && + name[0] == 'm' && name[1] == '\0') { + break; + } + } + if (code < 256) { + w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code); + w2 = splashOutSubstFonts[substIdx].mWidth; + if (!gfxFont->isSymbolic()) { + // if real font is substantially narrower than substituted + // font, reduce the font size accordingly + if (w1 > 0.01 && w1 < 0.9 * w2) { + w1 /= w2; + m11 *= w1; + m21 *= w1; + } + } + } + } + + // create the scaled font + mat[0] = m11; mat[1] = -m12; + mat[2] = m21; mat[3] = -m22; + font = fontEngine->getFont(fontFile, mat); + + if (tmpFileName) { + delete tmpFileName; + } + return; + + err2: + delete id; + err1: + if (tmpFileName) { + delete tmpFileName; + } + return; +} + +void SplashOutputDev::stroke(GfxState *state) { + SplashPath *path; + + path = convertPath(state, state->getPath()); + splash->stroke(path); + delete path; +} + +void SplashOutputDev::fill(GfxState *state) { + SplashPath *path; + + path = convertPath(state, state->getPath()); + splash->fill(path, gFalse); + delete path; +} + +void SplashOutputDev::eoFill(GfxState *state) { + SplashPath *path; + + path = convertPath(state, state->getPath()); + splash->fill(path, gTrue); + delete path; +} + +void SplashOutputDev::clip(GfxState *state) { + SplashPath *path; + + path = convertPath(state, state->getPath()); + splash->clipToPath(path, gFalse); + delete path; +} + +void SplashOutputDev::eoClip(GfxState *state) { + SplashPath *path; + + path = convertPath(state, state->getPath()); + splash->clipToPath(path, gTrue); + delete path; +} + +SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path) { + SplashPath *sPath; + GfxSubpath *subpath; + double x1, y1, x2, y2, x3, y3; + int i, j; + + sPath = new SplashPath(); + for (i = 0; i < path->getNumSubpaths(); ++i) { + subpath = path->getSubpath(i); + if (subpath->getNumPoints() > 0) { + state->transform(subpath->getX(0), subpath->getY(0), &x1, &y1); + sPath->moveTo((SplashCoord)x1, (SplashCoord)y1); + j = 1; + while (j < subpath->getNumPoints()) { + if (subpath->getCurve(j)) { + state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1); + state->transform(subpath->getX(j+1), subpath->getY(j+1), &x2, &y2); + state->transform(subpath->getX(j+2), subpath->getY(j+2), &x3, &y3); + sPath->curveTo((SplashCoord)x1, (SplashCoord)y1, + (SplashCoord)x2, (SplashCoord)y2, + (SplashCoord)x3, (SplashCoord)y3); + j += 3; + } else { + state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1); + sPath->lineTo((SplashCoord)x1, (SplashCoord)y1); + ++j; + } + } + if (subpath->isClosed()) { + sPath->close(); + } + } + } + return sPath; +} + +void SplashOutputDev::drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode code, Unicode *u, int uLen) { + double x1, y1; + SplashPath *path; + int render; + + if (needFontUpdate) { + updateFont(state); + } + if (!font) { + return; + } + + // check for invisible text -- this is used by Acrobat Capture + render = state->getRender(); + if (render == 3) { + return; + } + + x -= originX; + y -= originY; + state->transform(x, y, &x1, &y1); + + // fill + if (!(render & 1)) { + splash->fillChar((SplashCoord)x1, (SplashCoord)y1, code, font); + } + + // stroke + if ((render & 3) == 1 || (render & 3) == 2) { + if ((path = font->getGlyphPath(code))) { + path->offset((SplashCoord)x1, (SplashCoord)y1); + splash->stroke(path); + delete path; + } + } + + // clip + if (render & 4) { + path = font->getGlyphPath(code); + path->offset((SplashCoord)x1, (SplashCoord)y1); + if (textClipPath) { + textClipPath->append(path); + delete path; + } else { + textClipPath = path; + } + } +} + +GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, + double dx, double dy, + CharCode code, Unicode *u, int uLen) { + GfxFont *gfxFont; + Ref *fontID; + double *ctm, *bbox; + T3FontCache *t3Font; + T3GlyphStack *t3gs; + double x1, y1, xMin, yMin, xMax, yMax, xt, yt; + int i, j; + + if (!(gfxFont = state->getFont())) { + return gFalse; + } + fontID = gfxFont->getID(); + ctm = state->getCTM(); + state->transform(0, 0, &xt, &yt); + + // is it the first (MRU) font in the cache? + if (!(nT3Fonts > 0 && + t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) { + + // is the font elsewhere in the cache? + for (i = 1; i < nT3Fonts; ++i) { + if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) { + t3Font = t3FontCache[i]; + for (j = i; j > 0; --j) { + t3FontCache[j] = t3FontCache[j - 1]; + } + t3FontCache[0] = t3Font; + break; + } + } + if (i >= nT3Fonts) { + + // create new entry in the font cache + if (nT3Fonts == splashOutT3FontCacheSize) { + delete t3FontCache[nT3Fonts - 1]; + --nT3Fonts; + } + for (j = nT3Fonts; j > 0; --j) { + t3FontCache[j] = t3FontCache[j - 1]; + } + ++nT3Fonts; + bbox = gfxFont->getFontBBox(); + if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) { + // broken bounding box -- just take a guess + xMin = xt - 5; + xMax = xMin + 30; + yMax = yt + 15; + yMin = yMax - 45; + } else { + state->transform(bbox[0], bbox[1], &x1, &y1); + xMin = xMax = x1; + yMin = yMax = y1; + state->transform(bbox[0], bbox[3], &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + state->transform(bbox[2], bbox[1], &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + state->transform(bbox[2], bbox[3], &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + } + t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3], + (int)floor(xMin - xt), + (int)floor(yMin - yt), + (int)ceil(xMax) - (int)floor(xMin) + 3, + (int)ceil(yMax) - (int)floor(yMin) + 3, + colorMode != splashModeMono1); + } + } + t3Font = t3FontCache[0]; + + // is the glyph in the cache? + i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; + for (j = 0; j < t3Font->cacheAssoc; ++j) { + if ((t3Font->cacheTags[i+j].mru & 0x8000) && + t3Font->cacheTags[i+j].code == code) { + drawType3Glyph(t3Font, &t3Font->cacheTags[i+j], + t3Font->cacheData + (i+j) * t3Font->glyphSize, + xt, yt); + return gTrue; + } + } + + // push a new Type 3 glyph record + t3gs = new T3GlyphStack(); + t3gs->next = t3GlyphStack; + t3GlyphStack = t3gs; + t3GlyphStack->code = code; + t3GlyphStack->x = xt; + t3GlyphStack->y = yt; + t3GlyphStack->cache = t3Font; + t3GlyphStack->cacheTag = NULL; + t3GlyphStack->cacheData = NULL; + + return gFalse; +} + +void SplashOutputDev::endType3Char(GfxState *state) { + T3GlyphStack *t3gs; + double *ctm; + + if (t3GlyphStack->cacheTag) { + memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr().mono8, + t3GlyphStack->cache->glyphSize); + delete bitmap; + delete splash; + bitmap = t3GlyphStack->origBitmap; + splash = t3GlyphStack->origSplash; + ctm = state->getCTM(); + state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], + t3GlyphStack->origCTM4, t3GlyphStack->origCTM5); + drawType3Glyph(t3GlyphStack->cache, + t3GlyphStack->cacheTag, t3GlyphStack->cacheData, + t3GlyphStack->x, t3GlyphStack->y); + } + t3gs = t3GlyphStack; + t3GlyphStack = t3gs->next; + delete t3gs; +} + +void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) { +} + +void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, + double llx, double lly, double urx, double ury) { + double *ctm; + T3FontCache *t3Font; + SplashColor color; + double xt, yt, xMin, xMax, yMin, yMax, x1, y1; + int i, j; + + t3Font = t3GlyphStack->cache; + + // check for a valid bbox + state->transform(0, 0, &xt, &yt); + state->transform(llx, lly, &x1, &y1); + xMin = xMax = x1; + yMin = yMax = y1; + state->transform(llx, ury, &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + state->transform(urx, lly, &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + state->transform(urx, ury, &x1, &y1); + if (x1 < xMin) { + xMin = x1; + } else if (x1 > xMax) { + xMax = x1; + } + if (y1 < yMin) { + yMin = y1; + } else if (y1 > yMax) { + yMax = y1; + } + if (xMin - xt < t3Font->glyphX || + yMin - yt < t3Font->glyphY || + xMax - xt > t3Font->glyphX + t3Font->glyphW || + yMax - yt > t3Font->glyphY + t3Font->glyphH) { + error(-1, "Bad bounding box in Type 3 glyph"); + return; + } + + // allocate a cache entry + i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; + for (j = 0; j < t3Font->cacheAssoc; ++j) { + if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) { + t3Font->cacheTags[i+j].mru = 0x8000; + t3Font->cacheTags[i+j].code = t3GlyphStack->code; + t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j]; + t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize; + } else { + ++t3Font->cacheTags[i+j].mru; + } + } + + // save state + t3GlyphStack->origBitmap = bitmap; + t3GlyphStack->origSplash = splash; + ctm = state->getCTM(); + t3GlyphStack->origCTM4 = ctm[4]; + t3GlyphStack->origCTM5 = ctm[5]; + + // create the temporary bitmap + if (colorMode == splashModeMono1) { + bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, splashModeMono1); + splash = new Splash(bitmap); + color.mono1 = 0; + splash->clear(color); + color.mono1 = 1; + } else { + bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, splashModeMono8); + splash = new Splash(bitmap); + color.mono8 = 0x00; + splash->clear(color); + color.mono8 = 0xff; + } + splash->setFillPattern(new SplashSolidColor(color)); + splash->setStrokePattern(new SplashSolidColor(color)); + //~ this should copy other state from t3GlyphStack->origSplash? + state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], + -t3Font->glyphX, -t3Font->glyphY); +} + +void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font, + T3FontCacheTag *tag, Guchar *data, + double x, double y) { + SplashGlyphBitmap glyph; + + glyph.x = -t3Font->glyphX; + glyph.y = -t3Font->glyphY; + glyph.w = t3Font->glyphW; + glyph.h = t3Font->glyphH; + glyph.aa = colorMode != splashModeMono1; + glyph.data = data; + glyph.freeData = gFalse; + splash->fillGlyph((SplashCoord)x, (SplashCoord)y, &glyph); +} + +void SplashOutputDev::endTextObject(GfxState *state) { + if (textClipPath) { + splash->clipToPath(textClipPath, gFalse); + delete textClipPath; + textClipPath = NULL; + } +} + +struct SplashOutImageMaskData { + ImageStream *imgStr; + int nPixels, idx; + GBool invert; +}; + +GBool SplashOutputDev::imageMaskSrc(void *data, SplashMono1 *pixel) { + SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data; + Guchar pix; + + if (imgMaskData->idx >= imgMaskData->nPixels) { + return gFalse; + } + //~ use getLine + imgMaskData->imgStr->getPixel(&pix); + if (!imgMaskData->invert) { + pix ^= 1; + } + *pixel = pix; + ++imgMaskData->idx; + return gTrue; +} + +void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, + int width, int height, GBool invert, + GBool inlineImg) { + double *ctm; + SplashCoord mat[6]; + SplashOutImageMaskData imgMaskData; + Guchar pix; + + ctm = state->getCTM(); + mat[0] = ctm[0]; + mat[1] = ctm[1]; + mat[2] = -ctm[2]; + mat[3] = -ctm[3]; + mat[4] = ctm[2] + ctm[4]; + mat[5] = ctm[3] + ctm[5]; + + imgMaskData.imgStr = new ImageStream(str, width, 1, 1); + imgMaskData.imgStr->reset(); + imgMaskData.nPixels = width * height; + imgMaskData.idx = 0; + imgMaskData.invert = invert; + + splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat); + if (inlineImg) { + while (imageMaskSrc(&imgMaskData, &pix)) ; + } + + delete imgMaskData.imgStr; +} + +struct SplashOutImageData { + ImageStream *imgStr; + GfxImageColorMap *colorMap; + int *maskColors; + SplashOutputDev *out; + int nPixels, idx; +}; + +GBool SplashOutputDev::imageSrc(void *data, SplashColor *pixel, + Guchar *alpha) { + SplashOutImageData *imgData = (SplashOutImageData *)data; + Guchar pix[gfxColorMaxComps]; + GfxRGB rgb; + double gray; + int i; + + if (imgData->idx >= imgData->nPixels) { + return gFalse; + } + + //~ use getLine + imgData->imgStr->getPixel(pix); + switch (imgData->out->colorMode) { + case splashModeMono1: + case splashModeMono8: + imgData->colorMap->getGray(pix, &gray); + pixel->mono8 = soutRound(255 * gray); + break; + case splashModeRGB8: + imgData->colorMap->getRGB(pix, &rgb); + pixel->rgb8 = splashMakeRGB8(soutRound(255 * rgb.r), + soutRound(255 * rgb.g), + soutRound(255 * rgb.b)); + break; + case splashModeBGR8Packed: + imgData->colorMap->getRGB(pix, &rgb); + pixel->bgr8 = splashMakeBGR8(soutRound(255 * rgb.r), + soutRound(255 * rgb.g), + soutRound(255 * rgb.b)); + break; + } + + if (imgData->maskColors) { + *alpha = 0; + for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) { + if (pix[i] < imgData->maskColors[2*i] || + pix[i] > imgData->maskColors[2*i+1]) { + *alpha = 1; + break; + } + } + } else { + *alpha = 1; + } + + ++imgData->idx; + return gTrue; +} + +void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, + GfxImageColorMap *colorMap, + int *maskColors, GBool inlineImg) { + double *ctm; + SplashCoord mat[6]; + SplashOutImageData imgData; + SplashColor pix; + Guchar alpha; + + ctm = state->getCTM(); + mat[0] = ctm[0]; + mat[1] = ctm[1]; + mat[2] = -ctm[2]; + mat[3] = -ctm[3]; + mat[4] = ctm[2] + ctm[4]; + mat[5] = ctm[3] + ctm[5]; + + imgData.imgStr = new ImageStream(str, width, + colorMap->getNumPixelComps(), + colorMap->getBits()); + imgData.imgStr->reset(); + imgData.colorMap = colorMap; + imgData.maskColors = maskColors; + imgData.out = this; + imgData.nPixels = width * height; + imgData.idx = 0; + + splash->drawImage(&imageSrc, &imgData, + (colorMode == splashModeMono1) ? splashModeMono8 + : colorMode, + width, height, mat); + if (inlineImg) { + while (imageSrc(&imgData, &pix, &alpha)) ; + } + + delete imgData.imgStr; +} + +int SplashOutputDev::getBitmapWidth() { + return bitmap->getWidth(); +} + +int SplashOutputDev::getBitmapHeight() { + return bitmap->getHeight(); +} + +void SplashOutputDev::xorRectangle(int x0, int y0, int x1, int y1, + SplashPattern *pattern) { + SplashPath *path; + + path = new SplashPath(); + path->moveTo((SplashCoord)x0, (SplashCoord)y0); + path->lineTo((SplashCoord)x1, (SplashCoord)y0); + path->lineTo((SplashCoord)x1, (SplashCoord)y1); + path->lineTo((SplashCoord)x0, (SplashCoord)y1); + path->close(); + splash->setFillPattern(pattern); + splash->xorFill(path, gTrue); + delete path; +} + +void SplashOutputDev::setFillColor(int r, int g, int b) { + GfxRGB rgb; + double gray; + + rgb.r = r / 255.0; + rgb.g = g / 255.0; + rgb.b = b / 255.0; + gray = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g; + splash->setFillPattern(getColor(gray, &rgb)); +} + +SplashFont *SplashOutputDev::getFont(GString *name, double *mat) { + DisplayFontParam *dfp; + Ref ref; + SplashOutFontFileID *id; + SplashFontFile *fontFile; + SplashFont *fontObj; + int i; + + for (i = 0; i < 16; ++i) { + if (!name->cmp(splashOutSubstFonts[i].name)) { + break; + } + } + if (i == 16) { + return NULL; + } + ref.num = i; + ref.gen = -1; + id = new SplashOutFontFileID(&ref); + + // check the font file cache + if ((fontFile = fontEngine->getFontFile(id))) { + delete id; + + // load the font file + } else { + dfp = globalParams->getDisplayFont(name); + if (dfp->kind != displayFontT1) { + return NULL; + } + fontFile = fontEngine->loadType1Font(id, dfp->t1.fileName->getCString(), + gFalse, winAnsiEncoding); + } + + // create the scaled font + fontObj = fontEngine->getFont(fontFile, (SplashCoord *)mat); + + return fontObj; +} diff --git a/pdf/xpdf/SplashOutputDev.h b/pdf/xpdf/SplashOutputDev.h new file mode 100644 index 0000000..9300fe4 --- /dev/null +++ b/pdf/xpdf/SplashOutputDev.h @@ -0,0 +1,195 @@ +//======================================================================== +// +// SplashOutputDev.h +// +// Copyright 2003 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef SPLASHOUTPUTDEV_H +#define SPLASHOUTPUTDEV_H + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +#include "gtypes.h" +#include "SplashTypes.h" +#include "config.h" +#include "OutputDev.h" + +class GfxState; +class GfxPath; +class Gfx8BitFont; +class SplashBitmap; +class Splash; +class SplashPath; +class SplashPattern; +class SplashFontEngine; +class SplashFont; +class T3FontCache; +struct T3FontCacheTag; +struct T3GlyphStack; +struct GfxRGB; + +//------------------------------------------------------------------------ + +// number of Type 3 fonts to cache +#define splashOutT3FontCacheSize 8 + +//------------------------------------------------------------------------ +// SplashOutputDev +//------------------------------------------------------------------------ + +class SplashOutputDev: public OutputDev { +public: + + // Constructor. + SplashOutputDev(SplashColorMode colorModeA, GBool reverseVideoA, + SplashColor paperColorA); + + // Destructor. + virtual ~SplashOutputDev(); + + //----- get info about output device + + // Does this device use upside-down coordinates? + // (Upside-down means (0,0) is the top left corner of the page.) + virtual GBool upsideDown() { return gTrue; } + + // Does this device use drawChar() or drawString()? + virtual GBool useDrawChar() { return gTrue; } + + // Does this device use beginType3Char/endType3Char? Otherwise, + // text in Type 3 fonts will be drawn with drawChar/drawString. + virtual GBool interpretType3Chars() { return gTrue; } + + //----- initialization and control + + // Start a page. + virtual void startPage(int pageNum, GfxState *state); + + // End a page. + virtual void endPage(); + + //----- link borders + virtual void drawLink(Link *link, Catalog *catalog); + + //----- save/restore graphics state + virtual void saveState(GfxState *state); + virtual void restoreState(GfxState *state); + + //----- update graphics state + virtual void updateAll(GfxState *state); + virtual void updateCTM(GfxState *state, double m11, double m12, + double m21, double m22, double m31, double m32); + virtual void updateLineDash(GfxState *state); + virtual void updateFlatness(GfxState *state); + virtual void updateLineJoin(GfxState *state); + virtual void updateLineCap(GfxState *state); + virtual void updateMiterLimit(GfxState *state); + virtual void updateLineWidth(GfxState *state); + virtual void updateFillColor(GfxState *state); + virtual void updateStrokeColor(GfxState *state); + + //----- update text state + virtual void updateFont(GfxState *state); + + //----- path painting + virtual void stroke(GfxState *state); + virtual void fill(GfxState *state); + virtual void eoFill(GfxState *state); + + //----- path clipping + virtual void clip(GfxState *state); + virtual void eoClip(GfxState *state); + + //----- text drawing + virtual void drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode code, Unicode *u, int uLen); + virtual GBool beginType3Char(GfxState *state, double x, double y, + double dx, double dy, + CharCode code, Unicode *u, int uLen); + virtual void endType3Char(GfxState *state); + virtual void endTextObject(GfxState *state); + + //----- image drawing + virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, + int width, int height, GBool invert, + GBool inlineImg); + virtual void drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, GfxImageColorMap *colorMap, + int *maskColors, GBool inlineImg); + + //----- Type 3 font operators + virtual void type3D0(GfxState *state, double wx, double wy); + virtual void type3D1(GfxState *state, double wx, double wy, + double llx, double lly, double urx, double ury); + + //----- special access + + // Called to indicate that a new PDF document has been loaded. + void startDoc(XRef *xrefA); + + GBool isReverseVideo() { return reverseVideo; } + + // Get the bitmap and its size. + SplashBitmap *getBitmap() { return bitmap; } + int getBitmapWidth(); + int getBitmapHeight(); + + // Get the Splash object. + Splash *getSplash() { return splash; } + + // XOR a rectangular region in the bitmap with . + // is passed to Splash::setFillPattern, so it should not be used + // after calling this function. + void xorRectangle(int x0, int y0, int x1, int y1, SplashPattern *pattern); + + // Set the Splash fill color. + void setFillColor(int r, int g, int b); + + // Get a font object for a Base-14 font, using the Latin-1 encoding. + SplashFont *getFont(GString *name, double *mat); + + void setUnderlayCbk(void (*cbk)(void *data), void *data) + { underlayCbk = cbk; underlayCbkData = data; } + +private: + + SplashPattern *getColor(double gray, GfxRGB *rgb); + SplashPath *convertPath(GfxState *state, GfxPath *path); + void drawType3Glyph(T3FontCache *t3Font, + T3FontCacheTag *tag, Guchar *data, + double x, double y); + static GBool imageMaskSrc(void *data, SplashMono1 *pixel); + static GBool imageSrc(void *data, SplashColor *pixel, Guchar *alpha); + + SplashColorMode colorMode; + GBool reverseVideo; // reverse video mode + SplashColor paperColor; // paper color + + XRef *xref; // xref table for current document + + SplashBitmap *bitmap; + Splash *splash; + SplashFontEngine *fontEngine; + + T3FontCache * // Type 3 font cache + t3FontCache[splashOutT3FontCacheSize]; + int nT3Fonts; // number of valid entries in t3FontCache + T3GlyphStack *t3GlyphStack; // Type 3 glyph context stack + + SplashFont *font; // current font + GBool needFontUpdate; // set when the font needs to be updated + SplashPath *textClipPath; // clipping path built with text object + + void (*underlayCbk)(void *data); + void *underlayCbkData; +}; + +#endif diff --git a/pdf/xpdf/XSplashOutputDev.cc b/pdf/xpdf/XSplashOutputDev.cc new file mode 100644 index 0000000..3e2bfc4 --- /dev/null +++ b/pdf/xpdf/XSplashOutputDev.cc @@ -0,0 +1,323 @@ +//======================================================================== +// +// XSplashOutputDev.cc +// +// Copyright 2003 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include +#include +#include "gmem.h" +#include "SplashTypes.h" +#include "SplashBitmap.h" +#include "Object.h" +#include "GfxState.h" +#include "TextOutputDev.h" +#include "XSplashOutputDev.h" + +//------------------------------------------------------------------------ +// Constants and macros +//------------------------------------------------------------------------ + +#define xoutRound(x) ((int)(x + 0.5)) + +//------------------------------------------------------------------------ +// XSplashOutputDev +//------------------------------------------------------------------------ + +XSplashOutputDev::XSplashOutputDev(Display *displayA, int screenNumA, + Visual *visualA, Colormap colormapA, + GBool reverseVideoA, + SplashColor paperColorA, + GBool installCmapA, int rgbCubeSizeA, + GBool incrementalUpdateA, + void (*redrawCbkA)(void *data), + void *redrawCbkDataA): + SplashOutputDev(splashModeRGB8, reverseVideoA, paperColorA) +{ + XVisualInfo visualTempl; + XVisualInfo *visualList; + Gulong mask; + int nVisuals; + XColor xcolor; + XColor *xcolors; + int r, g, b, n, m; + GBool ok; + + incrementalUpdate = incrementalUpdateA; + redrawCbk = redrawCbkA; + redrawCbkData = redrawCbkDataA; + + // create text object + text = new TextPage(gFalse); + + //----- set up the X color stuff + + display = displayA; + visual = visualA; + + // check for TrueColor visual + //~ this should scan the list, not just look at the first one + visualTempl.visualid = XVisualIDFromVisual(visual); + visualList = XGetVisualInfo(display, VisualIDMask, + &visualTempl, &nVisuals); + if (nVisuals < 1) { + // this shouldn't happen + XFree((XPointer)visualList); + visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl, + &nVisuals); + } + depth = visualList->depth; + if (visualList->c_class == TrueColor) { + trueColor = gTrue; + for (mask = visualList->red_mask, rShift = 0; + mask && !(mask & 1); + mask >>= 1, ++rShift) ; + for (rDiv = 8; mask; mask >>= 1, --rDiv) ; + for (mask = visualList->green_mask, gShift = 0; + mask && !(mask & 1); + mask >>= 1, ++gShift) ; + for (gDiv = 8; mask; mask >>= 1, --gDiv) ; + for (mask = visualList->blue_mask, bShift = 0; + mask && !(mask & 1); + mask >>= 1, ++bShift) ; + for (bDiv = 8; mask; mask >>= 1, --bDiv) ; + } else { + trueColor = gFalse; + } + XFree((XPointer)visualList); + + // allocate a color cube + if (!trueColor) { + + // set colors in private colormap + if (installCmapA) { + for (rgbCubeSize = xOutMaxRGBCube; rgbCubeSize >= 2; --rgbCubeSize) { + m = rgbCubeSize * rgbCubeSize * rgbCubeSize; + if (XAllocColorCells(display, colormapA, False, NULL, 0, colors, m)) { + break; + } + } + if (rgbCubeSize >= 2) { + m = rgbCubeSize * rgbCubeSize * rgbCubeSize; + xcolors = (XColor *)gmalloc(m * sizeof(XColor)); + n = 0; + for (r = 0; r < rgbCubeSize; ++r) { + for (g = 0; g < rgbCubeSize; ++g) { + for (b = 0; b < rgbCubeSize; ++b) { + xcolors[n].pixel = colors[n]; + xcolors[n].red = (r * 65535) / (rgbCubeSize - 1); + xcolors[n].green = (g * 65535) / (rgbCubeSize - 1); + xcolors[n].blue = (b * 65535) / (rgbCubeSize - 1); + xcolors[n].flags = DoRed | DoGreen | DoBlue; + ++n; + } + } + } + XStoreColors(display, colormapA, xcolors, m); + gfree(xcolors); + } else { + rgbCubeSize = 1; + colors[0] = BlackPixel(display, screenNumA); + colors[1] = WhitePixel(display, screenNumA); + } + + // allocate colors in shared colormap + } else { + if (rgbCubeSize > xOutMaxRGBCube) { + rgbCubeSize = xOutMaxRGBCube; + } + ok = gFalse; + for (rgbCubeSize = rgbCubeSizeA; rgbCubeSize >= 2; --rgbCubeSize) { + ok = gTrue; + n = 0; + for (r = 0; r < rgbCubeSize && ok; ++r) { + for (g = 0; g < rgbCubeSize && ok; ++g) { + for (b = 0; b < rgbCubeSize && ok; ++b) { + if (n == 0) { + colors[n] = BlackPixel(display, screenNumA); + ++n; + } else { + xcolor.red = (r * 65535) / (rgbCubeSize - 1); + xcolor.green = (g * 65535) / (rgbCubeSize - 1); + xcolor.blue = (b * 65535) / (rgbCubeSize - 1); + if (XAllocColor(display, colormapA, &xcolor)) { + colors[n++] = xcolor.pixel; + } else { + ok = gFalse; + } + } + } + } + } + if (ok) { + break; + } + XFreeColors(display, colormapA, &colors[1], n-1, 0); + } + if (!ok) { + rgbCubeSize = 1; + colors[0] = BlackPixel(display, screenNumA); + colors[1] = WhitePixel(display, screenNumA); + } + } + } +} + +XSplashOutputDev::~XSplashOutputDev() { + delete text; +} + +void XSplashOutputDev::drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode code, Unicode *u, int uLen) { + text->addChar(state, x, y, dx, dy, code, u, uLen); + SplashOutputDev::drawChar(state, x, y, dx, dy, originX, originY, + code, u, uLen); +} + +GBool XSplashOutputDev::beginType3Char(GfxState *state, double x, double y, + double dx, double dy, + CharCode code, Unicode *u, int uLen) { + text->addChar(state, x, y, dx, dy, code, u, uLen); + return SplashOutputDev::beginType3Char(state, x, y, dx, dy, code, u, uLen); +} + +void XSplashOutputDev::clear() { + startDoc(NULL); + startPage(0, NULL); +} + +void XSplashOutputDev::startPage(int pageNum, GfxState *state) { + SplashOutputDev::startPage(pageNum, state); + text->startPage(state); +} + +void XSplashOutputDev::endPage() { + SplashOutputDev::endPage(); + if (!incrementalUpdate) { + (*redrawCbk)(redrawCbkData); + } + text->coalesce(gTrue); +} + +void XSplashOutputDev::dump() { + if (incrementalUpdate) { + (*redrawCbk)(redrawCbkData); + } +} + +void XSplashOutputDev::updateFont(GfxState *state) { + SplashOutputDev::updateFont(state); + text->updateFont(state); +} + +void XSplashOutputDev::redraw(int srcX, int srcY, + Drawable destDrawable, GC destGC, + int destX, int destY, + int width, int height) { + XImage *image; + SplashColorPtr dataPtr; + SplashRGB8 *p; + SplashRGB8 rgb; + Gulong pixel; + int bw, x, y, r, g, b, gray; + + //~ allocate this image once (whenever the window changes size) + //~ use XShm + image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, + width, height, 8, 0); + image->data = (char *)gmalloc(height * image->bytes_per_line); + + //~ optimize for known XImage formats + bw = getBitmap()->getWidth(); + dataPtr = getBitmap()->getDataPtr(); + + if (trueColor) { + for (y = 0; y < height; ++y) { + p = dataPtr.rgb8 + (y + srcY) * bw + srcX; + for (x = 0; x < width; ++x) { + rgb = *p++; + r = splashRGB8R(rgb) >> rDiv; + g = splashRGB8G(rgb) >> gDiv; + b = splashRGB8B(rgb) >> bDiv; + pixel = ((Gulong)r << rShift) + + ((Gulong)g << gShift) + + ((Gulong)b << bShift); + XPutPixel(image, x, y, pixel); + } + } + } else if (rgbCubeSize == 1) { + //~ this should really use splashModeMono, with non-clustered dithering + for (y = 0; y < height; ++y) { + p = dataPtr.rgb8 + (y + srcY) * bw + srcX; + for (x = 0; x < width; ++x) { + rgb = *p++; + gray = xoutRound(0.299 * splashRGB8R(rgb) + + 0.587 * splashRGB8G(rgb) + + 0.114 * splashRGB8B(rgb)); + if (gray < 128) { + pixel = colors[0]; + } else { + pixel = colors[1]; + } + XPutPixel(image, x, y, pixel); + } + } + } else { + for (y = 0; y < height; ++y) { + p = dataPtr.rgb8 + (y + srcY) * bw + srcX; + for (x = 0; x < width; ++x) { + rgb = *p++; + r = (splashRGB8R(rgb) * (rgbCubeSize - 1)) / 255; + g = (splashRGB8G(rgb) * (rgbCubeSize - 1)) / 255; + b = (splashRGB8B(rgb) * (rgbCubeSize - 1)) / 255; + pixel = colors[(r * rgbCubeSize + g) * rgbCubeSize + b]; + XPutPixel(image, x, y, pixel); + } + } + } + + XPutImage(display, destDrawable, destGC, image, + 0, 0, destX, destY, width, height); + + gfree(image->data); + image->data = NULL; + XDestroyImage(image); +} + +GBool XSplashOutputDev::findText(Unicode *s, int len, + GBool startAtTop, GBool stopAtBottom, + GBool startAtLast, GBool stopAtLast, + int *xMin, int *yMin, + int *xMax, int *yMax) { + double xMin1, yMin1, xMax1, yMax1; + + xMin1 = (double)*xMin; + yMin1 = (double)*yMin; + xMax1 = (double)*xMax; + yMax1 = (double)*yMax; + if (text->findText(s, len, startAtTop, stopAtBottom, + startAtLast, stopAtLast, + &xMin1, &yMin1, &xMax1, &yMax1)) { + *xMin = xoutRound(xMin1); + *xMax = xoutRound(xMax1); + *yMin = xoutRound(yMin1); + *yMax = xoutRound(yMax1); + return gTrue; + } + return gFalse; +} + +GString *XSplashOutputDev::getText(int xMin, int yMin, int xMax, int yMax) { + return text->getText((double)xMin, (double)yMin, + (double)xMax, (double)yMax); +} diff --git a/pdf/xpdf/XSplashOutputDev.h b/pdf/xpdf/XSplashOutputDev.h new file mode 100644 index 0000000..71e5b48 --- /dev/null +++ b/pdf/xpdf/XSplashOutputDev.h @@ -0,0 +1,110 @@ +//======================================================================== +// +// XSplashOutputDev.h +// +// Copyright 2003 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef XSPLASHOUTPUTDEV_H +#define XSPLASHOUTPUTDEV_H + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +#include +#include "SplashTypes.h" +#include "SplashOutputDev.h" + +//------------------------------------------------------------------------ + +#define xOutMaxRGBCube 6 // max size of RGB color cube + +//------------------------------------------------------------------------ +// XSplashOutputDev +//------------------------------------------------------------------------ + +class XSplashOutputDev: public SplashOutputDev { +public: + + XSplashOutputDev(Display *displayA, int screenNumA, + Visual *visualA, Colormap colormapA, + GBool reverseVideoA, SplashColor paperColorA, + GBool installCmapA, int rgbCubeSizeA, + GBool incrementalUpdateA, + void (*redrawCbkA)(void *data), + void *redrawCbkDataA); + + virtual ~XSplashOutputDev(); + + //----- initialization and control + + // Start a page. + virtual void startPage(int pageNum, GfxState *state); + + // End a page. + virtual void endPage(); + + // Dump page contents to display. + virtual void dump(); + + //----- update text state + virtual void updateFont(GfxState *state); + + //----- text drawing + virtual void drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode code, Unicode *u, int uLen); + virtual GBool beginType3Char(GfxState *state, double x, double y, + double dx, double dy, + CharCode code, Unicode *u, int uLen); + + //----- special access + + // Clear out the document (used when displaying an empty window). + void clear(); + + // Copy the rectangle (srcX, srcY, width, height) to (destX, destY) + // in destDC. + void redraw(int srcX, int srcY, + Drawable destDrawable, GC destGC, + int destX, int destY, + int width, int height); + + // Find a string. If is true, starts looking at the + // top of the page; else if is true, starts looking + // immediately after the last find result; else starts looking at + // ,. If is true, stops looking at the + // bottom of the page; else if is true, stops looking + // just before the last find result; else stops looking at + // ,. + GBool findText(Unicode *s, int len, + GBool startAtTop, GBool stopAtBottom, + GBool startAtLast, GBool stopAtLast, + int *xMin, int *yMin, + int *xMax, int *yMax); + + // Get the text which is inside the specified rectangle. + GString *getText(int xMin, int yMin, int xMax, int yMax); + +private: + + GBool incrementalUpdate; // incrementally update the display? + void (*redrawCbk)(void *data); + void *redrawCbkData; + TextPage *text; // text from the current page + + Display *display; // X display pointer + Visual *visual; // X visual + Guint depth; // visual depth + GBool trueColor; // set if using a TrueColor visual + int rDiv, gDiv, bDiv; // RGB right shifts (for TrueColor) + int rShift, gShift, bShift; // RGB left shifts (for TrueColor) + int rgbCubeSize; // size of color cube (for non-TrueColor) + Gulong // color cube (for non-TrueColor) + colors[xOutMaxRGBCube * xOutMaxRGBCube * xOutMaxRGBCube]; +}; + +#endif diff --git a/pdf/xpdf/pdftoppm.cc b/pdf/xpdf/pdftoppm.cc new file mode 100644 index 0000000..9be5c64 --- /dev/null +++ b/pdf/xpdf/pdftoppm.cc @@ -0,0 +1,189 @@ +//======================================================================== +// +// pdftoppm.cc +// +// Copyright 2003 Glyph & Cog, LLC +// +//======================================================================== + +#include +#include +#include "parseargs.h" +#include "gmem.h" +#include "GString.h" +#include "GlobalParams.h" +#include "Object.h" +#include "PDFDoc.h" +#include "SplashBitmap.h" +#include "Splash.h" +#include "SplashOutputDev.h" +#include "config.h" + +static int firstPage = 1; +static int lastPage = 0; +static int resolution = 150; +static GBool mono = gFalse; +static GBool gray = gFalse; +static char enableT1libStr[16] = ""; +static char enableFreeTypeStr[16] = ""; +static char antialiasStr[16] = ""; +static char ownerPassword[33] = ""; +static char userPassword[33] = ""; +static GBool quiet = gFalse; +static char cfgFileName[256] = ""; +static GBool printVersion = gFalse; +static GBool printHelp = gFalse; + +static ArgDesc argDesc[] = { + {"-f", argInt, &firstPage, 0, + "first page to print"}, + {"-l", argInt, &lastPage, 0, + "last page to print"}, + {"-r", argInt, &resolution, 0, + "resolution, in DPI (default is 150)"}, + {"-mono", argFlag, &mono, 0, + "generate a monochrome PBM file"}, + {"-gray", argFlag, &gray, 0, + "generate a grayscale PGM file"}, +#if HAVE_T1LIB_H + {"-t1lib", argString, enableT1libStr, sizeof(enableT1libStr), + "enable t1lib font rasterizer: yes, no"}, +#endif +#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H + {"-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr), + "enable FreeType font rasterizer: yes, no"}, +#endif + {"-aa", argString, antialiasStr, sizeof(antialiasStr), + "enable font anti-aliasing: yes, no"}, + {"-opw", argString, ownerPassword, sizeof(ownerPassword), + "owner password (for encrypted files)"}, + {"-upw", argString, userPassword, sizeof(userPassword), + "user password (for encrypted files)"}, + {"-q", argFlag, &quiet, 0, + "don't print any messages or errors"}, + {"-cfg", argString, cfgFileName, sizeof(cfgFileName), + "configuration file to use in place of .xpdfrc"}, + {"-v", argFlag, &printVersion, 0, + "print copyright and version info"}, + {"-h", argFlag, &printHelp, 0, + "print usage information"}, + {"-help", argFlag, &printHelp, 0, + "print usage information"}, + {"--help", argFlag, &printHelp, 0, + "print usage information"}, + {"-?", argFlag, &printHelp, 0, + "print usage information"}, + {NULL} +}; + +int main(int argc, char *argv[]) { + PDFDoc *doc; + GString *fileName; + char *ppmRoot; + char ppmFile[512]; + GString *ownerPW, *userPW; + SplashColor paperColor; + SplashOutputDev *splashOut; + GBool ok; + int exitCode; + int pg; + + exitCode = 99; + + // parse args + ok = parseArgs(argDesc, &argc, argv); + if (mono && gray) { + ok = gFalse; + } + if (!ok || argc != 3 || printVersion || printHelp) { + fprintf(stderr, "pdftoppm version %s\n", xpdfVersion); + fprintf(stderr, "%s\n", xpdfCopyright); + if (!printVersion) { + printUsage("pdftoppm", " ", argDesc); + } + goto err0; + } + fileName = new GString(argv[1]); + ppmRoot = argv[2]; + + // read config file + globalParams = new GlobalParams(cfgFileName); + globalParams->setupBaseFonts(NULL); + if (enableT1libStr[0]) { + if (!globalParams->setEnableT1lib(enableT1libStr)) { + fprintf(stderr, "Bad '-t1lib' value on command line\n"); + } + } + if (enableFreeTypeStr[0]) { + if (!globalParams->setEnableFreeType(enableFreeTypeStr)) { + fprintf(stderr, "Bad '-freetype' value on command line\n"); + } + } + if (antialiasStr[0]) { + if (!globalParams->setAntialias(antialiasStr)) { + fprintf(stderr, "Bad '-aa' value on command line\n"); + } + } + if (quiet) { + globalParams->setErrQuiet(quiet); + } + + // open PDF file + if (ownerPassword[0]) { + ownerPW = new GString(ownerPassword); + } else { + ownerPW = NULL; + } + if (userPassword[0]) { + userPW = new GString(userPassword); + } else { + userPW = NULL; + } + doc = new PDFDoc(fileName, ownerPW, userPW); + if (userPW) { + delete userPW; + } + if (ownerPW) { + delete ownerPW; + } + if (!doc->isOk()) { + exitCode = 1; + goto err1; + } + + // get page range + if (firstPage < 1) + firstPage = 1; + if (lastPage < 1 || lastPage > doc->getNumPages()) + lastPage = doc->getNumPages(); + + // write PPM files + paperColor.rgb8 = splashMakeRGB8(255, 255, 255); + splashOut = new SplashOutputDev(mono ? splashModeMono1 : + gray ? splashModeMono8 : + splashModeRGB8, + gFalse, paperColor); + splashOut->startDoc(doc->getXRef()); + for (pg = firstPage; pg <= lastPage; ++pg) { + doc->displayPage(splashOut, pg, resolution, resolution, 0, gTrue, gFalse); + sprintf(ppmFile, "%.*s-%06d.%s", + (int)sizeof(ppmFile) - 32, ppmRoot, pg, + mono ? "pbm" : gray ? "pgm" : "ppm"); + splashOut->getBitmap()->writePNMFile(ppmFile); + } + delete splashOut; + + exitCode = 0; + + // clean up + err1: + delete doc; + delete globalParams; + err0: + + // check for memory leaks + Object::memCheck(stderr); + gMemReport(stderr); + + return exitCode; +} -- cgit v0.9.1