X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=pdf2swf%2Fxpdf%2FJPXStream.cc;fp=pdf2swf%2Fxpdf%2FJPXStream.cc;h=defa7d2adb08e051fa7614c38c454a6905c2009d;hb=c7432833fe3a6469d63fad135151a92e12877b94;hp=0000000000000000000000000000000000000000;hpb=b9ddd4a2ed601bc01ddacc69d2522687a93a94ce;p=swftools.git diff --git a/pdf2swf/xpdf/JPXStream.cc b/pdf2swf/xpdf/JPXStream.cc new file mode 100644 index 0000000..defa7d2 --- /dev/null +++ b/pdf2swf/xpdf/JPXStream.cc @@ -0,0 +1,2822 @@ +//======================================================================== +// +// JPXStream.cc +// +// Copyright 2002-2003 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include "gmem.h" +#include "Error.h" +#include "JArithmeticDecoder.h" +#include "JPXStream.h" + +//~ to do: +// - precincts +// - ROI +// - progression order changes +// - packed packet headers +// - support for palettes, channel maps, etc. +// - make sure all needed JP2/JPX subboxes are parsed (readBoxes) +// - can we assume that QCC segments must come after the QCD segment? +// - skip EPH markers (readTilePartData) +// - handle tilePartToEOC in readTilePartData +// - deal with multiple codeword segments (readTilePartData, +// readCodeBlockData) +// - progression orders 2, 3, and 4 +// - in coefficient decoding (readCodeBlockData): +// - termination pattern: terminate after every coding pass +// - error resilience segmentation symbol +// - selective arithmetic coding bypass +// - vertically causal context formation +// - coeffs longer than 31 bits (should just ignore the extra bits?) +// - handle boxes larger than 2^32 bytes +// - the fixed-point arithmetic won't handle 16-bit pixels + +//------------------------------------------------------------------------ + +// number of contexts for the arithmetic decoder +#define jpxNContexts 19 + +#define jpxContextSigProp 0 // 0 - 8: significance prop and cleanup +#define jpxContextSign 9 // 9 - 13: sign +#define jpxContextMagRef 14 // 14 -16: magnitude refinement +#define jpxContextRunLength 17 // cleanup: run length +#define jpxContextUniform 18 // cleanup: first signif coeff + +//------------------------------------------------------------------------ + +#define jpxPassSigProp 0 +#define jpxPassMagRef 1 +#define jpxPassCleanup 2 + +//------------------------------------------------------------------------ + +// arithmetic decoder context for the significance propagation and +// cleanup passes: +// [horiz][vert][diag][subband] +// where subband = 0 for HL +// = 1 for LH and LL +// = 2 for HH +static Guint sigPropContext[3][3][5][3] = { + {{{ 0, 0, 0 }, // horiz=0, vert=0, diag=0 + { 1, 1, 3 }, // horiz=0, vert=0, diag=1 + { 2, 2, 6 }, // horiz=0, vert=0, diag=2 + { 2, 2, 8 }, // horiz=0, vert=0, diag=3 + { 2, 2, 8 }}, // horiz=0, vert=0, diag=4 + {{ 5, 3, 1 }, // horiz=0, vert=1, diag=0 + { 6, 3, 4 }, // horiz=0, vert=1, diag=1 + { 6, 3, 7 }, // horiz=0, vert=1, diag=2 + { 6, 3, 8 }, // horiz=0, vert=1, diag=3 + { 6, 3, 8 }}, // horiz=0, vert=1, diag=4 + {{ 8, 4, 2 }, // horiz=0, vert=2, diag=0 + { 8, 4, 5 }, // horiz=0, vert=2, diag=1 + { 8, 4, 7 }, // horiz=0, vert=2, diag=2 + { 8, 4, 8 }, // horiz=0, vert=2, diag=3 + { 8, 4, 8 }}}, // horiz=0, vert=2, diag=4 + {{{ 3, 5, 1 }, // horiz=1, vert=0, diag=0 + { 3, 6, 4 }, // horiz=1, vert=0, diag=1 + { 3, 6, 7 }, // horiz=1, vert=0, diag=2 + { 3, 6, 8 }, // horiz=1, vert=0, diag=3 + { 3, 6, 8 }}, // horiz=1, vert=0, diag=4 + {{ 7, 7, 2 }, // horiz=1, vert=1, diag=0 + { 7, 7, 5 }, // horiz=1, vert=1, diag=1 + { 7, 7, 7 }, // horiz=1, vert=1, diag=2 + { 7, 7, 8 }, // horiz=1, vert=1, diag=3 + { 7, 7, 8 }}, // horiz=1, vert=1, diag=4 + {{ 8, 7, 2 }, // horiz=1, vert=2, diag=0 + { 8, 7, 5 }, // horiz=1, vert=2, diag=1 + { 8, 7, 7 }, // horiz=1, vert=2, diag=2 + { 8, 7, 8 }, // horiz=1, vert=2, diag=3 + { 8, 7, 8 }}}, // horiz=1, vert=2, diag=4 + {{{ 4, 8, 2 }, // horiz=2, vert=0, diag=0 + { 4, 8, 5 }, // horiz=2, vert=0, diag=1 + { 4, 8, 7 }, // horiz=2, vert=0, diag=2 + { 4, 8, 8 }, // horiz=2, vert=0, diag=3 + { 4, 8, 8 }}, // horiz=2, vert=0, diag=4 + {{ 7, 8, 2 }, // horiz=2, vert=1, diag=0 + { 7, 8, 5 }, // horiz=2, vert=1, diag=1 + { 7, 8, 7 }, // horiz=2, vert=1, diag=2 + { 7, 8, 8 }, // horiz=2, vert=1, diag=3 + { 7, 8, 8 }}, // horiz=2, vert=1, diag=4 + {{ 8, 8, 2 }, // horiz=2, vert=2, diag=0 + { 8, 8, 5 }, // horiz=2, vert=2, diag=1 + { 8, 8, 7 }, // horiz=2, vert=2, diag=2 + { 8, 8, 8 }, // horiz=2, vert=2, diag=3 + { 8, 8, 8 }}} // horiz=2, vert=2, diag=4 +}; + +// arithmetic decoder context and xor bit for the sign bit in the +// significance propagation pass: +// [horiz][vert][k] +// where horiz/vert are offset by 2 (i.e., range is -2 .. 2) +// and k = 0 for the context +// = 1 for the xor bit +static Guint signContext[5][5][2] = { + {{ 13, 1 }, // horiz=-2, vert=-2 + { 13, 1 }, // horiz=-2, vert=-1 + { 12, 1 }, // horiz=-2, vert= 0 + { 11, 1 }, // horiz=-2, vert=+1 + { 11, 1 }}, // horiz=-2, vert=+2 + {{ 13, 1 }, // horiz=-1, vert=-2 + { 13, 1 }, // horiz=-1, vert=-1 + { 12, 1 }, // horiz=-1, vert= 0 + { 11, 1 }, // horiz=-1, vert=+1 + { 11, 1 }}, // horiz=-1, vert=+2 + {{ 10, 1 }, // horiz= 0, vert=-2 + { 10, 1 }, // horiz= 0, vert=-1 + { 9, 0 }, // horiz= 0, vert= 0 + { 10, 0 }, // horiz= 0, vert=+1 + { 10, 0 }}, // horiz= 0, vert=+2 + {{ 11, 0 }, // horiz=+1, vert=-2 + { 11, 0 }, // horiz=+1, vert=-1 + { 12, 0 }, // horiz=+1, vert= 0 + { 13, 0 }, // horiz=+1, vert=+1 + { 13, 0 }}, // horiz=+1, vert=+2 + {{ 11, 0 }, // horiz=+2, vert=-2 + { 11, 0 }, // horiz=+2, vert=-1 + { 12, 0 }, // horiz=+2, vert= 0 + { 13, 0 }, // horiz=+2, vert=+1 + { 13, 0 }}, // horiz=+2, vert=+2 +}; + +//------------------------------------------------------------------------ + +// constants used in the IDWT +#define idwtAlpha -1.586134342059924 +#define idwtBeta -0.052980118572961 +#define idwtGamma 0.882911075530934 +#define idwtDelta 0.443506852043971 +#define idwtKappa 1.230174104914001 +#define idwtIKappa (1.0 / idwtKappa) + +// number of bits to the right of the decimal point for the fixed +// point arithmetic used in the IDWT +#define fracBits 16 + +//------------------------------------------------------------------------ + +// floor(x / y) +#define jpxFloorDiv(x, y) ((x) / (y)) + +// floor(x / 2^y) +#define jpxFloorDivPow2(x, y) ((x) >> (y)) + +// ceil(x / y) +#define jpxCeilDiv(x, y) (((x) + (y) - 1) / (y)) + +// ceil(x / 2^y) +#define jpxCeilDivPow2(x, y) (((x) + (1 << (y)) - 1) >> (y)) + +//------------------------------------------------------------------------ + +JPXStream::JPXStream(Stream *strA): + FilterStream(strA) +{ + nComps = 0; + bpc = NULL; + width = height = 0; + haveCS = gFalse; + havePalette = gFalse; + haveCompMap = gFalse; + haveChannelDefn = gFalse; + + img.tiles = NULL; + bitBuf = 0; + bitBufLen = 0; + bitBufSkip = gFalse; + byteCount = 0; +} + +JPXStream::~JPXStream() { + JPXTile *tile; + JPXTileComp *tileComp; + JPXResLevel *resLevel; + JPXPrecinct *precinct; + JPXSubband *subband; + JPXCodeBlock *cb; + Guint comp, i, k, r, pre, sb; + + gfree(bpc); + if (havePalette) { + gfree(palette.bpc); + gfree(palette.c); + } + if (haveCompMap) { + gfree(compMap.comp); + gfree(compMap.type); + gfree(compMap.pComp); + } + if (haveChannelDefn) { + gfree(channelDefn.idx); + gfree(channelDefn.type); + gfree(channelDefn.assoc); + } + + if (img.tiles) { + for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { + tile = &img.tiles[i]; + if (tile->tileComps) { + for (comp = 0; comp < img.nComps; ++comp) { + tileComp = &tile->tileComps[comp]; + gfree(tileComp->quantSteps); + gfree(tileComp->data); + gfree(tileComp->buf); + if (tileComp->resLevels) { + for (r = 0; r <= tileComp->nDecompLevels; ++r) { + resLevel = &tileComp->resLevels[r]; + if (resLevel->precincts) { + for (pre = 0; pre < 1; ++pre) { + precinct = &resLevel->precincts[pre]; + if (precinct->subbands) { + for (sb = 0; sb < (r == 0 ? 1 : 3); ++sb) { + subband = &precinct->subbands[sb]; + gfree(subband->inclusion); + gfree(subband->zeroBitPlane); + if (subband->cbs) { + for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) { + cb = &subband->cbs[k]; + gfree(cb->coeffs); + if (cb->stats) { + delete cb->stats; + } + } + gfree(subband->cbs); + } + } + gfree(precinct->subbands); + } + } + gfree(img.tiles[i].tileComps[comp].resLevels[r].precincts); + } + } + gfree(img.tiles[i].tileComps[comp].resLevels); + } + } + gfree(img.tiles[i].tileComps); + } + } + gfree(img.tiles); + } + delete str; +} + +void JPXStream::reset() { + str->reset(); + if (readBoxes()) { + curY = img.yOffset; + } else { + // readBoxes reported an error, so we go immediately to EOF + curY = img.ySize; + } + curX = img.xOffset; + curComp = 0; + readBufLen = 0; +} + +int JPXStream::getChar() { + int c; + + if (readBufLen < 8) { + fillReadBuf(); + } + if (readBufLen == 8) { + c = readBuf & 0xff; + readBufLen = 0; + } else if (readBufLen > 8) { + c = (readBuf >> (readBufLen - 8)) & 0xff; + readBufLen -= 8; + } else if (readBufLen == 0) { + c = EOF; + } else { + c = (readBuf << (8 - readBufLen)) & 0xff; + readBufLen = 0; + } + return c; +} + +int JPXStream::lookChar() { + int c; + + if (readBufLen < 8) { + fillReadBuf(); + } + if (readBufLen == 8) { + c = readBuf & 0xff; + } else if (readBufLen > 8) { + c = (readBuf >> (readBufLen - 8)) & 0xff; + } else if (readBufLen == 0) { + c = EOF; + } else { + c = (readBuf << (8 - readBufLen)) & 0xff; + } + return c; +} + +void JPXStream::fillReadBuf() { + JPXTileComp *tileComp; + Guint tileIdx, tx, ty; + int pix, pixBits; + + do { + if (curY >= img.ySize) { + return; + } + tileIdx = ((curY - img.yTileOffset) / img.yTileSize) * img.nXTiles + + (curX - img.xTileOffset) / img.xTileSize; +#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid + tileComp = &img.tiles[tileIdx].tileComps[curComp]; +#else + tileComp = &img.tiles[tileIdx].tileComps[havePalette ? 0 : curComp]; +#endif + tx = jpxCeilDiv((curX - img.xTileOffset) % img.xTileSize, tileComp->hSep); + ty = jpxCeilDiv((curY - img.yTileOffset) % img.yTileSize, tileComp->vSep); + pix = (int)tileComp->data[ty * (tileComp->x1 - tileComp->x0) + tx]; + pixBits = tileComp->prec; +#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid + if (++curComp == img.nComps) { +#else + if (havePalette) { + if (pix >= 0 && pix < palette.nEntries) { + pix = palette.c[pix * palette.nComps + curComp]; + } else { + pix = + pixBits = palette.bpc[curComp]; + } + if (++curComp == (Guint)(havePalette ? palette.nComps : img.nComps)) { +#endif + curComp = 0; + if (++curX == img.xSize) { + curX = img.xOffset; + ++curY; + } + } + if (pixBits == 8) { + readBuf = (readBuf << 8) | (pix & 0xff); + } else { + readBuf = (readBuf << pixBits) | (pix & ((1 << pixBits) - 1)); + } + readBufLen += pixBits; + } while (readBufLen < 8); +} + +GString *JPXStream::getPSFilter(int psLevel, char *indent) { + return NULL; +} + +GBool JPXStream::isBinary(GBool last) { + return str->isBinary(gTrue); +} + +GBool JPXStream::readBoxes() { + Guint boxType, boxLen, dataLen; + Guint bpc1, compression, unknownColorspace, ipr; + Guint i, j; + + haveImgHdr = gFalse; + + // check for a naked JPEG 2000 codestream (without the JP2/JPX + // wrapper) -- this appears to be a violation of the PDF spec, but + // Acrobat allows it + if (str->lookChar() == 0xff) { + error(getPos(), "Naked JPEG 2000 codestream, missing JP2/JPX wrapper"); + readCodestream(0); + nComps = img.nComps; + bpc = (Guint *)gmalloc(nComps * sizeof(Guint)); + for (i = 0; i < nComps; ++i) { + bpc[i] = img.tiles[0].tileComps[i].prec; + } + width = img.xSize - img.xOffset; + height = img.ySize - img.yOffset; + return gTrue; + } + + while (readBoxHdr(&boxType, &boxLen, &dataLen)) { + switch (boxType) { + case 0x6a703268: // JP2 header + // this is a grouping box ('superbox') which has no real + // contents and doesn't appear to be used consistently, i.e., + // some things which should be subboxes of the JP2 header box + // show up outside of it - so we simply ignore the JP2 header + // box + break; + case 0x69686472: // image header + if (!readULong(&height) || + !readULong(&width) || + !readUWord(&nComps) || + !readUByte(&bpc1) || + !readUByte(&compression) || + !readUByte(&unknownColorspace) || + !readUByte(&ipr)) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + if (compression != 7) { + error(getPos(), "Unknown compression type in JPX stream"); + return gFalse; + } + bpc = (Guint *)gmalloc(nComps * sizeof(Guint)); + for (i = 0; i < nComps; ++i) { + bpc[i] = bpc1; + } + haveImgHdr = gTrue; + break; + case 0x62706363: // bits per component + if (!haveImgHdr) { + error(getPos(), "Found bits per component box before image header box in JPX stream"); + return gFalse; + } + if (dataLen != nComps) { + error(getPos(), "Invalid bits per component box in JPX stream"); + return gFalse; + } + for (i = 0; i < nComps; ++i) { + if (!readUByte(&bpc[i])) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + } + break; + case 0x636F6C72: // color specification + if (!readColorSpecBox(dataLen)) { + return gFalse; + } + break; + case 0x70636c72: // palette + if (!readUWord(&palette.nEntries) || + !readUByte(&palette.nComps)) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + palette.bpc = (Guint *)gmalloc(palette.nComps * sizeof(Guint)); + palette.c = + (int *)gmalloc(palette.nEntries * palette.nComps * sizeof(int)); + for (i = 0; i < palette.nComps; ++i) { + if (!readUByte(&palette.bpc[i])) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + ++palette.bpc[i]; + } + for (i = 0; i < palette.nEntries; ++i) { + for (j = 0; j < palette.nComps; ++j) { + if (!readNBytes(((palette.bpc[j] & 0x7f) + 7) >> 3, + (palette.bpc[j] & 0x80) ? gTrue : gFalse, + &palette.c[i * palette.nComps + j])) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + } + } + havePalette = gTrue; + break; + case 0x636d6170: // component mapping + compMap.nChannels = dataLen / 4; + compMap.comp = (Guint *)gmalloc(compMap.nChannels * sizeof(Guint)); + compMap.type = (Guint *)gmalloc(compMap.nChannels * sizeof(Guint)); + compMap.pComp = (Guint *)gmalloc(compMap.nChannels * sizeof(Guint)); + for (i = 0; i < compMap.nChannels; ++i) { + if (!readUWord(&compMap.comp[i]) || + !readUByte(&compMap.type[i]) || + !readUByte(&compMap.pComp[i])) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + } + haveCompMap = gTrue; + break; + case 0x63646566: // channel definition + if (!readUWord(&channelDefn.nChannels)) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + channelDefn.idx = + (Guint *)gmalloc(channelDefn.nChannels * sizeof(Guint)); + channelDefn.type = + (Guint *)gmalloc(channelDefn.nChannels * sizeof(Guint)); + channelDefn.assoc = + (Guint *)gmalloc(channelDefn.nChannels * sizeof(Guint)); + for (i = 0; i < channelDefn.nChannels; ++i) { + if (!readUWord(&channelDefn.idx[i]) || + !readUWord(&channelDefn.type[i]) || + !readUWord(&channelDefn.assoc[i])) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + } + haveChannelDefn = gTrue; + break; + case 0x6A703263: // contiguous codestream + if (!bpc) { + error(getPos(), "JPX stream is missing the image header box"); + return gFalse; + } + if (!haveCS) { + error(getPos(), "JPX stream has no supported color spec"); + return gFalse; + } + if (!readCodestream(dataLen)) { + return gFalse; + } + break; + default: + for (i = 0; i < dataLen; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Unexpected EOF in JPX stream"); + return gFalse; + } + } + break; + } + } + return gTrue; +} + +GBool JPXStream::readColorSpecBox(Guint dataLen) { + JPXColorSpec newCS; + Guint csApprox, csEnum; + Guint i; + GBool ok; + + ok = gFalse; + if (!readUByte(&newCS.meth) || + !readByte(&newCS.prec) || + !readUByte(&csApprox)) { + goto err; + } + switch (newCS.meth) { + case 1: // enumerated colorspace + if (!readULong(&csEnum)) { + goto err; + } + newCS.enumerated.type = (JPXColorSpaceType)csEnum; + switch (newCS.enumerated.type) { + case jpxCSBiLevel: + ok = gTrue; + break; + case jpxCSYCbCr1: + ok = gTrue; + break; + case jpxCSYCbCr2: + ok = gTrue; + break; + case jpxCSYCBCr3: + ok = gTrue; + break; + case jpxCSPhotoYCC: + ok = gTrue; + break; + case jpxCSCMY: + ok = gTrue; + break; + case jpxCSCMYK: + ok = gTrue; + break; + case jpxCSYCCK: + ok = gTrue; + break; + case jpxCSCIELab: + if (dataLen == 3 + 7*4) { + if (!readULong(&newCS.enumerated.cieLab.rl) || + !readULong(&newCS.enumerated.cieLab.ol) || + !readULong(&newCS.enumerated.cieLab.ra) || + !readULong(&newCS.enumerated.cieLab.oa) || + !readULong(&newCS.enumerated.cieLab.rb) || + !readULong(&newCS.enumerated.cieLab.ob) || + !readULong(&newCS.enumerated.cieLab.il)) { + goto err; + } + } else if (dataLen == 3) { + //~ this assumes the 8-bit case + newCS.enumerated.cieLab.rl = 100; + newCS.enumerated.cieLab.ol = 0; + newCS.enumerated.cieLab.ra = 255; + newCS.enumerated.cieLab.oa = 128; + newCS.enumerated.cieLab.rb = 255; + newCS.enumerated.cieLab.ob = 96; + newCS.enumerated.cieLab.il = 0x00443530; + } else { + goto err; + } + ok = gTrue; + break; + case jpxCSsRGB: + ok = gTrue; + break; + case jpxCSGrayscale: + ok = gTrue; + break; + case jpxCSBiLevel2: + ok = gTrue; + break; + case jpxCSCIEJab: + // not allowed in PDF + goto err; + case jpxCSCISesRGB: + ok = gTrue; + break; + case jpxCSROMMRGB: + ok = gTrue; + break; + case jpxCSsRGBYCbCr: + ok = gTrue; + break; + case jpxCSYPbPr1125: + ok = gTrue; + break; + case jpxCSYPbPr1250: + ok = gTrue; + break; + default: + goto err; + } + break; + case 2: // restricted ICC profile + case 3: // any ICC profile (JPX) + case 4: // vendor color (JPX) + for (i = 0; i < dataLen - 3; ++i) { + if (str->getChar() == EOF) { + goto err; + } + } + break; + } + + if (ok && (!haveCS || newCS.prec > cs.prec)) { + cs = newCS; + haveCS = gTrue; + } + + return gTrue; + + err: + error(getPos(), "Error in JPX color spec"); + return gFalse; +} + +GBool JPXStream::readCodestream(Guint len) { + JPXTile *tile; + JPXTileComp *tileComp; + int segType; + GBool haveSIZ, haveCOD, haveQCD, haveSOT; + Guint precinctSize, style; + Guint segLen, capabilities, comp, i, j, r; + + //----- main header + haveSIZ = haveCOD = haveQCD = haveSOT = gFalse; + do { + if (!readMarkerHdr(&segType, &segLen)) { + error(getPos(), "Error in JPX codestream"); + return gFalse; + } + switch (segType) { + case 0x4f: // SOC - start of codestream + // marker only + break; + case 0x51: // SIZ - image and tile size + if (!readUWord(&capabilities) || + !readULong(&img.xSize) || + !readULong(&img.ySize) || + !readULong(&img.xOffset) || + !readULong(&img.yOffset) || + !readULong(&img.xTileSize) || + !readULong(&img.yTileSize) || + !readULong(&img.xTileOffset) || + !readULong(&img.yTileOffset) || + !readUWord(&img.nComps)) { + error(getPos(), "Error in JPX SIZ marker segment"); + return gFalse; + } + if (haveImgHdr && img.nComps != nComps) { + error(getPos(), "Different number of components in JPX SIZ marker segment"); + return gFalse; + } + img.nXTiles = (img.xSize - img.xTileOffset + img.xTileSize - 1) + / img.xTileSize; + img.nYTiles = (img.ySize - img.yTileOffset + img.yTileSize - 1) + / img.yTileSize; + img.tiles = (JPXTile *)gmalloc(img.nXTiles * img.nYTiles * + sizeof(JPXTile)); + for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { + img.tiles[i].tileComps = (JPXTileComp *)gmalloc(img.nComps * + sizeof(JPXTileComp)); + for (comp = 0; comp < img.nComps; ++comp) { + img.tiles[i].tileComps[comp].quantSteps = NULL; + img.tiles[i].tileComps[comp].data = NULL; + img.tiles[i].tileComps[comp].buf = NULL; + img.tiles[i].tileComps[comp].resLevels = NULL; + } + } + for (comp = 0; comp < img.nComps; ++comp) { + if (!readUByte(&img.tiles[0].tileComps[comp].prec) || + !readUByte(&img.tiles[0].tileComps[comp].hSep) || + !readUByte(&img.tiles[0].tileComps[comp].vSep)) { + error(getPos(), "Error in JPX SIZ marker segment"); + return gFalse; + } + img.tiles[0].tileComps[comp].sgned = + (img.tiles[0].tileComps[comp].prec & 0x80) ? gTrue : gFalse; + img.tiles[0].tileComps[comp].prec = + (img.tiles[0].tileComps[comp].prec & 0x7f) + 1; + for (i = 1; i < img.nXTiles * img.nYTiles; ++i) { + img.tiles[i].tileComps[comp] = img.tiles[0].tileComps[comp]; + } + } + haveSIZ = gTrue; + break; + case 0x52: // COD - coding style default + if (!readUByte(&img.tiles[0].tileComps[0].style) || + !readUByte(&img.tiles[0].progOrder) || + !readUWord(&img.tiles[0].nLayers) || + !readUByte(&img.tiles[0].multiComp) || + !readUByte(&img.tiles[0].tileComps[0].nDecompLevels) || + !readUByte(&img.tiles[0].tileComps[0].codeBlockW) || + !readUByte(&img.tiles[0].tileComps[0].codeBlockH) || + !readUByte(&img.tiles[0].tileComps[0].codeBlockStyle) || + !readUByte(&img.tiles[0].tileComps[0].transform)) { + error(getPos(), "Error in JPX COD marker segment"); + return gFalse; + } + img.tiles[0].tileComps[0].codeBlockW += 2; + img.tiles[0].tileComps[0].codeBlockH += 2; + for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { + if (i != 0) { + img.tiles[i].progOrder = img.tiles[0].progOrder; + img.tiles[i].nLayers = img.tiles[0].nLayers; + img.tiles[i].multiComp = img.tiles[0].multiComp; + } + for (comp = 0; comp < img.nComps; ++comp) { + if (!(i == 0 && comp == 0)) { + img.tiles[i].tileComps[comp].style = + img.tiles[0].tileComps[0].style; + img.tiles[i].tileComps[comp].nDecompLevels = + img.tiles[0].tileComps[0].nDecompLevels; + img.tiles[i].tileComps[comp].codeBlockW = + img.tiles[0].tileComps[0].codeBlockW; + img.tiles[i].tileComps[comp].codeBlockH = + img.tiles[0].tileComps[0].codeBlockH; + img.tiles[i].tileComps[comp].codeBlockStyle = + img.tiles[0].tileComps[0].codeBlockStyle; + img.tiles[i].tileComps[comp].transform = + img.tiles[0].tileComps[0].transform; + } + img.tiles[i].tileComps[comp].resLevels = + (JPXResLevel *)gmalloc( + (img.tiles[i].tileComps[comp].nDecompLevels + 1) * + sizeof(JPXResLevel)); + for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { + img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL; + } + } + } + for (r = 0; r <= img.tiles[0].tileComps[0].nDecompLevels; ++r) { + if (img.tiles[0].tileComps[0].style & 0x01) { + if (!readUByte(&precinctSize)) { + error(getPos(), "Error in JPX COD marker segment"); + return gFalse; + } + img.tiles[0].tileComps[0].resLevels[r].precinctWidth = + precinctSize & 0x0f; + img.tiles[0].tileComps[0].resLevels[r].precinctHeight = + (precinctSize >> 4) & 0x0f; + } else { + img.tiles[0].tileComps[0].resLevels[r].precinctWidth = 15; + img.tiles[0].tileComps[0].resLevels[r].precinctHeight = 15; + } + } + for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { + for (comp = 0; comp < img.nComps; ++comp) { + if (!(i == 0 && comp == 0)) { + for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { + img.tiles[i].tileComps[comp].resLevels[r].precinctWidth = + img.tiles[0].tileComps[0].resLevels[r].precinctWidth; + img.tiles[i].tileComps[comp].resLevels[r].precinctHeight = + img.tiles[0].tileComps[0].resLevels[r].precinctHeight; + } + } + } + } + haveCOD = gTrue; + break; + case 0x53: // COC - coding style component + if (!haveCOD) { + error(getPos(), "JPX COC marker segment before COD segment"); + return gFalse; + } + if ((img.nComps > 256 && !readUWord(&comp)) || + (img.nComps <= 256 && !readUByte(&comp)) || + comp >= img.nComps || + !readUByte(&style) || + !readUByte(&img.tiles[0].tileComps[comp].nDecompLevels) || + !readUByte(&img.tiles[0].tileComps[comp].codeBlockW) || + !readUByte(&img.tiles[0].tileComps[comp].codeBlockH) || + !readUByte(&img.tiles[0].tileComps[comp].codeBlockStyle) || + !readUByte(&img.tiles[0].tileComps[comp].transform)) { + error(getPos(), "Error in JPX COC marker segment"); + return gFalse; + } + img.tiles[0].tileComps[comp].style = + (img.tiles[0].tileComps[comp].style & ~1) | (style & 1); + img.tiles[0].tileComps[comp].codeBlockW += 2; + img.tiles[0].tileComps[comp].codeBlockH += 2; + for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { + if (i != 0) { + img.tiles[i].tileComps[comp].style = + img.tiles[0].tileComps[comp].style; + img.tiles[i].tileComps[comp].nDecompLevels = + img.tiles[0].tileComps[comp].nDecompLevels; + img.tiles[i].tileComps[comp].codeBlockW = + img.tiles[0].tileComps[comp].codeBlockW; + img.tiles[i].tileComps[comp].codeBlockH = + img.tiles[0].tileComps[comp].codeBlockH; + img.tiles[i].tileComps[comp].codeBlockStyle = + img.tiles[0].tileComps[comp].codeBlockStyle; + img.tiles[i].tileComps[comp].transform = + img.tiles[0].tileComps[comp].transform; + } + img.tiles[i].tileComps[comp].resLevels = + (JPXResLevel *)grealloc( + img.tiles[i].tileComps[comp].resLevels, + (img.tiles[i].tileComps[comp].nDecompLevels + 1) * + sizeof(JPXResLevel)); + for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { + img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL; + } + } + for (r = 0; r <= img.tiles[0].tileComps[comp].nDecompLevels; ++r) { + if (img.tiles[0].tileComps[comp].style & 0x01) { + if (!readUByte(&precinctSize)) { + error(getPos(), "Error in JPX COD marker segment"); + return gFalse; + } + img.tiles[0].tileComps[comp].resLevels[r].precinctWidth = + precinctSize & 0x0f; + img.tiles[0].tileComps[comp].resLevels[r].precinctHeight = + (precinctSize >> 4) & 0x0f; + } else { + img.tiles[0].tileComps[comp].resLevels[r].precinctWidth = 15; + img.tiles[0].tileComps[comp].resLevels[r].precinctHeight = 15; + } + } + for (i = 1; i < img.nXTiles * img.nYTiles; ++i) { + for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { + img.tiles[i].tileComps[comp].resLevels[r].precinctWidth = + img.tiles[0].tileComps[comp].resLevels[r].precinctWidth; + img.tiles[i].tileComps[comp].resLevels[r].precinctHeight = + img.tiles[0].tileComps[comp].resLevels[r].precinctHeight; + } + } + break; + case 0x5c: // QCD - quantization default + if (!readUByte(&img.tiles[0].tileComps[0].quantStyle)) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x00) { + img.tiles[0].tileComps[0].nQuantSteps = segLen - 3; + img.tiles[0].tileComps[0].quantSteps = + (Guint *)grealloc(img.tiles[0].tileComps[0].quantSteps, + img.tiles[0].tileComps[0].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) { + if (!readUByte(&img.tiles[0].tileComps[0].quantSteps[i])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } + } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x01) { + img.tiles[0].tileComps[0].nQuantSteps = 1; + img.tiles[0].tileComps[0].quantSteps = + (Guint *)grealloc(img.tiles[0].tileComps[0].quantSteps, + img.tiles[0].tileComps[0].nQuantSteps * + sizeof(Guint)); + if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[0])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x02) { + img.tiles[0].tileComps[0].nQuantSteps = (segLen - 3) / 2; + img.tiles[0].tileComps[0].quantSteps = + (Guint *)grealloc(img.tiles[0].tileComps[0].quantSteps, + img.tiles[0].tileComps[0].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) { + if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[i])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } + } else { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { + for (comp = 0; comp < img.nComps; ++comp) { + if (!(i == 0 && comp == 0)) { + img.tiles[i].tileComps[comp].quantStyle = + img.tiles[0].tileComps[0].quantStyle; + img.tiles[i].tileComps[comp].nQuantSteps = + img.tiles[0].tileComps[0].nQuantSteps; + img.tiles[i].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[i].tileComps[comp].quantSteps, + img.tiles[0].tileComps[0].nQuantSteps * + sizeof(Guint)); + for (j = 0; j < img.tiles[0].tileComps[0].nQuantSteps; ++j) { + img.tiles[i].tileComps[comp].quantSteps[j] = + img.tiles[0].tileComps[0].quantSteps[j]; + } + } + } + } + haveQCD = gTrue; + break; + case 0x5d: // QCC - quantization component + if (!haveQCD) { + error(getPos(), "JPX QCC marker segment before QCD segment"); + return gFalse; + } + if ((img.nComps > 256 && !readUWord(&comp)) || + (img.nComps <= 256 && !readUByte(&comp)) || + comp >= img.nComps || + !readUByte(&img.tiles[0].tileComps[comp].quantStyle)) { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x00) { + img.tiles[0].tileComps[comp].nQuantSteps = + segLen - (img.nComps > 256 ? 5 : 4); + img.tiles[0].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[0].tileComps[comp].quantSteps, + img.tiles[0].tileComps[comp].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) { + if (!readUByte(&img.tiles[0].tileComps[comp].quantSteps[i])) { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + } + } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x01) { + img.tiles[0].tileComps[comp].nQuantSteps = 1; + img.tiles[0].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[0].tileComps[comp].quantSteps, + img.tiles[0].tileComps[comp].nQuantSteps * + sizeof(Guint)); + if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[0])) { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x02) { + img.tiles[0].tileComps[comp].nQuantSteps = + (segLen - (img.nComps > 256 ? 5 : 4)) / 2; + img.tiles[0].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[0].tileComps[comp].quantSteps, + img.tiles[0].tileComps[comp].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) { + if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[i])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } + } else { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + for (i = 1; i < img.nXTiles * img.nYTiles; ++i) { + img.tiles[i].tileComps[comp].quantStyle = + img.tiles[0].tileComps[comp].quantStyle; + img.tiles[i].tileComps[comp].nQuantSteps = + img.tiles[0].tileComps[comp].nQuantSteps; + img.tiles[i].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[i].tileComps[comp].quantSteps, + img.tiles[0].tileComps[comp].nQuantSteps * + sizeof(Guint)); + for (j = 0; j < img.tiles[0].tileComps[comp].nQuantSteps; ++j) { + img.tiles[i].tileComps[comp].quantSteps[j] = + img.tiles[0].tileComps[comp].quantSteps[j]; + } + } + break; + case 0x5e: // RGN - region of interest +#if 1 //~ ROI is unimplemented + fprintf(stderr, "RGN\n"); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PPM marker segment"); + return gFalse; + } + } +#else + if ((img.nComps > 256 && !readUWord(&comp)) || + (img.nComps <= 256 && !readUByte(&comp)) || + comp >= img.nComps || + !readUByte(&compInfo[comp].defROI.style) || + !readUByte(&compInfo[comp].defROI.shift)) { + error(getPos(), "Error in JPX RGN marker segment"); + return gFalse; + } +#endif + break; + case 0x5f: // POC - progression order change +#if 1 //~ progression order changes are unimplemented + fprintf(stderr, "POC\n"); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PPM marker segment"); + return gFalse; + } + } +#else + nProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7); + progs = (JPXProgOrder *)gmalloc(nProgs * sizeof(JPXProgOrder)); + for (i = 0; i < nProgs; ++i) { + if (!readUByte(&progs[i].startRes) || + !(img.nComps > 256 && readUWord(&progs[i].startComp)) || + !(img.nComps <= 256 && readUByte(&progs[i].startComp)) || + !readUWord(&progs[i].endLayer) || + !readUByte(&progs[i].endRes) || + !(img.nComps > 256 && readUWord(&progs[i].endComp)) || + !(img.nComps <= 256 && readUByte(&progs[i].endComp)) || + !readUByte(&progs[i].progOrder)) { + error(getPos(), "Error in JPX POC marker segment"); + return gFalse; + } + } +#endif + break; + case 0x60: // PPM - packed packet headers, main header +#if 1 //~ packed packet headers are unimplemented + fprintf(stderr, "PPM\n"); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PPM marker segment"); + return gFalse; + } + } +#endif + break; + case 0x55: // TLM - tile-part lengths + // skipped + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX TLM marker segment"); + return gFalse; + } + } + break; + case 0x57: // PLM - packet length, main header + // skipped + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PLM marker segment"); + return gFalse; + } + } + break; + case 0x63: // CRG - component registration + // skipped + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX CRG marker segment"); + return gFalse; + } + } + break; + case 0x64: // COM - comment + // skipped + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX COM marker segment"); + return gFalse; + } + } + break; + case 0x90: // SOT - start of tile + haveSOT = gTrue; + break; + default: + error(getPos(), "Unknown marker segment %02x in JPX stream", segType); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + break; + } + } + break; + } + } while (!haveSOT); + + if (!haveSIZ) { + error(getPos(), "Missing SIZ marker segment in JPX stream"); + return gFalse; + } + if (!haveCOD) { + error(getPos(), "Missing COD marker segment in JPX stream"); + return gFalse; + } + if (!haveQCD) { + error(getPos(), "Missing QCD marker segment in JPX stream"); + return gFalse; + } + + //----- read the tile-parts + while (1) { + if (!readTilePart()) { + return gFalse; + } + if (!readMarkerHdr(&segType, &segLen)) { + error(getPos(), "Error in JPX codestream"); + return gFalse; + } + if (segType != 0x90) { // SOT - start of tile + break; + } + } + + if (segType != 0xd9) { // EOC - end of codestream + error(getPos(), "Missing EOC marker in JPX codestream"); + return gFalse; + } + + //----- finish decoding the image + for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { + tile = &img.tiles[i]; + for (comp = 0; comp < img.nComps; ++comp) { + tileComp = &tile->tileComps[comp]; + inverseTransform(tileComp); + } + if (!inverseMultiCompAndDC(tile)) { + return gFalse; + } + } + + //~ can free memory below tileComps here, and also tileComp.buf + + return gTrue; +} + +GBool JPXStream::readTilePart() { + JPXTile *tile; + JPXTileComp *tileComp; + JPXResLevel *resLevel; + JPXPrecinct *precinct; + JPXSubband *subband; + JPXCodeBlock *cb; + GBool haveSOD; + Guint tileIdx, tilePartLen, tilePartIdx, nTileParts; + GBool tilePartToEOC; + Guint precinctSize, style; + Guint n, nSBs, nx, ny, sbx0, sby0, comp, segLen; + Guint i, j, k, cbX, cbY, r, pre, sb, cbi; + int segType, level; + + // process the SOT marker segment + if (!readUWord(&tileIdx) || + !readULong(&tilePartLen) || + !readUByte(&tilePartIdx) || + !readUByte(&nTileParts)) { + error(getPos(), "Error in JPX SOT marker segment"); + return gFalse; + } + + if (tileIdx >= img.nXTiles * img.nYTiles) { + error(getPos(), "Weird tile index in JPX stream"); + return gFalse; + } + + tilePartToEOC = tilePartLen == 0; + tilePartLen -= 12; // subtract size of SOT segment + + haveSOD = gFalse; + do { + if (!readMarkerHdr(&segType, &segLen)) { + error(getPos(), "Error in JPX tile-part codestream"); + return gFalse; + } + tilePartLen -= 2 + segLen; + switch (segType) { + case 0x52: // COD - coding style default + if (!readUByte(&img.tiles[tileIdx].tileComps[0].style) || + !readUByte(&img.tiles[tileIdx].progOrder) || + !readUWord(&img.tiles[tileIdx].nLayers) || + !readUByte(&img.tiles[tileIdx].multiComp) || + !readUByte(&img.tiles[tileIdx].tileComps[0].nDecompLevels) || + !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockW) || + !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockH) || + !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockStyle) || + !readUByte(&img.tiles[tileIdx].tileComps[0].transform)) { + error(getPos(), "Error in JPX COD marker segment"); + return gFalse; + } + img.tiles[tileIdx].tileComps[0].codeBlockW += 2; + img.tiles[tileIdx].tileComps[0].codeBlockH += 2; + for (comp = 0; comp < img.nComps; ++comp) { + if (comp != 0) { + img.tiles[tileIdx].tileComps[comp].style = + img.tiles[tileIdx].tileComps[0].style; + img.tiles[tileIdx].tileComps[comp].nDecompLevels = + img.tiles[tileIdx].tileComps[0].nDecompLevels; + img.tiles[tileIdx].tileComps[comp].codeBlockW = + img.tiles[tileIdx].tileComps[0].codeBlockW; + img.tiles[tileIdx].tileComps[comp].codeBlockH = + img.tiles[tileIdx].tileComps[0].codeBlockH; + img.tiles[tileIdx].tileComps[comp].codeBlockStyle = + img.tiles[tileIdx].tileComps[0].codeBlockStyle; + img.tiles[tileIdx].tileComps[comp].transform = + img.tiles[tileIdx].tileComps[0].transform; + } + img.tiles[tileIdx].tileComps[comp].resLevels = + (JPXResLevel *)grealloc( + img.tiles[tileIdx].tileComps[comp].resLevels, + (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1) * + sizeof(JPXResLevel)); + for (r = 0; + r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; + ++r) { + img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL; + } + } + for (r = 0; r <= img.tiles[tileIdx].tileComps[0].nDecompLevels; ++r) { + if (img.tiles[tileIdx].tileComps[0].style & 0x01) { + if (!readUByte(&precinctSize)) { + error(getPos(), "Error in JPX COD marker segment"); + return gFalse; + } + img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth = + precinctSize & 0x0f; + img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight = + (precinctSize >> 4) & 0x0f; + } else { + img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth = 15; + img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight = 15; + } + } + for (comp = 1; comp < img.nComps; ++comp) { + for (r = 0; + r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; + ++r) { + img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = + img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth; + img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = + img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight; + } + } + break; + case 0x53: // COC - coding style component + if ((img.nComps > 256 && !readUWord(&comp)) || + (img.nComps <= 256 && !readUByte(&comp)) || + comp >= img.nComps || + !readUByte(&style) || + !readUByte(&img.tiles[tileIdx].tileComps[comp].nDecompLevels) || + !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockW) || + !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockH) || + !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockStyle) || + !readUByte(&img.tiles[tileIdx].tileComps[comp].transform)) { + error(getPos(), "Error in JPX COC marker segment"); + return gFalse; + } + img.tiles[tileIdx].tileComps[comp].style = + (img.tiles[tileIdx].tileComps[comp].style & ~1) | (style & 1); + img.tiles[tileIdx].tileComps[comp].codeBlockW += 2; + img.tiles[tileIdx].tileComps[comp].codeBlockH += 2; + img.tiles[tileIdx].tileComps[comp].resLevels = + (JPXResLevel *)grealloc( + img.tiles[tileIdx].tileComps[comp].resLevels, + (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1) * + sizeof(JPXResLevel)); + for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) { + img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL; + } + for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) { + if (img.tiles[tileIdx].tileComps[comp].style & 0x01) { + if (!readUByte(&precinctSize)) { + error(getPos(), "Error in JPX COD marker segment"); + return gFalse; + } + img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = + precinctSize & 0x0f; + img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = + (precinctSize >> 4) & 0x0f; + } else { + img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = 15; + img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = 15; + } + } + break; + case 0x5c: // QCD - quantization default + if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantStyle)) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x00) { + img.tiles[tileIdx].tileComps[0].nQuantSteps = + segLen - 3; + img.tiles[tileIdx].tileComps[0].quantSteps = + (Guint *)grealloc(img.tiles[tileIdx].tileComps[0].quantSteps, + img.tiles[tileIdx].tileComps[0].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) { + if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } + } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x01) { + img.tiles[tileIdx].tileComps[0].nQuantSteps = 1; + img.tiles[tileIdx].tileComps[0].quantSteps = + (Guint *)grealloc(img.tiles[tileIdx].tileComps[0].quantSteps, + img.tiles[tileIdx].tileComps[0].nQuantSteps * + sizeof(Guint)); + if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[0])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x02) { + img.tiles[tileIdx].tileComps[0].nQuantSteps = (segLen - 3) / 2; + img.tiles[tileIdx].tileComps[0].quantSteps = + (Guint *)grealloc(img.tiles[tileIdx].tileComps[0].quantSteps, + img.tiles[tileIdx].tileComps[0].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) { + if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } + } else { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + for (comp = 1; comp < img.nComps; ++comp) { + img.tiles[tileIdx].tileComps[comp].quantStyle = + img.tiles[tileIdx].tileComps[0].quantStyle; + img.tiles[tileIdx].tileComps[comp].nQuantSteps = + img.tiles[tileIdx].tileComps[0].nQuantSteps; + img.tiles[tileIdx].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps, + img.tiles[tileIdx].tileComps[0].nQuantSteps * + sizeof(Guint)); + for (j = 0; j < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++j) { + img.tiles[tileIdx].tileComps[comp].quantSteps[j] = + img.tiles[tileIdx].tileComps[0].quantSteps[j]; + } + } + break; + case 0x5d: // QCC - quantization component + if ((img.nComps > 256 && !readUWord(&comp)) || + (img.nComps <= 256 && !readUByte(&comp)) || + comp >= img.nComps || + !readUByte(&img.tiles[tileIdx].tileComps[comp].quantStyle)) { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) == 0x00) { + img.tiles[tileIdx].tileComps[comp].nQuantSteps = + segLen - (img.nComps > 256 ? 5 : 4); + img.tiles[tileIdx].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps, + img.tiles[tileIdx].tileComps[comp].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) { + if (!readUByte(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + } + } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) + == 0x01) { + img.tiles[tileIdx].tileComps[comp].nQuantSteps = 1; + img.tiles[tileIdx].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps, + img.tiles[tileIdx].tileComps[comp].nQuantSteps * + sizeof(Guint)); + if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[0])) { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) + == 0x02) { + img.tiles[tileIdx].tileComps[comp].nQuantSteps = + (segLen - (img.nComps > 256 ? 5 : 4)) / 2; + img.tiles[tileIdx].tileComps[comp].quantSteps = + (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps, + img.tiles[tileIdx].tileComps[comp].nQuantSteps * + sizeof(Guint)); + for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) { + if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) { + error(getPos(), "Error in JPX QCD marker segment"); + return gFalse; + } + } + } else { + error(getPos(), "Error in JPX QCC marker segment"); + return gFalse; + } + break; + case 0x5e: // RGN - region of interest +#if 1 //~ ROI is unimplemented + fprintf(stderr, "RGN\n"); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PPM marker segment"); + return gFalse; + } + } +#else + if ((img.nComps > 256 && !readUWord(&comp)) || + (img.nComps <= 256 && !readUByte(&comp)) || + comp >= img.nComps || + !readUByte(&compInfo[comp].roi.style) || + !readUByte(&compInfo[comp].roi.shift)) { + error(getPos(), "Error in JPX RGN marker segment"); + return gFalse; + } +#endif + break; + case 0x5f: // POC - progression order change +#if 1 //~ progression order changes are unimplemented + fprintf(stderr, "POC\n"); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PPM marker segment"); + return gFalse; + } + } +#else + nTileProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7); + tileProgs = (JPXProgOrder *)gmalloc(nTileProgs * sizeof(JPXProgOrder)); + for (i = 0; i < nTileProgs; ++i) { + if (!readUByte(&tileProgs[i].startRes) || + !(img.nComps > 256 && readUWord(&tileProgs[i].startComp)) || + !(img.nComps <= 256 && readUByte(&tileProgs[i].startComp)) || + !readUWord(&tileProgs[i].endLayer) || + !readUByte(&tileProgs[i].endRes) || + !(img.nComps > 256 && readUWord(&tileProgs[i].endComp)) || + !(img.nComps <= 256 && readUByte(&tileProgs[i].endComp)) || + !readUByte(&tileProgs[i].progOrder)) { + error(getPos(), "Error in JPX POC marker segment"); + return gFalse; + } + } +#endif + break; + case 0x61: // PPT - packed packet headers, tile-part hdr +#if 1 //~ packed packet headers are unimplemented + fprintf(stderr, "PPT\n"); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PPT marker segment"); + return gFalse; + } + } +#endif + case 0x58: // PLT - packet length, tile-part header + // skipped + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX PLT marker segment"); + return gFalse; + } + } + break; + case 0x64: // COM - comment + // skipped + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + error(getPos(), "Error in JPX COM marker segment"); + return gFalse; + } + } + break; + case 0x93: // SOD - start of data + haveSOD = gTrue; + break; + default: + error(getPos(), "Unknown marker segment %02x in JPX tile-part stream", + segType); + for (i = 0; i < segLen - 2; ++i) { + if (str->getChar() == EOF) { + break; + } + } + break; + } + } while (!haveSOD); + + //----- initialize the tile, precincts, and code-blocks + if (tilePartIdx == 0) { + tile = &img.tiles[tileIdx]; + i = tileIdx / img.nXTiles; + j = tileIdx % img.nXTiles; + if ((tile->x0 = img.xTileOffset + j * img.xTileSize) < img.xOffset) { + tile->x0 = img.xOffset; + } + if ((tile->y0 = img.yTileOffset + i * img.yTileSize) < img.yOffset) { + tile->y0 = img.yOffset; + } + if ((tile->x1 = img.xTileOffset + (j + 1) * img.xTileSize) > img.xSize) { + tile->x1 = img.xSize; + } + if ((tile->y1 = img.yTileOffset + (i + 1) * img.yTileSize) > img.ySize) { + tile->y1 = img.ySize; + } + tile->comp = 0; + tile->res = 0; + tile->precinct = 0; + tile->layer = 0; + tile->maxNDecompLevels = 0; + for (comp = 0; comp < img.nComps; ++comp) { + tileComp = &tile->tileComps[comp]; + if (tileComp->nDecompLevels > tile->maxNDecompLevels) { + tile->maxNDecompLevels = tileComp->nDecompLevels; + } + tileComp->x0 = jpxCeilDiv(tile->x0, tileComp->hSep); + tileComp->y0 = jpxCeilDiv(tile->y0, tileComp->hSep); + tileComp->x1 = jpxCeilDiv(tile->x1, tileComp->hSep); + tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->hSep); + tileComp->cbW = 1 << tileComp->codeBlockW; + tileComp->cbH = 1 << tileComp->codeBlockH; + tileComp->data = (int *)gmalloc((tileComp->x1 - tileComp->x0) * + (tileComp->y1 - tileComp->y0) * + sizeof(int)); + if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) { + n = tileComp->x1 - tileComp->x0; + } else { + n = tileComp->y1 - tileComp->y0; + } + tileComp->buf = (int *)gmalloc((n + 8) * sizeof(int)); + for (r = 0; r <= tileComp->nDecompLevels; ++r) { + resLevel = &tileComp->resLevels[r]; + k = r == 0 ? tileComp->nDecompLevels + : tileComp->nDecompLevels - r + 1; + resLevel->x0 = jpxCeilDivPow2(tileComp->x0, k); + resLevel->y0 = jpxCeilDivPow2(tileComp->y0, k); + resLevel->x1 = jpxCeilDivPow2(tileComp->x1, k); + resLevel->y1 = jpxCeilDivPow2(tileComp->y1, k); + if (r == 0) { + resLevel->bx0[0] = resLevel->x0; + resLevel->by0[0] = resLevel->y0; + resLevel->bx1[0] = resLevel->x1; + resLevel->by1[0] = resLevel->y1; + } else { + resLevel->bx0[0] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k); + resLevel->by0[0] = resLevel->y0; + resLevel->bx1[0] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k); + resLevel->by1[0] = resLevel->y1; + resLevel->bx0[1] = resLevel->x0; + resLevel->by0[1] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k); + resLevel->bx1[1] = resLevel->x1; + resLevel->by1[1] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k); + resLevel->bx0[2] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k); + resLevel->by0[2] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k); + resLevel->bx1[2] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k); + resLevel->by1[2] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k); + } + resLevel->precincts = (JPXPrecinct *)gmalloc(1 * sizeof(JPXPrecinct)); + for (pre = 0; pre < 1; ++pre) { + precinct = &resLevel->precincts[pre]; + precinct->x0 = resLevel->x0; + precinct->y0 = resLevel->y0; + precinct->x1 = resLevel->x1; + precinct->y1 = resLevel->y1; + nSBs = r == 0 ? 1 : 3; + precinct->subbands = + (JPXSubband *)gmalloc(nSBs * sizeof(JPXSubband)); + for (sb = 0; sb < nSBs; ++sb) { + subband = &precinct->subbands[sb]; + subband->x0 = resLevel->bx0[sb]; + subband->y0 = resLevel->by0[sb]; + subband->x1 = resLevel->bx1[sb]; + subband->y1 = resLevel->by1[sb]; + subband->nXCBs = jpxCeilDivPow2(subband->x1, + tileComp->codeBlockW) + - jpxFloorDivPow2(subband->x0, + tileComp->codeBlockW); + subband->nYCBs = jpxCeilDivPow2(subband->y1, + tileComp->codeBlockH) + - jpxFloorDivPow2(subband->y0, + tileComp->codeBlockH); + n = subband->nXCBs > subband->nYCBs ? subband->nXCBs + : subband->nYCBs; + for (subband->maxTTLevel = 0, --n; + n; + ++subband->maxTTLevel, n >>= 1) ; + n = 0; + for (level = subband->maxTTLevel; level >= 0; --level) { + nx = jpxCeilDivPow2(subband->nXCBs, level); + ny = jpxCeilDivPow2(subband->nYCBs, level); + n += nx * ny; + } + subband->inclusion = + (JPXTagTreeNode *)gmalloc(n * sizeof(JPXTagTreeNode)); + subband->zeroBitPlane = + (JPXTagTreeNode *)gmalloc(n * sizeof(JPXTagTreeNode)); + for (k = 0; k < n; ++k) { + subband->inclusion[k].finished = gFalse; + subband->inclusion[k].val = 0; + subband->zeroBitPlane[k].finished = gFalse; + subband->zeroBitPlane[k].val = 0; + } + subband->cbs = (JPXCodeBlock *)gmalloc(subband->nXCBs * + subband->nYCBs * + sizeof(JPXCodeBlock)); + sbx0 = jpxFloorDivPow2(subband->x0, tileComp->codeBlockW); + sby0 = jpxFloorDivPow2(subband->y0, tileComp->codeBlockH); + cb = subband->cbs; + for (cbY = 0; cbY < subband->nYCBs; ++cbY) { + for (cbX = 0; cbX < subband->nXCBs; ++cbX) { + cb->x0 = (sbx0 + cbX) << tileComp->codeBlockW; + cb->x1 = cb->x0 + tileComp->cbW; + if (subband->x0 > cb->x0) { + cb->x0 = subband->x0; + } + if (subband->x1 < cb->x1) { + cb->x1 = subband->x1; + } + cb->y0 = (sby0 + cbY) << tileComp->codeBlockH; + cb->y1 = cb->y0 + tileComp->cbH; + if (subband->y0 > cb->y0) { + cb->y0 = subband->y0; + } + if (subband->y1 < cb->y1) { + cb->y1 = subband->y1; + } + cb->seen = gFalse; + cb->lBlock = 3; + cb->nextPass = jpxPassCleanup; + cb->nZeroBitPlanes = 0; + cb->coeffs = + (JPXCoeff *)gmalloc((1 << (tileComp->codeBlockW + + tileComp->codeBlockH)) + * sizeof(JPXCoeff)); + for (cbi = 0; + cbi < (Guint)(1 << (tileComp->codeBlockW + + tileComp->codeBlockH)); + ++cbi) { + cb->coeffs[cbi].flags = 0; + cb->coeffs[cbi].len = 0; + cb->coeffs[cbi].mag = 0; + } + cb->stats = new JArithmeticDecoderStats(jpxNContexts); + cb->stats->setEntry(jpxContextSigProp, 4, 0); + cb->stats->setEntry(jpxContextRunLength, 3, 0); + cb->stats->setEntry(jpxContextUniform, 46, 0); + ++cb; + } + } + } + } + } + } + } + + return readTilePartData(tileIdx, tilePartLen, tilePartToEOC); +} + +GBool JPXStream::readTilePartData(Guint tileIdx, + Guint tilePartLen, GBool tilePartToEOC) { + JPXTile *tile; + JPXTileComp *tileComp; + JPXResLevel *resLevel; + JPXPrecinct *precinct; + JPXSubband *subband; + JPXCodeBlock *cb; + Guint ttVal; + Guint bits, cbX, cbY, nx, ny, i, j, n, sb; + int level; + + tile = &img.tiles[tileIdx]; + + // read all packets from this tile-part + while (1) { + if (tilePartToEOC) { + //~ peek for an EOC marker + } else if (tilePartLen == 0) { + break; + } + + tileComp = &tile->tileComps[tile->comp]; + resLevel = &tileComp->resLevels[tile->res]; + precinct = &resLevel->precincts[tile->precinct]; + + //----- packet header + + // zero-length flag + if (!readBits(1, &bits)) { + goto err; + } + if (!bits) { + // packet is empty -- clear all code-block inclusion flags + for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) { + subband = &precinct->subbands[sb]; + for (cbY = 0; cbY < subband->nYCBs; ++cbY) { + for (cbX = 0; cbX < subband->nXCBs; ++cbX) { + cb = &subband->cbs[cbY * subband->nXCBs + cbX]; + cb->included = gFalse; + } + } + } + } else { + + for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) { + subband = &precinct->subbands[sb]; + for (cbY = 0; cbY < subband->nYCBs; ++cbY) { + for (cbX = 0; cbX < subband->nXCBs; ++cbX) { + cb = &subband->cbs[cbY * subband->nXCBs + cbX]; + + // skip code-blocks with no coefficients + if (cb->x0 >= cb->x1 || cb->y0 >= cb->y1) { + cb->included = gFalse; + continue; + } + + // code-block inclusion + if (cb->seen) { + if (!readBits(1, &cb->included)) { + goto err; + } + } else { + ttVal = 0; + i = 0; + for (level = subband->maxTTLevel; level >= 0; --level) { + nx = jpxCeilDivPow2(subband->nXCBs, level); + ny = jpxCeilDivPow2(subband->nYCBs, level); + j = i + (cbY >> level) * nx + (cbX >> level); + if (!subband->inclusion[j].finished && + !subband->inclusion[j].val) { + subband->inclusion[j].val = ttVal; + } else { + ttVal = subband->inclusion[j].val; + } + while (!subband->inclusion[j].finished && + ttVal <= tile->layer) { + if (!readBits(1, &bits)) { + goto err; + } + if (bits == 1) { + subband->inclusion[j].finished = gTrue; + } else { + ++ttVal; + } + } + subband->inclusion[j].val = ttVal; + if (ttVal > tile->layer) { + break; + } + i += nx * ny; + } + cb->included = level < 0; + } + + if (cb->included) { + + // zero bit-plane count + if (!cb->seen) { + ttVal = 0; + i = 0; + for (level = subband->maxTTLevel; level >= 0; --level) { + nx = jpxCeilDivPow2(subband->nXCBs, level); + ny = jpxCeilDivPow2(subband->nYCBs, level); + j = i + (cbY >> level) * nx + (cbX >> level); + if (!subband->zeroBitPlane[j].finished && + !subband->zeroBitPlane[j].val) { + subband->zeroBitPlane[j].val = ttVal; + } else { + ttVal = subband->zeroBitPlane[j].val; + } + while (!subband->zeroBitPlane[j].finished) { + if (!readBits(1, &bits)) { + goto err; + } + if (bits == 1) { + subband->zeroBitPlane[j].finished = gTrue; + } else { + ++ttVal; + } + } + subband->zeroBitPlane[j].val = ttVal; + i += nx * ny; + } + cb->nZeroBitPlanes = ttVal; + } + + // number of coding passes + if (!readBits(1, &bits)) { + goto err; + } + if (bits == 0) { + cb->nCodingPasses = 1; + } else { + if (!readBits(1, &bits)) { + goto err; + } + if (bits == 0) { + cb->nCodingPasses = 2; + } else { + if (!readBits(2, &bits)) { + goto err; + } + if (bits < 3) { + cb->nCodingPasses = 3 + bits; + } else { + if (!readBits(5, &bits)) { + goto err; + } + if (bits < 31) { + cb->nCodingPasses = 6 + bits; + } else { + if (!readBits(7, &bits)) { + goto err; + } + cb->nCodingPasses = 37 + bits; + } + } + } + } + + // update Lblock + while (1) { + if (!readBits(1, &bits)) { + goto err; + } + if (!bits) { + break; + } + ++cb->lBlock; + } + + // length of compressed data + //~ deal with multiple codeword segments + for (n = cb->lBlock, i = cb->nCodingPasses >> 1; + i; + ++n, i >>= 1) ; + if (!readBits(n, &cb->dataLen)) { + goto err; + } + } + } + } + } + } + tilePartLen -= byteCount; + clearBitBuf(); + + //----- packet data + + for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) { + subband = &precinct->subbands[sb]; + for (cbY = 0; cbY < subband->nYCBs; ++cbY) { + for (cbX = 0; cbX < subband->nXCBs; ++cbX) { + cb = &subband->cbs[cbY * subband->nXCBs + cbX]; + if (cb->included) { + if (!readCodeBlockData(tileComp, resLevel, precinct, subband, + tile->res, sb, cb)) { + return gFalse; + } + tilePartLen -= cb->dataLen; + cb->seen = gTrue; + } + } + } + } + + //----- next packet + + switch (tile->progOrder) { + case 0: // layer, resolution level, component, precinct + if (++tile->comp == img.nComps) { + tile->comp = 0; + if (++tile->res == tile->maxNDecompLevels + 1) { + tile->res = 0; + if (++tile->layer == tile->nLayers) { + tile->layer = 0; + } + } + } + break; + case 1: // resolution level, layer, component, precinct + if (++tile->comp == img.nComps) { + tile->comp = 0; + if (++tile->layer == tile->nLayers) { + tile->layer = 0; + if (++tile->res == tile->maxNDecompLevels + 1) { + tile->res = 0; + } + } + } + break; + case 2: // resolution level, precinct, component, layer + //~ this isn't correct -- see B.12.1.3 + if (++tile->layer == tile->nLayers) { + tile->layer = 0; + if (++tile->comp == img.nComps) { + tile->comp = 0; + if (++tile->res == tile->maxNDecompLevels + 1) { + tile->res = 0; + } + } + } + break; + case 3: // precinct, component, resolution level, layer + //~ this isn't correct -- see B.12.1.4 + if (++tile->layer == tile->nLayers) { + tile->layer = 0; + if (++tile->res == tile->maxNDecompLevels + 1) { + tile->res = 0; + if (++tile->comp == img.nComps) { + tile->comp = 0; + } + } + } + break; + case 4: // component, precinct, resolution level, layer + //~ this isn't correct -- see B.12.1.5 + if (++tile->layer == tile->nLayers) { + tile->layer = 0; + if (++tile->res == tile->maxNDecompLevels + 1) { + tile->res = 0; + if (++tile->comp == img.nComps) { + tile->comp = 0; + } + } + } + break; + } + } + + return gTrue; + + err: + error(getPos(), "Error in JPX stream"); + return gFalse; +} + +GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp, + JPXResLevel *resLevel, + JPXPrecinct *precinct, + JPXSubband *subband, + Guint res, Guint sb, + JPXCodeBlock *cb) { + JPXCoeff *coeff0, *coeff1, *coeff; + JArithmeticDecoder *arithDecoder; + Guint horiz, vert, diag, all, cx, xorBit; + int horizSign, vertSign; + Guint i, x, y0, y1, y2; + + arithDecoder = new JArithmeticDecoder(); + arithDecoder->setStream(str, cb->dataLen); + arithDecoder->start(); + + for (i = 0; i < cb->nCodingPasses; ++i) { + switch (cb->nextPass) { + + //----- significance propagation pass + case jpxPassSigProp: + for (y0 = cb->y0, coeff0 = cb->coeffs; + y0 < cb->y1; + y0 += 4, coeff0 += 4 << tileComp->codeBlockW) { + for (x = cb->x0, coeff1 = coeff0; + x < cb->x1; + ++x, ++coeff1) { + for (y1 = 0, coeff = coeff1; + y1 < 4 && y0+y1 < cb->y1; + ++y1, coeff += tileComp->cbW) { + if (!(coeff->flags & jpxCoeffSignificant)) { + horiz = vert = diag = 0; + horizSign = vertSign = 2; + if (x > cb->x0) { + if (coeff[-1].flags & jpxCoeffSignificant) { + ++horiz; + horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1; + } + if (y0+y1 > cb->y0) { + diag += (coeff[-tileComp->cbW - 1].flags + >> jpxCoeffSignificantB) & 1; + } + if (y0+y1 < cb->y1 - 1) { + diag += (coeff[tileComp->cbW - 1].flags + >> jpxCoeffSignificantB) & 1; + } + } + if (x < cb->x1 - 1) { + if (coeff[1].flags & jpxCoeffSignificant) { + ++horiz; + horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1; + } + if (y0+y1 > cb->y0) { + diag += (coeff[-tileComp->cbW + 1].flags + >> jpxCoeffSignificantB) & 1; + } + if (y0+y1 < cb->y1 - 1) { + diag += (coeff[tileComp->cbW + 1].flags + >> jpxCoeffSignificantB) & 1; + } + } + if (y0+y1 > cb->y0) { + if (coeff[-tileComp->cbW].flags & jpxCoeffSignificant) { + ++vert; + vertSign += (coeff[-tileComp->cbW].flags & jpxCoeffSign) + ? -1 : 1; + } + } + if (y0+y1 < cb->y1 - 1) { + if (coeff[tileComp->cbW].flags & jpxCoeffSignificant) { + ++vert; + vertSign += (coeff[tileComp->cbW].flags & jpxCoeffSign) + ? -1 : 1; + } + } + cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb]; + if (cx != 0) { + if (arithDecoder->decodeBit(cx, cb->stats)) { + coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef; + coeff->mag = (coeff->mag << 1) | 1; + cx = signContext[horizSign][vertSign][0]; + xorBit = signContext[horizSign][vertSign][1]; + if (arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) { + coeff->flags |= jpxCoeffSign; + } + } + ++coeff->len; + coeff->flags |= jpxCoeffTouched; + } + } + } + } + } + ++cb->nextPass; + break; + + //----- magnitude refinement pass + case jpxPassMagRef: + for (y0 = cb->y0, coeff0 = cb->coeffs; + y0 < cb->y1; + y0 += 4, coeff0 += 4 << tileComp->codeBlockW) { + for (x = cb->x0, coeff1 = coeff0; + x < cb->x1; + ++x, ++coeff1) { + for (y1 = 0, coeff = coeff1; + y1 < 4 && y0+y1 < cb->y1; + ++y1, coeff += tileComp->cbW) { + if ((coeff->flags & jpxCoeffSignificant) && + !(coeff->flags & jpxCoeffTouched)) { + if (coeff->flags & jpxCoeffFirstMagRef) { + all = 0; + if (x > cb->x0) { + all += (coeff[-1].flags >> jpxCoeffSignificantB) & 1; + if (y0+y1 > cb->y0) { + all += (coeff[-tileComp->cbW - 1].flags + >> jpxCoeffSignificantB) & 1; + } + if (y0+y1 < cb->y1 - 1) { + all += (coeff[tileComp->cbW - 1].flags + >> jpxCoeffSignificantB) & 1; + } + } + if (x < cb->x1 - 1) { + all += (coeff[1].flags >> jpxCoeffSignificantB) & 1; + if (y0+y1 > cb->y0) { + all += (coeff[-tileComp->cbW + 1].flags + >> jpxCoeffSignificantB) & 1; + } + if (y0+y1 < cb->y1 - 1) { + all += (coeff[tileComp->cbW + 1].flags + >> jpxCoeffSignificantB) & 1; + } + } + if (y0+y1 > cb->y0) { + all += (coeff[-tileComp->cbW].flags + >> jpxCoeffSignificantB) & 1; + } + if (y0+y1 < cb->y1 - 1) { + all += (coeff[tileComp->cbW].flags + >> jpxCoeffSignificantB) & 1; + } + cx = all ? 15 : 14; + } else { + cx = 16; + } + coeff->mag = (coeff->mag << 1) | + arithDecoder->decodeBit(cx, cb->stats); + ++coeff->len; + coeff->flags |= jpxCoeffTouched; + coeff->flags &= ~jpxCoeffFirstMagRef; + } + } + } + } + ++cb->nextPass; + break; + + //----- cleanup pass + case jpxPassCleanup: + for (y0 = cb->y0, coeff0 = cb->coeffs; + y0 < cb->y1; + y0 += 4, coeff0 += 4 << tileComp->codeBlockW) { + for (x = cb->x0, coeff1 = coeff0; + x < cb->x1; + ++x, ++coeff1) { + y1 = 0; + if (y0 + 3 < cb->y1 && + !(coeff1->flags & jpxCoeffTouched) && + !(coeff1[tileComp->cbW].flags & jpxCoeffTouched) && + !(coeff1[2 * tileComp->cbW].flags & jpxCoeffTouched) && + !(coeff1[3 * tileComp->cbW].flags & jpxCoeffTouched) && + (x == cb->x0 || y0 == cb->y0 || + !(coeff1[-tileComp->cbW - 1].flags + & jpxCoeffSignificant)) && + (y0 == cb->y0 || + !(coeff1[-tileComp->cbW].flags & jpxCoeffSignificant)) && + (x == cb->x1 - 1 || y0 == cb->y0 || + !(coeff1[-tileComp->cbW + 1].flags & jpxCoeffSignificant)) && + (x == cb->x0 || + (!(coeff1[-1].flags & jpxCoeffSignificant) && + !(coeff1[tileComp->cbW - 1].flags + & jpxCoeffSignificant) && + !(coeff1[2 * tileComp->cbW - 1].flags + & jpxCoeffSignificant) && + !(coeff1[3 * tileComp->cbW - 1].flags + & jpxCoeffSignificant))) && + (x == cb->x1 - 1 || + (!(coeff1[1].flags & jpxCoeffSignificant) && + !(coeff1[tileComp->cbW + 1].flags + & jpxCoeffSignificant) && + !(coeff1[2 * tileComp->cbW + 1].flags + & jpxCoeffSignificant) && + !(coeff1[3 * tileComp->cbW + 1].flags + & jpxCoeffSignificant))) && + (x == cb->x0 || y0+4 == cb->y1 || + !(coeff1[4 * tileComp->cbW - 1].flags & jpxCoeffSignificant)) && + (y0+4 == cb->y1 || + !(coeff1[4 * tileComp->cbW].flags & jpxCoeffSignificant)) && + (x == cb->x1 - 1 || y0+4 == cb->y1 || + !(coeff1[4 * tileComp->cbW + 1].flags + & jpxCoeffSignificant))) { + if (arithDecoder->decodeBit(jpxContextRunLength, cb->stats)) { + y1 = arithDecoder->decodeBit(jpxContextUniform, cb->stats); + y1 = (y1 << 1) | + arithDecoder->decodeBit(jpxContextUniform, cb->stats); + for (y2 = 0, coeff = coeff1; + y2 < y1; + ++y2, coeff += tileComp->cbW) { + ++coeff->len; + } + coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef; + coeff->mag = (coeff->mag << 1) | 1; + ++coeff->len; + cx = signContext[2][2][0]; + xorBit = signContext[2][2][1]; + if (arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) { + coeff->flags |= jpxCoeffSign; + } + ++y1; + } else { + for (y1 = 0, coeff = coeff1; + y1 < 4; + ++y1, coeff += tileComp->cbW) { + ++coeff->len; + } + y1 = 4; + } + } + for (coeff = &coeff1[y1 << tileComp->codeBlockW]; + y1 < 4 && y0 + y1 < cb->y1; + ++y1, coeff += tileComp->cbW) { + if (!(coeff->flags & jpxCoeffTouched)) { + horiz = vert = diag = 0; + horizSign = vertSign = 2; + if (x > cb->x0) { + if (coeff[-1].flags & jpxCoeffSignificant) { + ++horiz; + horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1; + } + if (y0+y1 > cb->y0) { + diag += (coeff[-tileComp->cbW - 1].flags + >> jpxCoeffSignificantB) & 1; + } + if (y0+y1 < cb->y1 - 1) { + diag += (coeff[tileComp->cbW - 1].flags + >> jpxCoeffSignificantB) & 1; + } + } + if (x < cb->x1 - 1) { + if (coeff[1].flags & jpxCoeffSignificant) { + ++horiz; + horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1; + } + if (y0+y1 > cb->y0) { + diag += (coeff[-tileComp->cbW + 1].flags + >> jpxCoeffSignificantB) & 1; + } + if (y0+y1 < cb->y1 - 1) { + diag += (coeff[tileComp->cbW + 1].flags + >> jpxCoeffSignificantB) & 1; + } + } + if (y0+y1 > cb->y0) { + if (coeff[-tileComp->cbW].flags & jpxCoeffSignificant) { + ++vert; + vertSign += (coeff[-tileComp->cbW].flags & jpxCoeffSign) + ? -1 : 1; + } + } + if (y0+y1 < cb->y1 - 1) { + if (coeff[tileComp->cbW].flags & jpxCoeffSignificant) { + ++vert; + vertSign += (coeff[tileComp->cbW].flags & jpxCoeffSign) + ? -1 : 1; + } + } + cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb]; + if (arithDecoder->decodeBit(cx, cb->stats)) { + coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef; + coeff->mag = (coeff->mag << 1) | 1; + cx = signContext[horizSign][vertSign][0]; + xorBit = signContext[horizSign][vertSign][1]; + if (arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) { + coeff->flags |= jpxCoeffSign; + } + } + ++coeff->len; + } else { + coeff->flags &= ~jpxCoeffTouched; + } + } + } + } + cb->nextPass = jpxPassSigProp; + break; + } + } + + delete arithDecoder; + return gTrue; +} + +// Inverse quantization, and wavelet transform (IDWT). This also does +// the initial shift to convert to fixed point format. +void JPXStream::inverseTransform(JPXTileComp *tileComp) { + JPXResLevel *resLevel; + JPXPrecinct *precinct; + JPXSubband *subband; + JPXCodeBlock *cb; + JPXCoeff *coeff0, *coeff; + Guint qStyle, guard, eps, shift, shift2; + double mu; + int val; + int *dataPtr; + Guint nx0, ny0, nx1, ny1; + Guint r, cbX, cbY, x, y; + + //----- (NL)LL subband (resolution level 0) + + resLevel = &tileComp->resLevels[0]; + precinct = &resLevel->precincts[0]; + subband = &precinct->subbands[0]; + + // i-quant parameters + qStyle = tileComp->quantStyle & 0x1f; + guard = (tileComp->quantStyle >> 5) & 7; + if (qStyle == 0) { + eps = (tileComp->quantSteps[0] >> 3) & 0x1f; + shift = guard + eps - 1; + mu = 0; // make gcc happy + } else { + shift = guard - 1 + tileComp->prec; + mu = (double)(0x800 + (tileComp->quantSteps[0] & 0x7ff)) / 2048.0; + } + if (tileComp->transform == 0) { + shift += fracBits; + } + + // copy (NL)LL into the upper-left corner of the data array, doing + // the fixed point adjustment and dequantization along the way + cb = subband->cbs; + for (cbY = 0; cbY < subband->nYCBs; ++cbY) { + for (cbX = 0; cbX < subband->nXCBs; ++cbX) { + for (y = cb->y0, coeff0 = cb->coeffs; + y < cb->y1; + ++y, coeff0 += tileComp->cbW) { + dataPtr = &tileComp->data[(y - subband->y0) + * (tileComp->x1 - tileComp->x0) + + (cb->x0 - subband->x0)]; + for (x = cb->x0, coeff = coeff0; x < cb->x1; ++x, ++coeff) { + val = (int)coeff->mag; + if (val != 0) { + shift2 = shift - (cb->nZeroBitPlanes + coeff->len); + if (shift2 > 0) { + val = (val << shift2) + (1 << (shift2 - 1)); + } else { + val >>= -shift2; + } + if (qStyle == 0) { + if (tileComp->transform == 0) { + val &= -1 << fracBits; + } + } else { + val = (int)((double)val * mu); + } + if (coeff->flags & jpxCoeffSign) { + val = -val; + } + } + *dataPtr++ = val; + } + } + ++cb; + } + } + + //----- IDWT for each level + + for (r = 1; r <= tileComp->nDecompLevels; ++r) { + resLevel = &tileComp->resLevels[r]; + + // (n)LL is already in the upper-left corner of the + // tile-component data array -- interleave with (n)HL/LH/HH + // and inverse transform to get (n-1)LL, which will be stored + // in the upper-left corner of the tile-component data array + if (r == tileComp->nDecompLevels) { + nx0 = tileComp->x0; + ny0 = tileComp->y0; + nx1 = tileComp->x1; + ny1 = tileComp->y1; + } else { + nx0 = tileComp->resLevels[r+1].x0; + ny0 = tileComp->resLevels[r+1].y0; + nx1 = tileComp->resLevels[r+1].x1; + ny1 = tileComp->resLevels[r+1].y1; + } + inverseTransformLevel(tileComp, r, resLevel, nx0, ny0, nx1, ny1); + } +} + +// Do one level of the inverse transform: +// - take (n)LL from the tile-component data array +// - take (n)HL/LH/HH from +// - leave the resulting (n-1)LL in the tile-component data array +void JPXStream::inverseTransformLevel(JPXTileComp *tileComp, + Guint r, JPXResLevel *resLevel, + Guint nx0, Guint ny0, + Guint nx1, Guint ny1) { + JPXPrecinct *precinct; + JPXSubband *subband; + JPXCodeBlock *cb; + JPXCoeff *coeff0, *coeff; + Guint qStyle, guard, eps, shift, shift2, t; + double mu; + int val; + int *dataPtr; + Guint xo, yo; + Guint x, y, sb, cbX, cbY; + int xx, yy; + + //----- interleave + + // spread out LL + for (yy = resLevel->y1 - 1; yy >= (int)resLevel->y0; --yy) { + for (xx = resLevel->x1 - 1; xx >= (int)resLevel->x0; --xx) { + tileComp->data[(2 * yy - ny0) * (tileComp->x1 - tileComp->x0) + + (2 * xx - nx0)] = + tileComp->data[(yy - resLevel->y0) * (tileComp->x1 - tileComp->x0) + + (xx - resLevel->x0)]; + } + } + + // i-quant parameters + qStyle = tileComp->quantStyle & 0x1f; + guard = (tileComp->quantStyle >> 5) & 7; + + // interleave HL/LH/HH + precinct = &resLevel->precincts[0]; + for (sb = 0; sb < 3; ++sb) { + + // i-quant parameters + if (qStyle == 0) { + eps = (tileComp->quantSteps[3*r - 2 + sb] >> 3) & 0x1f; + shift = guard + eps - 1; + mu = 0; // make gcc happy + } else { + shift = guard + tileComp->prec; + if (sb == 2) { + ++shift; + } + t = tileComp->quantSteps[qStyle == 1 ? 0 : (3*r - 2 + sb)]; + mu = (double)(0x800 + (t & 0x7ff)) / 2048.0; + } + if (tileComp->transform == 0) { + shift += fracBits; + } + + // copy the subband coefficients into the data array, doing the + // fixed point adjustment and dequantization along the way + xo = (sb & 1) ? 0 : 1; + yo = (sb > 0) ? 1 : 0; + subband = &precinct->subbands[sb]; + cb = subband->cbs; + for (cbY = 0; cbY < subband->nYCBs; ++cbY) { + for (cbX = 0; cbX < subband->nXCBs; ++cbX) { + for (y = cb->y0, coeff0 = cb->coeffs; + y < cb->y1; + ++y, coeff0 += tileComp->cbW) { + dataPtr = &tileComp->data[(2 * y + yo - ny0) + * (tileComp->x1 - tileComp->x0) + + (2 * cb->x0 + xo - nx0)]; + for (x = cb->x0, coeff = coeff0; x < cb->x1; ++x, ++coeff) { + val = (int)coeff->mag; + if (val != 0) { + shift2 = shift - (cb->nZeroBitPlanes + coeff->len); + if (shift2 > 0) { + val = (val << shift2) + (1 << (shift2 - 1)); + } else { + val >>= -shift2; + } + if (qStyle == 0) { + if (tileComp->transform == 0) { + val &= -1 << fracBits; + } + } else { + val = (int)((double)val * mu); + } + if (coeff->flags & jpxCoeffSign) { + val = -val; + } + } + *dataPtr = val; + dataPtr += 2; + } + } + ++cb; + } + } + } + + //----- horizontal (row) transforms + dataPtr = tileComp->data; + for (y = 0; y < ny1 - ny0; ++y) { + inverseTransform1D(tileComp, dataPtr, 1, nx0, nx1); + dataPtr += tileComp->x1 - tileComp->x0; + } + + //----- vertical (column) transforms + dataPtr = tileComp->data; + for (x = 0; x < nx1 - nx0; ++x) { + inverseTransform1D(tileComp, dataPtr, + tileComp->x1 - tileComp->x0, ny0, ny1); + ++dataPtr; + } +} + +void JPXStream::inverseTransform1D(JPXTileComp *tileComp, + int *data, Guint stride, + Guint i0, Guint i1) { + int *buf; + Guint offset, end, i; + + //----- special case for length = 1 + if (i1 - i0 == 1) { + if (i0 & 1) { + *data >>= 1; + } + + } else { + + // choose an offset: this makes even buf[] indexes correspond to + // odd values of i, and vice versa + offset = 3 + (i0 & 1); + end = offset + i1 - i0; + + //----- gather + buf = tileComp->buf; + for (i = 0; i < i1 - i0; ++i) { + buf[offset + i] = data[i * stride]; + } + + //----- extend right + buf[end] = buf[end - 2]; + if (i1 - i0 == 2) { + buf[end+1] = buf[offset + 1]; + buf[end+2] = buf[offset]; + buf[end+3] = buf[offset + 1]; + } else { + buf[end+1] = buf[end - 3]; + if (i1 - i0 == 3) { + buf[end+2] = buf[offset + 1]; + buf[end+3] = buf[offset + 2]; + } else { + buf[end+2] = buf[end - 4]; + if (i1 - i0 == 4) { + buf[end+3] = buf[offset + 1]; + } else { + buf[end+3] = buf[end - 5]; + } + } + } + + //----- extend left + buf[offset - 1] = buf[offset + 1]; + buf[offset - 2] = buf[offset + 2]; + buf[offset - 3] = buf[offset + 3]; + if (offset == 4) { + buf[0] = buf[offset + 4]; + } + + //----- 9-7 irreversible filter + + if (tileComp->transform == 0) { + // step 1 (even) + for (i = 1; i <= end + 2; i += 2) { + buf[i] = (int)(idwtKappa * buf[i]); + } + // step 2 (odd) + for (i = 0; i <= end + 3; i += 2) { + buf[i] = (int)(idwtIKappa * buf[i]); + } + // step 3 (even) + for (i = 1; i <= end + 2; i += 2) { + buf[i] = (int)(buf[i] - idwtDelta * (buf[i-1] + buf[i+1])); + } + // step 4 (odd) + for (i = 2; i <= end + 1; i += 2) { + buf[i] = (int)(buf[i] - idwtGamma * (buf[i-1] + buf[i+1])); + } + // step 5 (even) + for (i = 3; i <= end; i += 2) { + buf[i] = (int)(buf[i] - idwtBeta * (buf[i-1] + buf[i+1])); + } + // step 6 (odd) + for (i = 4; i <= end - 1; i += 2) { + buf[i] = (int)(buf[i] - idwtAlpha * (buf[i-1] + buf[i+1])); + } + + //----- 5-3 reversible filter + + } else { + // step 1 (even) + for (i = 3; i <= end; i += 2) { + buf[i] -= (buf[i-1] + buf[i+1] + 2) >> 2; + } + // step 2 (odd) + for (i = 4; i < end; i += 2) { + buf[i] += (buf[i-1] + buf[i+1]) >> 1; + } + } + + //----- scatter + for (i = 0; i < i1 - i0; ++i) { + data[i * stride] = buf[offset + i]; + } + } +} + +// Inverse multi-component transform and DC level shift. This also +// converts fixed point samples back to integers. +GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) { + JPXTileComp *tileComp; + int coeff, d0, d1, d2, minVal, maxVal, zeroVal; + int *dataPtr; + Guint j, comp, x, y; + + //----- inverse multi-component transform + + if (tile->multiComp == 1) { + if (img.nComps < 3 || + tile->tileComps[0].hSep != tile->tileComps[1].hSep || + tile->tileComps[0].vSep != tile->tileComps[1].vSep || + tile->tileComps[1].hSep != tile->tileComps[2].hSep || + tile->tileComps[1].vSep != tile->tileComps[2].vSep) { + return gFalse; + } + + // inverse irreversible multiple component transform + if (tile->tileComps[0].transform == 0) { + j = 0; + for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) { + for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) { + d0 = tile->tileComps[0].data[j]; + d1 = tile->tileComps[1].data[j]; + d2 = tile->tileComps[2].data[j]; + tile->tileComps[0].data[j] = (int)(d0 + 1.402 * d2 + 0.5); + tile->tileComps[1].data[j] = + (int)(d0 - 0.34413 * d1 - 0.71414 * d2 + 0.5); + tile->tileComps[2].data[j] = (int)(d0 + 1.772 * d1 + 0.5); + ++j; + } + } + + // inverse reversible multiple component transform + } else { + j = 0; + for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) { + for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) { + d0 = tile->tileComps[0].data[j]; + d1 = tile->tileComps[1].data[j]; + d2 = tile->tileComps[2].data[j]; + tile->tileComps[0].data[j] = d0 - ((d2 + d1) >> 2); + tile->tileComps[1].data[j] = d2 - d1; + tile->tileComps[2].data[j] = d0 - d1; + ++j; + } + } + } + } + + //----- DC level shift + for (comp = 0; comp < img.nComps; ++comp) { + tileComp = &tile->tileComps[comp]; + + // signed: clip + if (tileComp->sgned) { + minVal = -(1 << (tileComp->prec - 1)); + maxVal = (1 << (tileComp->prec - 1)) - 1; + dataPtr = tileComp->data; + for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) { + for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) { + coeff = *dataPtr; + if (tileComp->transform == 0) { + coeff >>= fracBits; + } + if (coeff < minVal) { + coeff = minVal; + } else if (coeff > maxVal) { + coeff = maxVal; + } + *dataPtr++ = coeff; + } + } + + // unsigned: inverse DC level shift and clip + } else { + maxVal = (1 << tileComp->prec) - 1; + zeroVal = 1 << (tileComp->prec - 1); + dataPtr = tileComp->data; + for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) { + for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) { + coeff = *dataPtr; + if (tileComp->transform == 0) { + coeff >>= fracBits; + } + coeff += zeroVal; + if (coeff < 0) { + coeff = 0; + } else if (coeff > maxVal) { + coeff = maxVal; + } + *dataPtr++ = coeff; + } + } + } + } + + return gTrue; +} + +GBool JPXStream::readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen) { + Guint len, lenH; + + if (!readULong(&len) || + !readULong(boxType)) { + return gFalse; + } + if (len == 1) { + if (!readULong(&lenH) || !readULong(&len)) { + return gFalse; + } + if (lenH) { + error(getPos(), "JPX stream contains a box larger than 2^32 bytes"); + return gFalse; + } + *boxLen = len; + *dataLen = len - 16; + } else if (len == 0) { + *boxLen = 0; + *dataLen = 0; + } else { + *boxLen = len; + *dataLen = len - 8; + } + return gTrue; +} + +int JPXStream::readMarkerHdr(int *segType, Guint *segLen) { + int c; + + do { + do { + if ((c = str->getChar()) == EOF) { + return gFalse; + } + } while (c != 0xff); + do { + if ((c = str->getChar()) == EOF) { + return gFalse; + } + } while (c == 0xff); + } while (c == 0x00); + *segType = c; + if ((c >= 0x30 && c <= 0x3f) || + c == 0x4f || c == 0x92 || c == 0x93 || c == 0xd9) { + *segLen = 0; + return gTrue; + } + return readUWord(segLen); +} + +GBool JPXStream::readUByte(Guint *x) { + int c0; + + if ((c0 = str->getChar()) == EOF) { + return gFalse; + } + *x = (Guint)c0; + return gTrue; +} + +GBool JPXStream::readByte(int *x) { + int c0; + + if ((c0 = str->getChar()) == EOF) { + return gFalse; + } + *x = c0; + if (c0 & 0x80) { + *x |= -1 - 0xff; + } + return gTrue; +} + +GBool JPXStream::readUWord(Guint *x) { + int c0, c1; + + if ((c0 = str->getChar()) == EOF || + (c1 = str->getChar()) == EOF) { + return gFalse; + } + *x = (Guint)((c0 << 8) | c1); + return gTrue; +} + +GBool JPXStream::readULong(Guint *x) { + int c0, c1, c2, c3; + + if ((c0 = str->getChar()) == EOF || + (c1 = str->getChar()) == EOF || + (c2 = str->getChar()) == EOF || + (c3 = str->getChar()) == EOF) { + return gFalse; + } + *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3); + return gTrue; +} + +GBool JPXStream::readNBytes(int nBytes, GBool signd, int *x) { + int y, c, i; + + y = 0; + for (i = 0; i < nBytes; ++i) { + if ((c = str->getChar()) == EOF) { + return gFalse; + } + y = (y << 8) + c; + } + if (signd) { + if (y & (1 << (8 * nBytes - 1))) { + y |= -1 << (8 * nBytes); + } + } + *x = y; + return gTrue; +} + +GBool JPXStream::readBits(int nBits, Guint *x) { + int c; + + while (bitBufLen < nBits) { + if ((c = str->getChar()) == EOF) { + return gFalse; + } + ++byteCount; + if (bitBufSkip) { + bitBuf = (bitBuf << 7) | (c & 0x7f); + bitBufLen += 7; + } else { + bitBuf = (bitBuf << 8) | (c & 0xff); + bitBufLen += 8; + } + bitBufSkip = c == 0xff; + } + *x = (bitBuf >> (bitBufLen - nBits)) & ((1 << nBits) - 1); + bitBufLen -= nBits; + return gTrue; +} + +void JPXStream::clearBitBuf() { + bitBufLen = 0; + bitBufSkip = gFalse; + byteCount = 0; +}