Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/pdf/xpdf
diff options
context:
space:
mode:
authorMartin Kretzschmar <mkretzschmar@src.gnome.org>2004-05-17 18:12:38 (GMT)
committer Martin Kretzschmar <mkretzschmar@src.gnome.org>2004-05-17 18:12:38 (GMT)
commitbace4ea18c03bfcaadab55300bc15290f87540c7 (patch)
tree48c9c670e4dde608d50fe68e00e783f18af8697b /pdf/xpdf
parentad63666daeeda50acc7630132d61fe044634fddd (diff)
Initial revision
Diffstat (limited to 'pdf/xpdf')
-rw-r--r--pdf/xpdf/JArithmeticDecoder.cc300
-rw-r--r--pdf/xpdf/JArithmeticDecoder.h91
-rw-r--r--pdf/xpdf/JPXStream.cc2822
-rw-r--r--pdf/xpdf/JPXStream.h340
-rw-r--r--pdf/xpdf/SplashOutputDev.cc1345
-rw-r--r--pdf/xpdf/SplashOutputDev.h195
-rw-r--r--pdf/xpdf/XSplashOutputDev.cc323
-rw-r--r--pdf/xpdf/XSplashOutputDev.h110
-rw-r--r--pdf/xpdf/pdftoppm.cc189
9 files changed, 5715 insertions, 0 deletions
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 <aconf.h>
+
+#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 <aconf.h>
+
+#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 *<x> 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 <aconf.h>
+
+#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 <resLevel>
+// - 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 <aconf.h>
+
+#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 <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include <math.h>
+#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 <aconf.h>
+
+#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 <pattern>. <pattern>
+ // 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 <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#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 <X11/Xlib.h>
+#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 <startAtTop> is true, starts looking at the
+ // top of the page; else if <startAtLast> is true, starts looking
+ // immediately after the last find result; else starts looking at
+ // <xMin>,<yMin>. If <stopAtBottom> is true, stops looking at the
+ // bottom of the page; else if <stopAtLast> is true, stops looking
+ // just before the last find result; else stops looking at
+ // <xMax>,<yMax>.
+ 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 <aconf.h>
+#include <stdio.h>
+#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", "<PDF-file> <PPM-root>", 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;
+}