0001: /*
0002: * $RCSfile: FileBitstreamReaderAgent.java,v $
0003: * $Revision: 1.4 $
0004: * $Date: 2006/10/05 01:10:31 $
0005: * $State: Exp $
0006: *
0007: * Class: FileBitstreamReaderAgent
0008: *
0009: * Description: Retrieve code-blocks codewords in the bit stream
0010: *
0011: *
0012: *
0013: * COPYRIGHT:
0014: *
0015: * This software module was originally developed by Raphaël Grosbois and
0016: * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
0017: * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
0018: * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
0019: * Centre France S.A) in the course of development of the JPEG2000
0020: * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
0021: * software module is an implementation of a part of the JPEG 2000
0022: * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
0023: * Systems AB and Canon Research Centre France S.A (collectively JJ2000
0024: * Partners) agree not to assert against ISO/IEC and users of the JPEG
0025: * 2000 Standard (Users) any of their rights under the copyright, not
0026: * including other intellectual property rights, for this software module
0027: * with respect to the usage by ISO/IEC and Users of this software module
0028: * or modifications thereof for use in hardware or software products
0029: * claiming conformance to the JPEG 2000 Standard. Those intending to use
0030: * this software module in hardware or software products are advised that
0031: * their use may infringe existing patents. The original developers of
0032: * this software module, JJ2000 Partners and ISO/IEC assume no liability
0033: * for use of this software module or modifications thereof. No license
0034: * or right to this software module is granted for non JPEG 2000 Standard
0035: * conforming products. JJ2000 Partners have full right to use this
0036: * software module for his/her own purpose, assign or donate this
0037: * software module to any third party and to inhibit third parties from
0038: * using this software module for non JPEG 2000 Standard conforming
0039: * products. This copyright notice must be included in all copies or
0040: * derivative works of this software module.
0041: *
0042: * Copyright (c) 1999/2000 JJ2000 Partners.
0043: * */
0044: package jj2000.j2k.codestream.reader;
0045:
0046: import java.awt.Point;
0047:
0048: import jj2000.j2k.quantization.dequantizer.*;
0049: import jj2000.j2k.wavelet.synthesis.*;
0050: import jj2000.j2k.entropy.decoder.*;
0051: import jj2000.j2k.codestream.*;
0052: import jj2000.j2k.decoder.*;
0053: import jj2000.j2k.entropy.*;
0054: import jj2000.j2k.image.*;
0055: import jj2000.j2k.util.*;
0056: import jj2000.j2k.io.*;
0057: import jj2000.j2k.*;
0058:
0059: import java.util.*;
0060: import java.io.*;
0061: import javax.imageio.stream.ImageInputStream;
0062: import javax.imageio.stream.MemoryCacheImageInputStream;
0063:
0064: import com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageReadParamJava;
0065:
0066: /**
0067: * This class reads the bit stream (with the help of HeaderDecoder for tile
0068: * headers and PktDecoder for packets header and body) and retrives location
0069: * of all code-block's codewords.
0070: *
0071: * <P>Note: All tile-parts headers are read by the constructor whereas packets
0072: * are processed when decoding related tile (when setTile method is called).
0073: *
0074: * <P>In parsing mode, the reader simulates a virtual layer-resolution
0075: * progressive bit stream with the same truncation points in each code-block,
0076: * whereas in truncation mode, only the first bytes are taken into account (it
0077: * behaves like if it is a real truncated codestream).
0078: *
0079: * @see HeaderDecoder
0080: * @see PktDecoder
0081: * */
0082: public class FileBitstreamReaderAgent extends BitstreamReaderAgent
0083: implements Markers, ProgressionType, StdEntropyCoderOptions {
0084:
0085: /** Whether or not the last read Psot value was zero. Only the Psot in the
0086: * last tile-part in the codestream can have such a value. */
0087: private boolean isPsotEqualsZero = true;
0088:
0089: /** Reference to the PktDecoder instance */
0090: public PktDecoder pktDec;
0091:
0092: /** Reference to the J2KImageReadParamJava instance */
0093: private J2KImageReadParamJava j2krparam;
0094:
0095: /** The RandomAccessIO where to get data from */
0096: private RandomAccessIO in;
0097:
0098: /** The number of tiles in the image */
0099: private int nt;
0100:
0101: /** Offset of the first packet in each tile-part in each tile */
0102: private int[][] firstPackOff;
0103:
0104: /**
0105: * Returns the number of tile-part found for a given tile
0106: *
0107: * @param t Tile index
0108: *
0109: * */
0110: public int getNumTileParts(int t) {
0111: if (firstPackOff == null || firstPackOff[t] == null) {
0112: throw new Error("Tile " + t
0113: + " not found in input codestream.");
0114: }
0115: return firstPackOff[t].length;
0116: }
0117:
0118: /** Number of bytes allocated to each tile. In parsing mode, this number
0119: * is related to the tile length in the codestream whereas in truncation
0120: * mode all the rate is affected to the first tiles. */
0121: private int[] nBytes;
0122:
0123: /** Whether or not to print information found in codestream */
0124: private boolean printInfo = false;
0125:
0126: /**
0127: * Backup of the number of bytes allocated to each tile. This array is
0128: * used to restore the number of bytes to read in each tile when the
0129: * codestream is read several times (for instance when decoding an R,G,B
0130: * image to three output files)
0131: * */
0132: private int[] baknBytes;
0133:
0134: /** Length of each tile-part (written in Psot) */
0135: private int[][] tilePartLen;
0136:
0137: /** Total length of each tile */
0138: private int[] totTileLen;
0139:
0140: /** Total length of tiles' header */
0141: private int[] totTileHeadLen;
0142:
0143: /** First tile part header length*/
0144: private int firstTilePartHeadLen;
0145:
0146: /** Total length of all tile parts in all tiles */
0147: private double totAllTileLen;
0148:
0149: /** Length of main header */
0150: private int mainHeadLen;
0151:
0152: /** Length of main and tile-parts headers */
0153: private int headLen = 0;
0154:
0155: /** Length of all tile-part headers */
0156: private int[][] tilePartHeadLen;
0157:
0158: /** Length of each packet head found in the tile */
0159: private Vector pktHL;
0160:
0161: /** True if truncation mode is used. False if parsing mode */
0162: private boolean isTruncMode;
0163:
0164: /** The number of tile-parts that remain to read */
0165: private int remainingTileParts;
0166:
0167: /** The number of tile-parts read so far for each tile */
0168: private int[] tilePartsRead;
0169:
0170: /** Thetotal number of tile-parts read so far */
0171: private int totTilePartsRead = 0;
0172:
0173: /** The number of tile-parts in each tile */
0174: private int[] tileParts;
0175:
0176: /** The total number of tile-parts in each tile */
0177: private int[] totTileParts;
0178:
0179: /** The current tile part being used */
0180: private int curTilePart;
0181:
0182: /** The number of the tile-part in the codestream */
0183: private int[][] tilePartNum;
0184:
0185: /** Whether or not a EOC marker has been found instead of a SOT */
0186: private boolean isEOCFound = false;
0187:
0188: /** Reference to the HeaderInfo instance (used when reading SOT marker
0189: * segments) */
0190: private HeaderInfo hi;
0191:
0192: /** Array containing info. for all the code-blocks:<br>
0193: * - 1st dim: component index.<br>
0194: * - 2nd dim: resolution level index.<br>
0195: * - 3rd dim: subband index.<br>
0196: * - 4th/5th dim: code-block index (vert. and horiz.).<br>
0197: */
0198: private CBlkInfo[][][][][] cbI;
0199:
0200: /** Gets the reference to the CBlkInfo array */
0201: public CBlkInfo[][][][][] getCBlkInfo() {
0202: return cbI;
0203: }
0204:
0205: /** The maximum number of layers to decode for any code-block */
0206: private int lQuit;
0207:
0208: /** Whether or not to use only first progression order */
0209: private boolean usePOCQuit = false;
0210:
0211: /**
0212: * Reads all tiles headers and keep offset of their first
0213: * packet. Finally it calls the rate allocation method.
0214: *
0215: * @param hd HeaderDecoder of the codestream.
0216: *
0217: * @param ehs The input stream where to read bit-stream.
0218: *
0219: * @param decSpec The decoder specifications
0220: *
0221: * @param j2krparam The J2KImageReadParam instance created from the
0222: * command-line arguments.
0223: *
0224: * @param cdstrInfo Whether or not to print information found in
0225: * codestream.
0226: *
0227: * @see #allocateRate
0228: * */
0229: public FileBitstreamReaderAgent(HeaderDecoder hd,
0230: RandomAccessIO ehs, DecoderSpecs decSpec,
0231: J2KImageReadParamJava j2krparam, boolean cdstrInfo,
0232: HeaderInfo hi) throws IOException {
0233: super (hd, decSpec);
0234:
0235: this .j2krparam = j2krparam;
0236: this .printInfo = cdstrInfo;
0237: this .hi = hi;
0238:
0239: String strInfo = printInfo ? "Codestream elements information in bytes "
0240: + "(offset, total length, header length):\n\n"
0241: : null;
0242:
0243: // Check whether quit conditiosn used
0244: //usePOCQuit = j2krparam.getPOCQuit();
0245:
0246: // Get decoding rate
0247: if (j2krparam.getDecodingRate() == Double.MAX_VALUE)
0248: tnbytes = Integer.MAX_VALUE;
0249: else
0250: tnbytes = (int) (j2krparam.getDecodingRate()
0251: * hd.getMaxCompImgWidth() * hd
0252: .getMaxCompImgHeight()) / 8;
0253: //isTruncMode = !j2krparam.getParsing();
0254: isTruncMode = true;
0255:
0256: // Check if quit conditions are being used
0257: //int ncbQuit = j2krparam.getNCBQuit();
0258: int ncbQuit = -1;
0259: if (ncbQuit != -1 && !isTruncMode) {
0260: throw new Error(
0261: "Cannot use -parsing and -ncb_quit condition at "
0262: + "the same time.");
0263: }
0264:
0265: // lQuit = j2krparam.getLQuit();
0266: lQuit = -1;
0267:
0268: // initializations
0269: nt = ntX * ntY;
0270: in = ehs;
0271: pktDec = new PktDecoder(decSpec, hd, ehs, this , isTruncMode,
0272: ncbQuit);
0273:
0274: tileParts = new int[nt];
0275: totTileParts = new int[nt];
0276: totTileLen = new int[nt];
0277: tilePartLen = new int[nt][];
0278: tilePartNum = new int[nt][];
0279: firstPackOff = new int[nt][];
0280: tilePartsRead = new int[nt];
0281: totTileHeadLen = new int[nt];
0282: tilePartHeadLen = new int[nt][];
0283: nBytes = new int[nt];
0284: baknBytes = new int[nt];
0285: hd.nTileParts = new int[nt];
0286:
0287: this .isTruncMode = isTruncMode;
0288:
0289: // Keeps main header's length, takes file format overhead into account
0290: cdstreamStart = hd.mainHeadOff; // Codestream offset in the file
0291: mainHeadLen = in.getPos() - cdstreamStart;
0292: headLen = mainHeadLen;
0293:
0294: // If ncb and lbody quit conditions are used, headers are not counted
0295: if (ncbQuit == -1) {
0296: anbytes = mainHeadLen;
0297: } else {
0298: anbytes = 0;
0299: }
0300:
0301: if (printInfo)
0302: strInfo += "Main header length : " + cdstreamStart
0303: + ", " + mainHeadLen + ", " + mainHeadLen + "\n";
0304:
0305: // If cannot even read the first tile-part
0306: if (anbytes > tnbytes) {
0307: throw new Error("Requested bitrate is too small.");
0308: }
0309:
0310: // Initialize variables used when reading tile-part headers.
0311: totAllTileLen = 0;
0312: remainingTileParts = nt; // at least as many tile-parts as tiles
0313: maxPos = lastPos = in.getPos();
0314:
0315: // Update 'res' value according to the parameter and the main header.
0316: if (j2krparam.getResolution() == -1) {
0317: targetRes = decSpec.dls.getMin();
0318: } else {
0319: targetRes = j2krparam.getResolution();
0320: if (targetRes < 0) {
0321: throw new IllegalArgumentException(
0322: "Specified negative "
0323: + "resolution level index: "
0324: + targetRes);
0325: }
0326: }
0327:
0328: // Verify reduction in resolution level
0329: int mdl = decSpec.dls.getMin();
0330: if (targetRes > mdl) {
0331: FacilityManager
0332: .getMsgLogger()
0333: .printmsg(
0334: MsgLogger.WARNING,
0335: "Specified resolution level ("
0336: + targetRes
0337: + ") is larger"
0338: + " than the maximum possible. Setting it to "
0339: + mdl + " (maximum possible)");
0340: targetRes = mdl;
0341: }
0342:
0343: // Initialize tile part positions from TLM marker segment.
0344: initTLM();
0345: }
0346:
0347: // An array of the positions of tile parts:
0348: // - length of tilePartPositions is nt.
0349: // - length of tilePartPositions[i] is totTileParts[i].
0350: long[][] tilePartPositions = null;
0351:
0352: //
0353: // Initialize the tilePartPositions positions array if a TLM marker
0354: // segment is present in the main header. If no such marker segment
0355: // is present the array will remain null. This method rewinds to the
0356: // start of the codestream and scans until the first SOT marker is
0357: // encountered. Before return the stream is returned to its position
0358: // when the method was invoked.
0359: //
0360: private void initTLM() throws IOException {
0361: // Save the position to return to at the end of this method.
0362: int savePos = in.getPos();
0363:
0364: // Array to store contents of TLM segments. The first index is
0365: // Ztlm. The contents of tlmSegments[i] is the bytes in the TLM
0366: // segment with Ztlm == i after the Ztlm byte.
0367: byte[][] tlmSegments = null;
0368:
0369: // Number of TLM segments. The first numTLM elements of tlmSegments
0370: // should be non-null if the segments are correct.
0371: int numTLM = 0;
0372:
0373: try {
0374: // Rewind to the start of the main header.
0375: in.seek(cdstreamStart + 2); // skip SOC
0376:
0377: // Loop over marker segments.
0378: short marker;
0379: while ((marker = in.readShort()) != SOT) {
0380: // Get the length (which includes the 2-byte length parameter).
0381: int markerLength = in.readUnsignedShort();
0382:
0383: // Process TLM segments.
0384: if (marker == TLM) {
0385: numTLM++;
0386:
0387: if (tlmSegments == null) {
0388: tlmSegments = new byte[256][]; // 0 <= Ztlm <= 255
0389: }
0390:
0391: // Save contents after Ztlm in array.
0392: int Ztlm = in.read();
0393: tlmSegments[Ztlm] = new byte[markerLength - 3];
0394: in
0395: .readFully(tlmSegments[Ztlm], 0,
0396: markerLength - 3);
0397: } else {
0398: in.skipBytes(markerLength - 2);
0399: }
0400: }
0401: } catch (IOException e) {
0402: // Reset so that the TLM segments are not processed further.
0403: tlmSegments = null;
0404: }
0405:
0406: if (tlmSegments != null) {
0407: ArrayList[] tlmOffsets = null;
0408:
0409: // Tiles start after the main header.
0410: long tilePos = cdstreamStart + mainHeadLen;
0411:
0412: // Tile counter for when tile indexes are not included.
0413: int tileCounter = 0;
0414:
0415: for (int itlm = 0; itlm < numTLM; itlm++) {
0416: if (tlmSegments[itlm] == null) {
0417: // Null segment among first numTLM entries: error.
0418: tlmOffsets = null;
0419: break;
0420: } else if (tlmOffsets == null) {
0421: tlmOffsets = new ArrayList[nt];
0422: }
0423:
0424: // Create a stream.
0425: ByteArrayInputStream bais = new ByteArrayInputStream(
0426: tlmSegments[itlm]);
0427: ImageInputStream iis = new MemoryCacheImageInputStream(
0428: bais);
0429:
0430: try {
0431: int Stlm = iis.read();
0432: int ST = (Stlm >> 4) & 0x3;
0433: int SP = (Stlm >> 6) & 0x1;
0434:
0435: int tlmLength = tlmSegments[itlm].length;
0436: while (iis.getStreamPosition() < tlmLength) {
0437: int tileIndex = tileCounter;
0438: switch (ST) {
0439: case 1:
0440: tileIndex = iis.read();
0441: break;
0442: case 2:
0443: tileIndex = iis.readUnsignedShort();
0444: }
0445:
0446: if (tlmOffsets[tileIndex] == null) {
0447: tlmOffsets[tileIndex] = new ArrayList();
0448: }
0449: tlmOffsets[tileIndex].add(new Long(tilePos));
0450:
0451: long tileLength = 0L;
0452: switch (SP) {
0453: case 0:
0454: tileLength = iis.readUnsignedShort();
0455: break;
0456: case 1:
0457: tileLength = iis.readUnsignedInt();
0458: break;
0459: }
0460:
0461: tilePos += tileLength;
0462:
0463: if (ST == 0)
0464: tileCounter++;
0465: }
0466: } catch (IOException e) {
0467: // XXX?
0468: }
0469: }
0470:
0471: if (tlmOffsets != null) {
0472: tilePartPositions = new long[nt][];
0473: for (int i = 0; i < nt; i++) {
0474: if (tlmOffsets[i] == null) {
0475: tilePartPositions = null;
0476: break;
0477: } else {
0478: ArrayList list = tlmOffsets[i];
0479: int count = list.size();
0480: tilePartPositions[i] = new long[count];
0481: long[] tpPos = tilePartPositions[i];
0482: for (int j = 0; j < count; j++) {
0483: tpPos[j] = ((Long) list.get(j)).longValue();
0484: }
0485: }
0486: }
0487: }
0488: }
0489:
0490: in.seek(savePos);
0491: }
0492:
0493: int cdstreamStart = 0;
0494: int t = 0, pos = -1, tp = 0, tptot = 0;
0495: int tilePartStart = 0;
0496: boolean rateReached = false;
0497: int numtp = 0;
0498: int maxTP = nt; // If maximum 1 tile part per tile specified
0499: int lastPos = 0, maxPos = 0;
0500:
0501: /**
0502: * Read all tile-part headers of the requested tile. All tile-part
0503: * headers prior to the last tile-part header of the current tile will
0504: * also be read.
0505: *
0506: * @param tileNum The index of the tile for which to read tile-part
0507: * headers.
0508: */
0509: private void initTile(int tileNum) throws IOException {
0510: if (tilePartPositions == null)
0511: in.seek(lastPos);
0512: String strInfo = "";
0513: int ncbQuit = -1;
0514: boolean isTilePartRead = false;
0515: boolean isEOFEncountered = false;
0516: try {
0517: int tpNum = 0;
0518: while (remainingTileParts != 0
0519: && (totTileParts[tileNum] == 0 || tilePartsRead[tileNum] < totTileParts[tileNum])) {
0520: isTilePartRead = true;
0521:
0522: if (tilePartPositions != null) {
0523: in.seek((int) tilePartPositions[tileNum][tpNum++]);
0524: }
0525: tilePartStart = in.getPos();
0526:
0527: // Read tile-part header
0528: try {
0529: t = readTilePartHeader();
0530: if (isEOCFound) { // Some tiles are missing but the
0531: // codestream is OK
0532: break;
0533: }
0534: tp = tilePartsRead[t];
0535: if (isPsotEqualsZero) { // Psot may equals zero for the
0536: // last tile-part: it is assumed that this tile-part
0537: // contain all data until EOC
0538: tilePartLen[t][tp] = in.length() - 2
0539: - tilePartStart;
0540: }
0541: } catch (EOFException e) {
0542: firstPackOff[t][tp] = in.length();
0543: throw e;
0544: }
0545:
0546: pos = in.getPos();
0547:
0548: // In truncation mode, if target decoding rate is reached in
0549: // tile-part header, skips the tile-part and stop reading
0550: // unless the ncb and lbody quit condition is in use
0551: if (isTruncMode && ncbQuit == -1) {
0552: if ((pos - cdstreamStart) > tnbytes) {
0553: firstPackOff[t][tp] = in.length();
0554: rateReached = true;
0555: break;
0556: }
0557: }
0558:
0559: // Set tile part position and header length
0560: firstPackOff[t][tp] = pos;
0561: tilePartHeadLen[t][tp] = (pos - tilePartStart);
0562:
0563: if (printInfo)
0564: strInfo += "Tile-part " + tp + " of tile " + t
0565: + " : " + tilePartStart + ", "
0566: + tilePartLen[t][tp] + ", "
0567: + tilePartHeadLen[t][tp] + "\n";
0568:
0569: // Update length counters
0570: totTileLen[t] += tilePartLen[t][tp];
0571: totTileHeadLen[t] += tilePartHeadLen[t][tp];
0572: totAllTileLen += tilePartLen[t][tp];
0573: if (isTruncMode) {
0574: if (anbytes + tilePartLen[t][tp] > tnbytes) {
0575: anbytes += tilePartHeadLen[t][tp];
0576: headLen += tilePartHeadLen[t][tp];
0577: rateReached = true;
0578: nBytes[t] += (tnbytes - anbytes);
0579: break;
0580: } else {
0581: anbytes += tilePartHeadLen[t][tp];
0582: headLen += tilePartHeadLen[t][tp];
0583: nBytes[t] += (tilePartLen[t][tp] - tilePartHeadLen[t][tp]);
0584: }
0585: } else {
0586: if (anbytes + tilePartHeadLen[t][tp] > tnbytes) {
0587: break;
0588: } else {
0589: anbytes += tilePartHeadLen[t][tp];
0590: headLen += tilePartHeadLen[t][tp];
0591: }
0592: }
0593:
0594: // If this is first tile-part, remember header length
0595: if (tptot == 0)
0596: firstTilePartHeadLen = tilePartHeadLen[t][tp];
0597:
0598: // Go to the beginning of next tile part
0599: tilePartsRead[t]++;
0600: int nextMarkerPos = tilePartStart + tilePartLen[t][tp];
0601: if (tilePartPositions == null) {
0602: in.seek(nextMarkerPos);
0603: }
0604: if (nextMarkerPos > maxPos) {
0605: maxPos = nextMarkerPos;
0606: }
0607: remainingTileParts--;
0608: maxTP--;
0609: tptot++;
0610: // If Psot of the current tile-part was equal to zero, it is
0611: // assumed that it contains all data until the EOC marker
0612: if (isPsotEqualsZero) {
0613: if (remainingTileParts != 0) {
0614: FacilityManager
0615: .getMsgLogger()
0616: .printmsg(
0617: MsgLogger.WARNING,
0618: "Some tile-parts have not "
0619: + "been found. The codestream may be corrupted.");
0620: }
0621: break;
0622: }
0623: }
0624: } catch (EOFException e) {
0625: isEOFEncountered = true;
0626:
0627: if (printInfo) {
0628: FacilityManager.getMsgLogger().printmsg(MsgLogger.INFO,
0629: strInfo);
0630: }
0631: FacilityManager.getMsgLogger().printmsg(MsgLogger.WARNING,
0632: "Codestream truncated in tile " + t);
0633:
0634: // Set specified rate to end of file if valid
0635: int fileLen = in.length();
0636: if (fileLen < tnbytes) {
0637: tnbytes = fileLen;
0638: trate = tnbytes * 8f / hd.getMaxCompImgWidth()
0639: / hd.getMaxCompImgHeight();
0640: }
0641: }
0642:
0643: // If no tile-parts read then return.
0644: if (!isTilePartRead)
0645: return;
0646:
0647: /* XXX: BEGIN Updating the resolution here is logical when all tile-part
0648: headers are read as was the case with the original version of this
0649: class. With initTile() however the tiles could be read in random
0650: order so modifying the resolution value could cause unexpected
0651: results if a given tile-part has fewer levels than the main header
0652: indicated.
0653: // Update 'res' value once all tile-part headers are read
0654: if(j2krparam.getResolution()== -1) {
0655: targetRes = decSpec.dls.getMin();
0656: } else {
0657: targetRes = j2krparam.getResolution();
0658: if(targetRes<0) {
0659: throw new
0660: IllegalArgumentException("Specified negative "+
0661: "resolution level index: "+
0662: targetRes);
0663: }
0664: }
0665:
0666: // Verify reduction in resolution level
0667: int mdl = decSpec.dls.getMin();
0668: if(targetRes>mdl) {
0669: FacilityManager.getMsgLogger().
0670: printmsg(MsgLogger.WARNING,
0671: "Specified resolution level ("+targetRes+
0672: ") is larger"+
0673: " than the maximum possible. Setting it to "+
0674: mdl +" (maximum possible)");
0675: targetRes = mdl;
0676: }
0677: XXX: END */
0678:
0679: if (!isEOFEncountered) {
0680: if (printInfo) {
0681: FacilityManager.getMsgLogger().printmsg(MsgLogger.INFO,
0682: strInfo);
0683: }
0684:
0685: if (remainingTileParts == 0) {
0686: // Check presence of EOC marker is decoding rate not reached or
0687: // if this marker has not been found yet
0688: if (!isEOCFound && !isPsotEqualsZero && !rateReached) {
0689: try {
0690: int savePos = in.getPos();
0691: in.seek(maxPos);
0692: if (in.readShort() != EOC) {
0693: FacilityManager
0694: .getMsgLogger()
0695: .printmsg(
0696: MsgLogger.WARNING,
0697: "EOC marker not found. "
0698: + "Codestream is corrupted.");
0699: }
0700: in.seek(savePos);
0701: } catch (EOFException e) {
0702: FacilityManager.getMsgLogger().printmsg(
0703: MsgLogger.WARNING,
0704: "EOC marker is missing");
0705: }
0706: }
0707: }
0708: }
0709:
0710: // Bit-rate allocation
0711: if (!isTruncMode) {
0712: allocateRate();
0713: } else if (remainingTileParts == 0 && !isEOFEncountered) {
0714: // Take EOC into account if rate is not reached
0715: if (in.getPos() >= tnbytes)
0716: anbytes += 2;
0717: }
0718:
0719: if (tilePartPositions == null)
0720: lastPos = in.getPos();
0721:
0722: // Backup nBytes
0723: for (int tIdx = 0; tIdx < nt; tIdx++) {
0724: baknBytes[tIdx] = nBytes[tIdx];
0725: if (printInfo) {
0726: FacilityManager
0727: .getMsgLogger()
0728: .println(
0729: ""
0730: + hi
0731: .toStringTileHeader(
0732: tIdx,
0733: tilePartLen[tIdx].length),
0734: 2, 2);
0735: }
0736: }
0737: }
0738:
0739: /**
0740: * Allocates output bit-rate for each tile in parsing mode: The allocator
0741: * simulates the truncation of a virtual layer-resolution progressive
0742: * codestream.
0743: * */
0744: private void allocateRate() throws IOException {
0745: int stopOff = tnbytes;
0746:
0747: // In parsing mode, the bitrate is allocated related to each tile's
0748: // length in the bit stream
0749:
0750: // EOC marker's length
0751: if (remainingTileParts == 0)
0752: anbytes += 2;
0753:
0754: // If there are too few bytes to read the tile part headers throw an
0755: // error
0756: if (anbytes > stopOff) {
0757: throw new Error(
0758: "Requested bitrate is too small for parsing");
0759: }
0760:
0761: // Calculate bitrate for each tile
0762: int rem = stopOff - anbytes;
0763: int totnByte = rem;
0764: for (int t = nt - 1; t > 0; t--) {
0765: rem -= nBytes[t] = (int) (totnByte * (totTileLen[t] / totAllTileLen));
0766: }
0767: nBytes[0] = rem;
0768: }
0769:
0770: /**
0771: * Reads SOT marker segment of the tile-part header and calls related
0772: * methods of the HeaderDecoder to read other markers segments. The
0773: * tile-part header is entirely read when a SOD marker is encountered.
0774: *
0775: * @return The tile number of the tile part that was read
0776: * */
0777: private int readTilePartHeader() throws IOException {
0778: HeaderInfo.SOT ms = hi.getNewSOT();
0779:
0780: // SOT marker
0781: short marker = in.readShort();
0782: if (marker != SOT) {
0783: if (marker == EOC) {
0784: isEOCFound = true;
0785: return -1;
0786: } else {
0787: throw new CorruptedCodestreamException(
0788: "SOT tag not found " + "in tile-part start");
0789: }
0790: }
0791: isEOCFound = false;
0792:
0793: // Lsot (shall equals 10)
0794: int lsot = in.readUnsignedShort();
0795: ms.lsot = lsot;
0796: if (lsot != 10)
0797: throw new CorruptedCodestreamException("Wrong length for "
0798: + "SOT marker segment: " + lsot);
0799:
0800: // Isot
0801: int tile = in.readUnsignedShort();
0802: ms.isot = tile;
0803: if (tile > 65534) {
0804: throw new CorruptedCodestreamException(
0805: "Tile index too high in " + "tile-part.");
0806: }
0807:
0808: // Psot
0809: int psot = in.readInt();
0810: ms.psot = psot;
0811: isPsotEqualsZero = (psot != 0) ? false : true;
0812: if (psot < 0) {
0813: throw new NotImplementedError("Tile length larger "
0814: + "than maximum supported");
0815: }
0816: // TPsot
0817: int tilePart = in.read();
0818: ms.tpsot = tilePart;
0819: if (tilePart != tilePartsRead[tile] || tilePart < 0
0820: || tilePart > 254) {
0821: throw new CorruptedCodestreamException(
0822: "Out of order tile-part");
0823: }
0824: // TNsot
0825: int nrOfTileParts = in.read();
0826: ms.tnsot = nrOfTileParts;
0827: hi.sot.put("t" + tile + "_tp" + tilePart, ms);
0828: if (nrOfTileParts == 0) { // The number of tile-part is not specified in
0829: // this tile-part header.
0830:
0831: // Assumes that there will be another tile-part in the codestream
0832: // that will indicate the number of tile-parts for this tile)
0833: int nExtraTp;
0834: if (tileParts[tile] == 0
0835: || tileParts[tile] == tilePartLen.length) {
0836: // Then there are two tile-parts (one is the current and the
0837: // other will indicate the number of tile-part for this tile)
0838: nExtraTp = 2;
0839: remainingTileParts += 1;
0840: } else {
0841: // There is already one scheduled extra tile-part. In this
0842: // case just add place for the current one
0843: nExtraTp = 1;
0844: }
0845:
0846: tileParts[tile] += nExtraTp;
0847: nrOfTileParts = tileParts[tile];
0848: FacilityManager
0849: .getMsgLogger()
0850: .printmsg(
0851: MsgLogger.WARNING,
0852: "Header of tile-part "
0853: + tilePart
0854: + " of tile "
0855: + tile
0856: + ", does not indicate the total"
0857: + " number of tile-parts. Assuming that there are "
0858: + nrOfTileParts
0859: + " tile-parts for this tile.");
0860:
0861: // Increase and re-copy tilePartLen array
0862: int[] tmpA = tilePartLen[tile];
0863: tilePartLen[tile] = new int[nrOfTileParts];
0864: for (int i = 0; i < nrOfTileParts - nExtraTp; i++) {
0865: tilePartLen[tile][i] = tmpA[i];
0866: }
0867: // Increase and re-copy tilePartNum array
0868: tmpA = tilePartNum[tile];
0869: tilePartNum[tile] = new int[nrOfTileParts];
0870: for (int i = 0; i < nrOfTileParts - nExtraTp; i++) {
0871: tilePartNum[tile][i] = tmpA[i];
0872: }
0873:
0874: // Increase and re-copy firsPackOff array
0875: tmpA = firstPackOff[tile];
0876: firstPackOff[tile] = new int[nrOfTileParts];
0877: for (int i = 0; i < nrOfTileParts - nExtraTp; i++) {
0878: firstPackOff[tile][i] = tmpA[i];
0879: }
0880:
0881: // Increase and re-copy tilePartHeadLen array
0882: tmpA = tilePartHeadLen[tile];
0883: tilePartHeadLen[tile] = new int[nrOfTileParts];
0884: for (int i = 0; i < nrOfTileParts - nExtraTp; i++) {
0885: tilePartHeadLen[tile][i] = tmpA[i];
0886: }
0887: } else { // The number of tile-parts is specified in the tile-part
0888: // header
0889: totTileParts[tile] = nrOfTileParts;
0890:
0891: // Check if it is consistant with what was found in previous
0892: // tile-part headers
0893:
0894: if (tileParts[tile] == 0) { // First tile-part: OK
0895: remainingTileParts += nrOfTileParts - 1;
0896: tileParts[tile] = nrOfTileParts;
0897: tilePartLen[tile] = new int[nrOfTileParts];
0898: tilePartNum[tile] = new int[nrOfTileParts];
0899: firstPackOff[tile] = new int[nrOfTileParts];
0900: tilePartHeadLen[tile] = new int[nrOfTileParts];
0901: } else if (tileParts[tile] > nrOfTileParts) {
0902: // Already found more tile-parts than signaled here
0903: throw new CorruptedCodestreamException(
0904: "Invalid number " + "of tile-parts in"
0905: + " tile " + tile + ": "
0906: + nrOfTileParts);
0907: } else { // Signaled number of tile-part fits with number of
0908: // previously found tile-parts
0909: remainingTileParts += nrOfTileParts - tileParts[tile];
0910:
0911: if (tileParts[tile] != nrOfTileParts) {
0912:
0913: // Increase and re-copy tilePartLen array
0914: int[] tmpA = tilePartLen[tile];
0915: tilePartLen[tile] = new int[nrOfTileParts];
0916: for (int i = 0; i < tileParts[tile] - 1; i++) {
0917: tilePartLen[tile][i] = tmpA[i];
0918: }
0919:
0920: // Increase and re-copy tilePartNum array
0921: tmpA = tilePartNum[tile];
0922: tilePartNum[tile] = new int[nrOfTileParts];
0923: for (int i = 0; i < tileParts[tile] - 1; i++) {
0924: tilePartNum[tile][i] = tmpA[i];
0925: }
0926:
0927: // Increase and re-copy firstPackOff array
0928: tmpA = firstPackOff[tile];
0929: firstPackOff[tile] = new int[nrOfTileParts];
0930: for (int i = 0; i < tileParts[tile] - 1; i++) {
0931: firstPackOff[tile][i] = tmpA[i];
0932: }
0933:
0934: // Increase and re-copy tilePartHeadLen array
0935: tmpA = tilePartHeadLen[tile];
0936: tilePartHeadLen[tile] = new int[nrOfTileParts];
0937: for (int i = 0; i < tileParts[tile] - 1; i++) {
0938: tilePartHeadLen[tile][i] = tmpA[i];
0939: }
0940: }
0941: }
0942: }
0943:
0944: // Other markers
0945: hd.resetHeaderMarkers();
0946: hd.nTileParts[tile] = nrOfTileParts;
0947: // Decode and store the tile-part header (i.e. until a SOD marker is
0948: // found)
0949: do {
0950: hd.extractTilePartMarkSeg(in.readShort(), in, tile,
0951: tilePart);
0952: } while ((hd.getNumFoundMarkSeg() & hd.SOD_FOUND) == 0);
0953:
0954: // Read each marker segment previously found
0955: hd.readFoundTilePartMarkSeg(tile, tilePart);
0956:
0957: tilePartLen[tile][tilePart] = psot;
0958:
0959: tilePartNum[tile][tilePart] = totTilePartsRead;
0960: totTilePartsRead++;
0961:
0962: // Add to list of which tile each successive tile-part belongs.
0963: // This list is needed if there are PPM markers used
0964: hd.setTileOfTileParts(tile);
0965:
0966: return tile;
0967:
0968: }
0969:
0970: /**
0971: * Reads packets of the current tile according to the
0972: * layer-resolution-component-position progressiveness.
0973: *
0974: * @param lys Index of the first layer for each component and resolution.
0975: *
0976: * @param lye Index of the last layer.
0977: *
0978: * @param ress Index of the first resolution level.
0979: *
0980: * @param rese Index of the last resolution level.
0981: *
0982: * @param comps Index of the first component.
0983: *
0984: * @param compe Index of the last component.
0985: *
0986: * @return True if rate has been reached.
0987: * */
0988: private boolean readLyResCompPos(int[][] lys, int lye, int ress,
0989: int rese, int comps, int compe) throws IOException {
0990:
0991: int minlys = 10000;
0992: for (int c = comps; c < compe; c++) { //loop on components
0993: // Check if this component exists
0994: if (c >= mdl.length)
0995: continue;
0996:
0997: for (int r = ress; r < rese; r++) {//loop on resolution levels
0998: if (lys[c] != null && r < lys[c].length
0999: && lys[c][r] < minlys) {
1000: minlys = lys[c][r];
1001: }
1002: }
1003: }
1004:
1005: int t = getTileIdx();
1006: int start;
1007: boolean status = false;
1008: int lastByte = firstPackOff[t][curTilePart]
1009: + tilePartLen[t][curTilePart] - 1
1010: - tilePartHeadLen[t][curTilePart];
1011: int numLayers = ((Integer) decSpec.nls.getTileDef(t))
1012: .intValue();
1013: int nPrec = 1;
1014: int hlen, plen;
1015: String strInfo = printInfo ? "Tile " + getTileIdx()
1016: + " (tile-part:" + curTilePart
1017: + "): offset, length, header length\n" : null;
1018: boolean pph = false;
1019: if (((Boolean) decSpec.pphs.getTileDef(t)).booleanValue()) {
1020: pph = true;
1021: }
1022: for (int l = minlys; l < lye; l++) { // loop on layers
1023: for (int r = ress; r < rese; r++) { // loop on resolution levels
1024: for (int c = comps; c < compe; c++) { // loop on components
1025: // Checks if component exists
1026: if (c >= mdl.length)
1027: continue;
1028: // Checks if resolution level exists
1029: if (r >= lys[c].length)
1030: continue;
1031: if (r > mdl[c])
1032: continue;
1033: // Checks if layer exists
1034: if (l < lys[c][r] || l >= numLayers)
1035: continue;
1036:
1037: nPrec = pktDec.getNumPrecinct(c, r);
1038: for (int p = 0; p < nPrec; p++) { // loop on precincts
1039: start = in.getPos();
1040:
1041: // If packed packet headers are used, there is no need
1042: // to check that there are bytes enough to read header
1043: if (pph) {
1044: pktDec.readPktHead(l, r, c, p, cbI[c][r],
1045: nBytes);
1046: }
1047:
1048: // If we are about to read outside of tile-part,
1049: // skip to next tile-part
1050: if (start > lastByte
1051: && curTilePart < firstPackOff[t].length - 1) {
1052: curTilePart++;
1053: in.seek(firstPackOff[t][curTilePart]);
1054: lastByte = in.getPos()
1055: + tilePartLen[t][curTilePart] - 1
1056: - tilePartHeadLen[t][curTilePart];
1057: }
1058:
1059: // Read SOP marker segment if necessary
1060: status = pktDec.readSOPMarker(nBytes, p, c, r);
1061:
1062: if (status) {
1063: if (printInfo) {
1064: FacilityManager.getMsgLogger()
1065: .printmsg(MsgLogger.INFO,
1066: strInfo);
1067: }
1068: return true;
1069: }
1070:
1071: if (!pph) {
1072: status = pktDec.readPktHead(l, r, c, p,
1073: cbI[c][r], nBytes);
1074: }
1075:
1076: if (status) {
1077: if (printInfo) {
1078: FacilityManager.getMsgLogger()
1079: .printmsg(MsgLogger.INFO,
1080: strInfo);
1081: }
1082: return true;
1083: }
1084:
1085: // Store packet's head length
1086: hlen = in.getPos() - start;
1087: pktHL.addElement(new Integer(hlen));
1088:
1089: // Reads packet's body
1090: status = pktDec.readPktBody(l, r, c, p,
1091: cbI[c][r], nBytes);
1092: plen = in.getPos() - start;
1093: if (printInfo)
1094: strInfo += " Pkt l=" + l + ",r=" + r
1095: + ",c=" + c + ",p=" + p + ": "
1096: + start + ", " + plen + ", " + hlen
1097: + "\n";
1098:
1099: if (status) {
1100: if (printInfo) {
1101: FacilityManager.getMsgLogger()
1102: .printmsg(MsgLogger.INFO,
1103: strInfo);
1104: }
1105: return true;
1106: }
1107:
1108: } // end loop on precincts
1109: } // end loop on components
1110: } // end loop on resolution levels
1111: } // end loop on layers
1112:
1113: if (printInfo) {
1114: FacilityManager.getMsgLogger().printmsg(MsgLogger.INFO,
1115: strInfo);
1116: }
1117: return false; // Decoding rate was not reached
1118: }
1119:
1120: /**
1121: * Reads packets of the current tile according to the
1122: * resolution-layer-component-position progressiveness.
1123: *
1124: * @param lys Index of the first layer for each component and resolution.
1125: *
1126: * @param lye Index of the last layer.
1127: *
1128: * @param ress Index of the first resolution level.
1129: *
1130: * @param rese Index of the last resolution level.
1131: *
1132: * @param comps Index of the first component.
1133: *
1134: * @param compe Index of the last component.
1135: *
1136: * @return True if rate has been reached.
1137: * */
1138: private boolean readResLyCompPos(int lys[][], int lye, int ress,
1139: int rese, int comps, int compe) throws IOException {
1140:
1141: int t = getTileIdx(); // Current tile index
1142: boolean status = false; // True if decoding rate is reached when
1143: int lastByte = firstPackOff[t][curTilePart]
1144: + tilePartLen[t][curTilePart] - 1
1145: - tilePartHeadLen[t][curTilePart];
1146: int minlys = 10000;
1147: for (int c = comps; c < compe; c++) { //loop on components
1148: // Check if this component exists
1149: if (c >= mdl.length)
1150: continue;
1151:
1152: for (int r = ress; r < rese; r++) {//loop on resolution levels
1153: if (r > mdl[c])
1154: continue;
1155: if (lys[c] != null && r < lys[c].length
1156: && lys[c][r] < minlys) {
1157: minlys = lys[c][r];
1158: }
1159: }
1160: }
1161:
1162: String strInfo = printInfo ? "Tile " + getTileIdx()
1163: + " (tile-part:" + curTilePart
1164: + "): offset, length, header length\n" : null;
1165: int numLayers = ((Integer) decSpec.nls.getTileDef(t))
1166: .intValue();
1167: boolean pph = false;
1168: if (((Boolean) decSpec.pphs.getTileDef(t)).booleanValue()) {
1169: pph = true;
1170: }
1171: int nPrec = 1;
1172: int start;
1173: int hlen, plen;
1174: for (int r = ress; r < rese; r++) { // loop on resolution levels
1175: for (int l = minlys; l < lye; l++) { // loop on layers
1176: for (int c = comps; c < compe; c++) { // loop on components
1177: // Checks if component exists
1178: if (c >= mdl.length)
1179: continue;
1180: // Checks if resolution level exists
1181: if (r > mdl[c])
1182: continue;
1183: if (r >= lys[c].length)
1184: continue;
1185: // Checks if layer exists
1186: if (l < lys[c][r] || l >= numLayers)
1187: continue;
1188:
1189: nPrec = pktDec.getNumPrecinct(c, r);
1190:
1191: for (int p = 0; p < nPrec; p++) { // loop on precincts
1192: start = in.getPos();
1193:
1194: // If packed packet headers are used, there is no need
1195: // to check that there are bytes enough to read header
1196: if (pph) {
1197: pktDec.readPktHead(l, r, c, p, cbI[c][r],
1198: nBytes);
1199: }
1200:
1201: // If we are about to read outside of tile-part,
1202: // skip to next tile-part
1203: if (start > lastByte
1204: && curTilePart < firstPackOff[t].length - 1) {
1205: curTilePart++;
1206: in.seek(firstPackOff[t][curTilePart]);
1207: lastByte = in.getPos()
1208: + tilePartLen[t][curTilePart] - 1
1209: - tilePartHeadLen[t][curTilePart];
1210: }
1211:
1212: // Read SOP marker segment if necessary
1213: status = pktDec.readSOPMarker(nBytes, p, c, r);
1214:
1215: if (status) {
1216: if (printInfo) {
1217: FacilityManager.getMsgLogger()
1218: .printmsg(MsgLogger.INFO,
1219: strInfo);
1220: }
1221: return true;
1222: }
1223:
1224: if (!pph) {
1225: status = pktDec.readPktHead(l, r, c, p,
1226: cbI[c][r], nBytes);
1227: }
1228:
1229: if (status) {
1230: if (printInfo) {
1231: FacilityManager.getMsgLogger()
1232: .printmsg(MsgLogger.INFO,
1233: strInfo);
1234: }
1235: // Output rate of EOF reached
1236: return true;
1237: }
1238:
1239: // Store packet's head length
1240: hlen = in.getPos() - start;
1241: pktHL.addElement(new Integer(hlen));
1242:
1243: // Reads packet's body
1244: status = pktDec.readPktBody(l, r, c, p,
1245: cbI[c][r], nBytes);
1246: plen = in.getPos() - start;
1247: if (printInfo)
1248: strInfo += " Pkt l=" + l + ",r=" + r
1249: + ",c=" + c + ",p=" + p + ": "
1250: + start + ", " + plen + ", " + hlen
1251: + "\n";
1252:
1253: if (status) {
1254: if (printInfo) {
1255: FacilityManager.getMsgLogger()
1256: .printmsg(MsgLogger.INFO,
1257: strInfo);
1258: }
1259: // Output rate or EOF reached
1260: return true;
1261: }
1262:
1263: } // end loop on precincts
1264: } // end loop on components
1265: } // end loop on layers
1266: } // end loop on resolution levels
1267:
1268: if (printInfo) {
1269: FacilityManager.getMsgLogger().printmsg(MsgLogger.INFO,
1270: strInfo);
1271: }
1272: return false; // Decoding rate was not reached
1273: }
1274:
1275: /**
1276: * Reads packets of the current tile according to the
1277: * resolution-position-component-layer progressiveness.
1278: *
1279: * @param lys Index of the first layer for each component and resolution.
1280: *
1281: * @param lye Index of the last layer.
1282: *
1283: * @param ress Index of the first resolution level.
1284: *
1285: * @param rese Index of the last resolution level.
1286: *
1287: * @param comps Index of the first component.
1288: *
1289: * @param compe Index of the last component.
1290: *
1291: * @return True if rate has been reached.
1292: * */
1293: private boolean readResPosCompLy(int[][] lys, int lye, int ress,
1294: int rese, int comps, int compe) throws IOException {
1295: // Computes current tile offset in the reference grid
1296:
1297: Point nTiles = getNumTiles(null);
1298: Point tileI = getTile(null);
1299: int x0siz = hd.getImgULX();
1300: int y0siz = hd.getImgULY();
1301: int xsiz = x0siz + hd.getImgWidth();
1302: int ysiz = y0siz + hd.getImgHeight();
1303: int xt0siz = getTilePartULX();
1304: int yt0siz = getTilePartULY();
1305: int xtsiz = getNomTileWidth();
1306: int ytsiz = getNomTileHeight();
1307: int tx0 = (tileI.x == 0) ? x0siz : xt0siz + tileI.x * xtsiz;
1308: int ty0 = (tileI.y == 0) ? y0siz : yt0siz + tileI.y * ytsiz;
1309: int tx1 = (tileI.x != nTiles.x - 1) ? xt0siz + (tileI.x + 1)
1310: * xtsiz : xsiz;
1311: int ty1 = (tileI.y != nTiles.y - 1) ? yt0siz + (tileI.y + 1)
1312: * ytsiz : ysiz;
1313:
1314: // Get precinct information (number,distance between two consecutive
1315: // precincts in the reference grid) in each component and resolution
1316: // level
1317: int t = getTileIdx(); // Current tile index
1318: PrecInfo prec; // temporary variable
1319: int p; // Current precinct index
1320: int gcd_x = 0; // Horiz. distance between 2 precincts in the ref. grid
1321: int gcd_y = 0; // Vert. distance between 2 precincts in the ref. grid
1322: int nPrec = 0; // Total number of found precincts
1323: int[][] nextPrec = new int[compe][]; // Next precinct index in each
1324: // component and resolution level
1325: int minlys = 100000; // minimum layer start index of each component
1326: int minx = tx1; // Horiz. offset of the second precinct in the
1327: // reference grid
1328: int miny = ty1; // Vert. offset of the second precinct in the
1329: // reference grid.
1330: int maxx = tx0; // Max. horiz. offset of precincts in the ref. grid
1331: int maxy = ty0; // Max. vert. offset of precincts in the ref. grid
1332: Point numPrec;
1333: for (int c = comps; c < compe; c++) { // components
1334: for (int r = ress; r < rese; r++) { // resolution levels
1335: if (c >= mdl.length)
1336: continue;
1337: if (r > mdl[c])
1338: continue;
1339: nextPrec[c] = new int[mdl[c] + 1];
1340: if (lys[c] != null && r < lys[c].length
1341: && lys[c][r] < minlys) {
1342: minlys = lys[c][r];
1343: }
1344: p = pktDec.getNumPrecinct(c, r) - 1;
1345: for (; p >= 0; p--) {
1346: prec = pktDec.getPrecInfo(c, r, p);
1347: if (prec.rgulx != tx0) {
1348: if (prec.rgulx < minx)
1349: minx = prec.rgulx;
1350: if (prec.rgulx > maxx)
1351: maxx = prec.rgulx;
1352: }
1353: if (prec.rguly != ty0) {
1354: if (prec.rguly < miny)
1355: miny = prec.rguly;
1356: if (prec.rguly > maxy)
1357: maxy = prec.rguly;
1358: }
1359:
1360: if (nPrec == 0) {
1361: gcd_x = prec.rgw;
1362: gcd_y = prec.rgh;
1363: } else {
1364: gcd_x = MathUtil.gcd(gcd_x, prec.rgw);
1365: gcd_y = MathUtil.gcd(gcd_y, prec.rgh);
1366: }
1367: nPrec++;
1368: } // precincts
1369: } // resolution levels
1370: } // components
1371:
1372: if (nPrec == 0) {
1373: throw new Error("Image cannot have no precinct");
1374: }
1375:
1376: int pyend = (maxy - miny) / gcd_y + 1;
1377: int pxend = (maxx - minx) / gcd_x + 1;
1378: int x, y;
1379: int hlen, plen;
1380: int start;
1381: boolean status = false;
1382: int lastByte = firstPackOff[t][curTilePart]
1383: + tilePartLen[t][curTilePart] - 1
1384: - tilePartHeadLen[t][curTilePart];
1385: int numLayers = ((Integer) decSpec.nls.getTileDef(t))
1386: .intValue();
1387: String strInfo = printInfo ? "Tile " + getTileIdx()
1388: + " (tile-part:" + curTilePart
1389: + "): offset, length, header length\n" : null;
1390: boolean pph = false;
1391: if (((Boolean) decSpec.pphs.getTileDef(t)).booleanValue()) {
1392: pph = true;
1393: }
1394: for (int r = ress; r < rese; r++) { // loop on resolution levels
1395: y = ty0;
1396: x = tx0;
1397: for (int py = 0; py <= pyend; py++) { // Vertical precincts
1398: for (int px = 0; px <= pxend; px++) { // Horiz. precincts
1399: for (int c = comps; c < compe; c++) { // Components
1400: if (c >= mdl.length)
1401: continue;
1402: if (r > mdl[c])
1403: continue;
1404: if (nextPrec[c][r] >= pktDec.getNumPrecinct(c,
1405: r)) {
1406: continue;
1407: }
1408: prec = pktDec.getPrecInfo(c, r, nextPrec[c][r]);
1409: if ((prec.rgulx != x) || (prec.rguly != y)) {
1410: continue;
1411: }
1412: for (int l = minlys; l < lye; l++) { // layers
1413: if (r >= lys[c].length)
1414: continue;
1415: if (l < lys[c][r] || l >= numLayers)
1416: continue;
1417:
1418: start = in.getPos();
1419:
1420: // If packed packet headers are used, there is no
1421: // need to check that there are bytes enough to
1422: // read header
1423: if (pph) {
1424: pktDec.readPktHead(l, r, c,
1425: nextPrec[c][r], cbI[c][r],
1426: nBytes);
1427: }
1428: // If we are about to read outside of tile-part,
1429: // skip to next tile-part
1430: if (start > lastByte
1431: && curTilePart < firstPackOff[t].length - 1) {
1432: curTilePart++;
1433: in.seek(firstPackOff[t][curTilePart]);
1434: lastByte = in.getPos()
1435: + tilePartLen[t][curTilePart]
1436: - 1
1437: - tilePartHeadLen[t][curTilePart];
1438: }
1439:
1440: // Read SOP marker segment if necessary
1441: status = pktDec.readSOPMarker(nBytes,
1442: nextPrec[c][r], c, r);
1443:
1444: if (status) {
1445: if (printInfo) {
1446: FacilityManager.getMsgLogger()
1447: .printmsg(MsgLogger.INFO,
1448: strInfo);
1449: }
1450: return true;
1451: }
1452:
1453: if (!pph) {
1454: status = pktDec.readPktHead(l, r, c,
1455: nextPrec[c][r], cbI[c][r],
1456: nBytes);
1457: }
1458:
1459: if (status) {
1460: if (printInfo) {
1461: FacilityManager.getMsgLogger()
1462: .printmsg(MsgLogger.INFO,
1463: strInfo);
1464: }
1465: return true;
1466: }
1467:
1468: // Store packet's head length
1469: hlen = in.getPos() - start;
1470: pktHL.addElement(new Integer(hlen));
1471:
1472: // Reads packet's body
1473: status = pktDec.readPktBody(l, r, c,
1474: nextPrec[c][r], cbI[c][r], nBytes);
1475: plen = in.getPos() - start;
1476: if (printInfo)
1477: strInfo += " Pkt l=" + l + ",r=" + r
1478: + ",c=" + c + ",p="
1479: + nextPrec[c][r] + ": " + start
1480: + ", " + plen + ", " + hlen
1481: + "\n";
1482:
1483: if (status) {
1484: if (printInfo) {
1485: FacilityManager.getMsgLogger()
1486: .printmsg(MsgLogger.INFO,
1487: strInfo);
1488: }
1489: return true;
1490: }
1491: } // layers
1492: nextPrec[c][r]++;
1493: } // Components
1494: if (px != pxend) {
1495: x = minx + px * gcd_x;
1496: } else {
1497: x = tx0;
1498: }
1499: } // Horizontal precincts
1500: if (py != pyend) {
1501: y = miny + py * gcd_y;
1502: } else {
1503: y = ty0;
1504: }
1505: } // Vertical precincts
1506: } // end loop on resolution levels
1507:
1508: if (printInfo) {
1509: FacilityManager.getMsgLogger().printmsg(MsgLogger.INFO,
1510: strInfo);
1511: }
1512: return false; // Decoding rate was not reached
1513: }
1514:
1515: /**
1516: * Reads packets of the current tile according to the
1517: * position-component-resolution-layer progressiveness.
1518: *
1519: * @param lys Index of the first layer for each component and resolution.
1520: *
1521: * @param lye Index of the last layer.
1522: *
1523: * @param ress Index of the first resolution level.
1524: *
1525: * @param rese Index of the last resolution level.
1526: *
1527: * @param comps Index of the first component.
1528: *
1529: * @param compe Index of the last component.
1530: *
1531: * @return True if rate has been reached.
1532: * */
1533: private boolean readPosCompResLy(int[][] lys, int lye, int ress,
1534: int rese, int comps, int compe) throws IOException {
1535: Point nTiles = getNumTiles(null);
1536: Point tileI = getTile(null);
1537: int x0siz = hd.getImgULX();
1538: int y0siz = hd.getImgULY();
1539: int xsiz = x0siz + hd.getImgWidth();
1540: int ysiz = y0siz + hd.getImgHeight();
1541: int xt0siz = getTilePartULX();
1542: int yt0siz = getTilePartULY();
1543: int xtsiz = getNomTileWidth();
1544: int ytsiz = getNomTileHeight();
1545: int tx0 = (tileI.x == 0) ? x0siz : xt0siz + tileI.x * xtsiz;
1546: int ty0 = (tileI.y == 0) ? y0siz : yt0siz + tileI.y * ytsiz;
1547: int tx1 = (tileI.x != nTiles.x - 1) ? xt0siz + (tileI.x + 1)
1548: * xtsiz : xsiz;
1549: int ty1 = (tileI.y != nTiles.y - 1) ? yt0siz + (tileI.y + 1)
1550: * ytsiz : ysiz;
1551:
1552: // Get precinct information (number,distance between two consecutive
1553: // precincts in the reference grid) in each component and resolution
1554: // level
1555: int t = getTileIdx(); // Current tile index
1556: PrecInfo prec; // temporary variable
1557: int p; // Current precinct index
1558: int gcd_x = 0; // Horiz. distance between 2 precincts in the ref. grid
1559: int gcd_y = 0; // Vert. distance between 2 precincts in the ref. grid
1560: int nPrec = 0; // Total number of found precincts
1561: int[][] nextPrec = new int[compe][]; // Next precinct index in each
1562: // component and resolution level
1563: int minlys = 100000; // minimum layer start index of each component
1564: int minx = tx1; // Horiz. offset of the second precinct in the
1565: // reference grid
1566: int miny = ty1; // Vert. offset of the second precinct in the
1567: // reference grid.
1568: int maxx = tx0; // Max. horiz. offset of precincts in the ref. grid
1569: int maxy = ty0; // Max. vert. offset of precincts in the ref. grid
1570: Point numPrec;
1571: for (int c = comps; c < compe; c++) { // components
1572: for (int r = ress; r < rese; r++) { // resolution levels
1573: if (c >= mdl.length)
1574: continue;
1575: if (r > mdl[c])
1576: continue;
1577: nextPrec[c] = new int[mdl[c] + 1];
1578: if (lys[c] != null && r < lys[c].length
1579: && lys[c][r] < minlys) {
1580: minlys = lys[c][r];
1581: }
1582: p = pktDec.getNumPrecinct(c, r) - 1;
1583: for (; p >= 0; p--) {
1584: prec = pktDec.getPrecInfo(c, r, p);
1585: if (prec.rgulx != tx0) {
1586: if (prec.rgulx < minx)
1587: minx = prec.rgulx;
1588: if (prec.rgulx > maxx)
1589: maxx = prec.rgulx;
1590: }
1591: if (prec.rguly != ty0) {
1592: if (prec.rguly < miny)
1593: miny = prec.rguly;
1594: if (prec.rguly > maxy)
1595: maxy = prec.rguly;
1596: }
1597:
1598: if (nPrec == 0) {
1599: gcd_x = prec.rgw;
1600: gcd_y = prec.rgh;
1601: } else {
1602: gcd_x = MathUtil.gcd(gcd_x, prec.rgw);
1603: gcd_y = MathUtil.gcd(gcd_y, prec.rgh);
1604: }
1605: nPrec++;
1606: } // precincts
1607: } // resolution levels
1608: } // components
1609:
1610: if (nPrec == 0) {
1611: throw new Error("Image cannot have no precinct");
1612: }
1613:
1614: int pyend = (maxy - miny) / gcd_y + 1;
1615: int pxend = (maxx - minx) / gcd_x + 1;
1616: int hlen, plen;
1617: int start;
1618: boolean status = false;
1619: int lastByte = firstPackOff[t][curTilePart]
1620: + tilePartLen[t][curTilePart] - 1
1621: - tilePartHeadLen[t][curTilePart];
1622: int numLayers = ((Integer) decSpec.nls.getTileDef(t))
1623: .intValue();
1624: String strInfo = printInfo ? "Tile " + getTileIdx()
1625: + " (tile-part:" + curTilePart
1626: + "): offset, length, header length\n" : null;
1627: boolean pph = false;
1628: if (((Boolean) decSpec.pphs.getTileDef(t)).booleanValue()) {
1629: pph = true;
1630: }
1631:
1632: int y = ty0;
1633: int x = tx0;
1634: for (int py = 0; py <= pyend; py++) { // Vertical precincts
1635: for (int px = 0; px <= pxend; px++) { // Horiz. precincts
1636: for (int c = comps; c < compe; c++) { // Components
1637: if (c >= mdl.length)
1638: continue;
1639: for (int r = ress; r < rese; r++) { // Resolution levels
1640: if (r > mdl[c])
1641: continue;
1642: if (nextPrec[c][r] >= pktDec.getNumPrecinct(c,
1643: r)) {
1644: continue;
1645: }
1646: prec = pktDec.getPrecInfo(c, r, nextPrec[c][r]);
1647: if ((prec.rgulx != x) || (prec.rguly != y)) {
1648: continue;
1649: }
1650: for (int l = minlys; l < lye; l++) { // Layers
1651: if (r >= lys[c].length)
1652: continue;
1653: if (l < lys[c][r] || l >= numLayers)
1654: continue;
1655:
1656: start = in.getPos();
1657:
1658: // If packed packet headers are used, there is no
1659: // need to check that there are bytes enough to
1660: // read header
1661: if (pph) {
1662: pktDec.readPktHead(l, r, c,
1663: nextPrec[c][r], cbI[c][r],
1664: nBytes);
1665: }
1666: // Read SOP marker segment if necessary
1667: status = pktDec.readSOPMarker(nBytes,
1668: nextPrec[c][r], c, r);
1669:
1670: if (status) {
1671: if (printInfo) {
1672: FacilityManager.getMsgLogger()
1673: .printmsg(MsgLogger.INFO,
1674: strInfo);
1675: }
1676: return true;
1677: }
1678:
1679: if (!pph) {
1680: status = pktDec.readPktHead(l, r, c,
1681: nextPrec[c][r], cbI[c][r],
1682: nBytes);
1683: }
1684:
1685: if (status) {
1686: if (printInfo) {
1687: FacilityManager.getMsgLogger()
1688: .printmsg(MsgLogger.INFO,
1689: strInfo);
1690: }
1691: return true;
1692: }
1693:
1694: // Store packet's head length
1695: hlen = in.getPos() - start;
1696: pktHL.addElement(new Integer(hlen));
1697:
1698: // Reads packet's body
1699: status = pktDec.readPktBody(l, r, c,
1700: nextPrec[c][r], cbI[c][r], nBytes);
1701: plen = in.getPos() - start;
1702: if (printInfo)
1703: strInfo += " Pkt l=" + l + ",r=" + r
1704: + ",c=" + c + ",p="
1705: + nextPrec[c][r] + ": " + start
1706: + ", " + plen + ", " + hlen
1707: + "\n";
1708:
1709: if (status) {
1710: if (printInfo) {
1711: FacilityManager.getMsgLogger()
1712: .printmsg(MsgLogger.INFO,
1713: strInfo);
1714: }
1715: return true;
1716: }
1717:
1718: } // layers
1719: nextPrec[c][r]++;
1720: } // Resolution levels
1721: } // Components
1722: if (px != pxend) {
1723: x = minx + px * gcd_x;
1724: } else {
1725: x = tx0;
1726: }
1727: } // Horizontal precincts
1728: if (py != pyend) {
1729: y = miny + py * gcd_y;
1730: } else {
1731: y = ty0;
1732: }
1733: } // Vertical precincts
1734:
1735: if (printInfo) {
1736: FacilityManager.getMsgLogger().printmsg(MsgLogger.INFO,
1737: strInfo);
1738: }
1739: return false; // Decoding rate was not reached
1740: }
1741:
1742: /**
1743: * Reads packets of the current tile according to the
1744: * component-position-resolution-layer progressiveness.
1745: *
1746: * @param lys Index of the first layer for each component and resolution.
1747: *
1748: * @param lye Index of the last layer.
1749: *
1750: * @param ress Index of the first resolution level.
1751: *
1752: * @param rese Index of the last resolution level.
1753: *
1754: * @param comps Index of the first component.
1755: *
1756: * @param compe Index of the last component.
1757: *
1758: * @return True if rate has been reached.
1759: * */
1760: private boolean readCompPosResLy(int lys[][], int lye, int ress,
1761: int rese, int comps, int compe) throws IOException {
1762: Point nTiles = getNumTiles(null);
1763: Point tileI = getTile(null);
1764: int x0siz = hd.getImgULX();
1765: int y0siz = hd.getImgULY();
1766: int xsiz = x0siz + hd.getImgWidth();
1767: int ysiz = y0siz + hd.getImgHeight();
1768: int xt0siz = getTilePartULX();
1769: int yt0siz = getTilePartULY();
1770: int xtsiz = getNomTileWidth();
1771: int ytsiz = getNomTileHeight();
1772: int tx0 = (tileI.x == 0) ? x0siz : xt0siz + tileI.x * xtsiz;
1773: int ty0 = (tileI.y == 0) ? y0siz : yt0siz + tileI.y * ytsiz;
1774: int tx1 = (tileI.x != nTiles.x - 1) ? xt0siz + (tileI.x + 1)
1775: * xtsiz : xsiz;
1776: int ty1 = (tileI.y != nTiles.y - 1) ? yt0siz + (tileI.y + 1)
1777: * ytsiz : ysiz;
1778:
1779: // Get precinct information (number,distance between two consecutive
1780: // precincts in the reference grid) in each component and resolution
1781: // level
1782: int t = getTileIdx(); // Current tile index
1783: PrecInfo prec; // temporary variable
1784: int p; // Current precinct index
1785: int gcd_x = 0; // Horiz. distance between 2 precincts in the ref. grid
1786: int gcd_y = 0; // Vert. distance between 2 precincts in the ref. grid
1787: int nPrec = 0; // Total number of found precincts
1788: int[][] nextPrec = new int[compe][]; // Next precinct index in each
1789: // component and resolution level
1790: int minlys = 100000; // minimum layer start index of each component
1791: int minx = tx1; // Horiz. offset of the second precinct in the
1792: // reference grid
1793: int miny = ty1; // Vert. offset of the second precinct in the
1794: // reference grid.
1795: int maxx = tx0; // Max. horiz. offset of precincts in the ref. grid
1796: int maxy = ty0; // Max. vert. offset of precincts in the ref. grid
1797: Point numPrec;
1798: for (int c = comps; c < compe; c++) { // components
1799: for (int r = ress; r < rese; r++) { // resolution levels
1800: if (c >= mdl.length)
1801: continue;
1802: if (r > mdl[c])
1803: continue;
1804: nextPrec[c] = new int[mdl[c] + 1];
1805: if (lys[c] != null && r < lys[c].length
1806: && lys[c][r] < minlys) {
1807: minlys = lys[c][r];
1808: }
1809: p = pktDec.getNumPrecinct(c, r) - 1;
1810: for (; p >= 0; p--) {
1811: prec = pktDec.getPrecInfo(c, r, p);
1812: if (prec.rgulx != tx0) {
1813: if (prec.rgulx < minx)
1814: minx = prec.rgulx;
1815: if (prec.rgulx > maxx)
1816: maxx = prec.rgulx;
1817: }
1818: if (prec.rguly != ty0) {
1819: if (prec.rguly < miny)
1820: miny = prec.rguly;
1821: if (prec.rguly > maxy)
1822: maxy = prec.rguly;
1823: }
1824:
1825: if (nPrec == 0) {
1826: gcd_x = prec.rgw;
1827: gcd_y = prec.rgh;
1828: } else {
1829: gcd_x = MathUtil.gcd(gcd_x, prec.rgw);
1830: gcd_y = MathUtil.gcd(gcd_y, prec.rgh);
1831: }
1832: nPrec++;
1833: } // precincts
1834: } // resolution levels
1835: } // components
1836:
1837: if (nPrec == 0) {
1838: throw new Error("Image cannot have no precinct");
1839: }
1840:
1841: int pyend = (maxy - miny) / gcd_y + 1;
1842: int pxend = (maxx - minx) / gcd_x + 1;
1843: int hlen, plen;
1844: int start;
1845: boolean status = false;
1846: int lastByte = firstPackOff[t][curTilePart]
1847: + tilePartLen[t][curTilePart] - 1
1848: - tilePartHeadLen[t][curTilePart];
1849: int numLayers = ((Integer) decSpec.nls.getTileDef(t))
1850: .intValue();
1851: String strInfo = printInfo ? "Tile " + getTileIdx()
1852: + " (tile-part:" + curTilePart
1853: + "): offset, length, header length\n" : null;
1854: boolean pph = false;
1855: if (((Boolean) decSpec.pphs.getTileDef(t)).booleanValue()) {
1856: pph = true;
1857: }
1858:
1859: int x, y;
1860: for (int c = comps; c < compe; c++) { // components
1861: if (c >= mdl.length)
1862: continue;
1863: y = ty0;
1864: x = tx0;
1865: for (int py = 0; py <= pyend; py++) { // Vertical precincts
1866: for (int px = 0; px <= pxend; px++) { // Horiz. precincts
1867: for (int r = ress; r < rese; r++) { // Resolution levels
1868: if (r > mdl[c])
1869: continue;
1870: if (nextPrec[c][r] >= pktDec.getNumPrecinct(c,
1871: r)) {
1872: continue;
1873: }
1874: prec = pktDec.getPrecInfo(c, r, nextPrec[c][r]);
1875: if ((prec.rgulx != x) || (prec.rguly != y)) {
1876: continue;
1877: }
1878:
1879: for (int l = minlys; l < lye; l++) { // Layers
1880: if (r >= lys[c].length)
1881: continue;
1882: if (l < lys[c][r])
1883: continue;
1884:
1885: start = in.getPos();
1886:
1887: // If packed packet headers are used, there is no
1888: // need to check that there are bytes enough to
1889: // read header
1890: if (pph) {
1891: pktDec.readPktHead(l, r, c,
1892: nextPrec[c][r], cbI[c][r],
1893: nBytes);
1894: }
1895: // If we are about to read outside of tile-part,
1896: // skip to next tile-part
1897: if (start > lastByte
1898: && curTilePart < firstPackOff[t].length - 1) {
1899: curTilePart++;
1900: in.seek(firstPackOff[t][curTilePart]);
1901: lastByte = in.getPos()
1902: + tilePartLen[t][curTilePart]
1903: - 1
1904: - tilePartHeadLen[t][curTilePart];
1905: }
1906:
1907: // Read SOP marker segment if necessary
1908: status = pktDec.readSOPMarker(nBytes,
1909: nextPrec[c][r], c, r);
1910:
1911: if (status) {
1912: if (printInfo) {
1913: FacilityManager.getMsgLogger()
1914: .printmsg(MsgLogger.INFO,
1915: strInfo);
1916: }
1917: return true;
1918: }
1919:
1920: if (!pph) {
1921: status = pktDec.readPktHead(l, r, c,
1922: nextPrec[c][r], cbI[c][r],
1923: nBytes);
1924: }
1925:
1926: if (status) {
1927: if (printInfo) {
1928: FacilityManager.getMsgLogger()
1929: .printmsg(MsgLogger.INFO,
1930: strInfo);
1931: }
1932: return true;
1933: }
1934:
1935: // Store packet's head length
1936: hlen = in.getPos() - start;
1937: pktHL.addElement(new Integer(hlen));
1938:
1939: // Reads packet's body
1940: status = pktDec.readPktBody(l, r, c,
1941: nextPrec[c][r], cbI[c][r], nBytes);
1942: plen = in.getPos() - start;
1943: if (printInfo)
1944: strInfo += " Pkt l=" + l + ",r=" + r
1945: + ",c=" + c + ",p="
1946: + nextPrec[c][r] + ": " + start
1947: + ", " + plen + ", " + hlen
1948: + "\n";
1949:
1950: if (status) {
1951: if (printInfo) {
1952: FacilityManager.getMsgLogger()
1953: .printmsg(MsgLogger.INFO,
1954: strInfo);
1955: }
1956: return true;
1957: }
1958:
1959: } // layers
1960: nextPrec[c][r]++;
1961: } // Resolution levels
1962: if (px != pxend) {
1963: x = minx + px * gcd_x;
1964: } else {
1965: x = tx0;
1966: }
1967: } // Horizontal precincts
1968: if (py != pyend) {
1969: y = miny + py * gcd_y;
1970: } else {
1971: y = ty0;
1972: }
1973: } // Vertical precincts
1974: } // components
1975:
1976: if (printInfo) {
1977: FacilityManager.getMsgLogger().printmsg(MsgLogger.INFO,
1978: strInfo);
1979: }
1980: return false; // Decoding rate was not reached
1981: }
1982:
1983: /**
1984: * Finish initialization of members for specified tile, reads packets head
1985: * of each tile and keeps location of each code-block's codewords. The
1986: * last 2 tasks are done by calling specific methods of PktDecoder.
1987: *
1988: * <p>Then, if a parsing output rate is defined, it keeps information of
1989: * first layers only. This operation simulates a creation of a
1990: * layer-resolution-component progressive bit-stream which will be next
1991: * truncated and decoded.</p>
1992: *
1993: * @param t Tile index
1994: *
1995: * @see PktDecoder
1996: * */
1997: private void readTilePkts(int t) throws IOException {
1998: pktHL = new Vector();
1999:
2000: int oldNBytes = nBytes[t];
2001:
2002: // Number of layers
2003: int nl = ((Integer) decSpec.nls.getTileDef(t)).intValue();
2004:
2005: // If packed packet headers was used, get the packet headers for this
2006: // tile
2007: if (((Boolean) decSpec.pphs.getTileDef(t)).booleanValue()) {
2008: // Gets packed headers as separate input stream
2009: ByteArrayInputStream pphbais = hd.getPackedPktHead(t);
2010:
2011: // Restarts PktDecoder instance
2012: cbI = pktDec.restart(nc, mdl, nl, cbI, true, pphbais);
2013: } else {
2014: // Restarts PktDecoder instance
2015: cbI = pktDec.restart(nc, mdl, nl, cbI, false, null);
2016: }
2017:
2018: // Reads packets of the tile according to the progression order
2019: int[][] pocSpec = ((int[][]) decSpec.pcs.getTileDef(t));
2020: int nChg = (pocSpec == null) ? 1 : pocSpec.length;
2021:
2022: // Create an array containing information about changes (progression
2023: // order type, layers index start, layer index end, resolution level
2024: // start, resolution level end, component index start, component index
2025: // end). There is one row per progresion order
2026: int[][] change = new int[nChg][6];
2027: int idx = 0; // Index of the current progression order
2028:
2029: change[0][1] = 0; // layer start
2030:
2031: if (pocSpec == null) {
2032: change[idx][0] = ((Integer) decSpec.pos.getTileDef(t))
2033: .intValue();
2034: // Progression type found in COx marker segments
2035: change[idx][1] = nl; // Layer index end
2036: change[idx][2] = 0; // resolution level start
2037: change[idx][3] = decSpec.dls.getMaxInTile(t) + 1; // res. level end
2038: change[idx][4] = 0; // Component index start
2039: change[idx][5] = nc; // Component index end
2040: } else {
2041: for (idx = 0; idx < nChg; idx++) {
2042: change[idx][0] = pocSpec[idx][5];
2043: change[idx][1] = pocSpec[idx][2]; // layer end
2044: change[idx][2] = pocSpec[idx][0]; // res. lev. start
2045: change[idx][3] = pocSpec[idx][3]; // res. lev. end
2046: change[idx][4] = pocSpec[idx][1]; // Comp. index start
2047: change[idx][5] = pocSpec[idx][4]; // Comp. index end
2048: }
2049: }
2050:
2051: // Seeks to the first packet of the first tile-part
2052: try {
2053: // If in truncation mode, the first tile-part may be beyond the
2054: // target decoding rate. In this case, the offset of the first
2055: // packet is not defined.
2056: if (isTruncMode && firstPackOff == null
2057: || firstPackOff[t] == null) {
2058: return;
2059: }
2060: in.seek(firstPackOff[t][0]);
2061: } catch (EOFException e) {
2062: FacilityManager.getMsgLogger().printmsg(MsgLogger.WARNING,
2063: "Codestream truncated in tile " + t);
2064: return;
2065: }
2066:
2067: curTilePart = 0;
2068:
2069: // Start and end indexes for layers, resolution levels and components.
2070: int lye, ress, rese, comps, compe;
2071: boolean status = false;
2072: int nb = nBytes[t];
2073: int[][] lys = new int[nc][];
2074: for (int c = 0; c < nc; c++) {
2075: lys[c] = new int[((Integer) decSpec.dls
2076: .getTileCompVal(t, c)).intValue() + 1];
2077: }
2078:
2079: try {
2080: for (int chg = 0; chg < nChg; chg++) {
2081:
2082: lye = change[chg][1];
2083: ress = change[chg][2];
2084: rese = change[chg][3];
2085: comps = change[chg][4];
2086: compe = change[chg][5];
2087:
2088: switch (change[chg][0]) {
2089: case LY_RES_COMP_POS_PROG:
2090: status = readLyResCompPos(lys, lye, ress, rese,
2091: comps, compe);
2092: break;
2093: case RES_LY_COMP_POS_PROG:
2094: status = readResLyCompPos(lys, lye, ress, rese,
2095: comps, compe);
2096: break;
2097: case RES_POS_COMP_LY_PROG:
2098: status = readResPosCompLy(lys, lye, ress, rese,
2099: comps, compe);
2100: break;
2101: case POS_COMP_RES_LY_PROG:
2102: status = readPosCompResLy(lys, lye, ress, rese,
2103: comps, compe);
2104: break;
2105: case COMP_POS_RES_LY_PROG:
2106: status = readCompPosResLy(lys, lye, ress, rese,
2107: comps, compe);
2108: break;
2109: default:
2110: throw new IllegalArgumentException(
2111: "Not recognized " + "progression type");
2112: }
2113:
2114: // Update next first layer index
2115: for (int c = comps; c < compe; c++) {
2116: if (c >= lys.length)
2117: continue;
2118: for (int r = ress; r < rese; r++) {
2119: if (r >= lys[c].length)
2120: continue;
2121: lys[c][r] = lye;
2122: }
2123: }
2124:
2125: if (status || usePOCQuit) {
2126: break;
2127: }
2128: }
2129: } catch (EOFException e) {
2130: // Should never happen. Truncated codestream are normally found by
2131: // the class constructor
2132: throw e;
2133: }
2134:
2135: // In truncation mode, update the number of read bytes
2136: if (isTruncMode) {
2137: anbytes += nb - nBytes[t];
2138:
2139: // If truncation rate is reached
2140: if (status) {
2141: nBytes[t] = 0;
2142: }
2143: } else if (nBytes[t] < (totTileLen[t] - totTileHeadLen[t])) {
2144: // In parsing mode, if there is not enough rate to entirely read the
2145: // tile. Then, parses the bit stream so as to create a virtual
2146: // layer-resolution-component progressive bit stream that will be
2147: // truncated and decoded afterwards.
2148: CBlkInfo cb;
2149:
2150: // Systematicaly reject all remaining code-blocks if one
2151: // code-block, at least, is refused.
2152: boolean reject;
2153: // Stop reading any data from the bit stream
2154: boolean stopCount = false;
2155: // Length of each packet's head (in an array)
2156: int[] pktHeadLen = new int[pktHL.size()];
2157: for (int i = pktHL.size() - 1; i >= 0; i--) {
2158: pktHeadLen[i] = ((Integer) pktHL.elementAt(i))
2159: .intValue();
2160: }
2161:
2162: // Parse each code-block, layer per layer until nBytes[t] is
2163: // reached
2164: reject = false;
2165: for (int l = 0; l < nl; l++) { // layers
2166: if (cbI == null)
2167: continue;
2168: int nc = cbI.length;
2169:
2170: int mres = 0;
2171: for (int c = 0; c < nc; c++) {
2172: if (cbI[c] != null && cbI[c].length > mres)
2173: mres = cbI[c].length;
2174: }
2175: for (int r = 0; r < mres; r++) { // resolutions
2176:
2177: int msub = 0;
2178: for (int c = 0; c < nc; c++) {
2179: if (cbI[c] != null && cbI[c][r] != null
2180: && cbI[c][r].length > msub)
2181: msub = cbI[c][r].length;
2182: }
2183: for (int s = 0; s < msub; s++) { // subbands
2184: // Only LL subband resolution level 0
2185: if (r == 0 && s != 0) {
2186: continue;
2187: } else if (r != 0 && s == 0) {
2188: // No LL subband in resolution level > 0
2189: continue;
2190: }
2191:
2192: int mnby = 0;
2193: for (int c = 0; c < nc; c++) {
2194: if (cbI[c] != null && cbI[c][r] != null
2195: && cbI[c][r][s] != null
2196: && cbI[c][r][s].length > mnby)
2197: mnby = cbI[c][r][s].length;
2198: }
2199: for (int m = 0; m < mnby; m++) {
2200:
2201: int mnbx = 0;
2202: for (int c = 0; c < nc; c++) {
2203: if (cbI[c] != null
2204: && cbI[c][r] != null
2205: && cbI[c][r][s] != null
2206: && cbI[c][r][s][m] != null
2207: && cbI[c][r][s][m].length > mnbx)
2208: mnbx = cbI[c][r][s][m].length;
2209: }
2210: for (int n = 0; n < mnbx; n++) {
2211:
2212: for (int c = 0; c < nc; c++) {
2213:
2214: if (cbI[c] == null
2215: || cbI[c][r] == null
2216: || cbI[c][r][s] == null
2217: || cbI[c][r][s][m] == null
2218: || cbI[c][r][s][m][n] == null) {
2219: continue;
2220: }
2221: cb = cbI[c][r][s][m][n];
2222:
2223: // If no code-block has been refused until
2224: // now
2225: if (!reject) {
2226: // Rate is to low to allow reading of
2227: // packet's head
2228: if (nBytes[t] < pktHeadLen[cb.pktIdx[l]]) {
2229: // Stop parsing
2230: stopCount = true;
2231: // Reject all next
2232: // code-blocks
2233: reject = true;
2234: } else {
2235: // Rate is enough to read packet's
2236: // head
2237: if (!stopCount) {
2238: //If parsing was not stopped
2239: //Takes into account packet's
2240: //head length
2241: nBytes[t] -= pktHeadLen[cb.pktIdx[l]];
2242: anbytes += pktHeadLen[cb.pktIdx[l]];
2243: // Set packet's head length to
2244: // 0, so that it won't be
2245: // taken into account next
2246: // time
2247: pktHeadLen[cb.pktIdx[l]] = 0;
2248: }
2249: }
2250: }
2251: // Code-block has no data in this layer
2252: if (cb.len[l] == 0) {
2253: continue;
2254: }
2255:
2256: // Accepts code-block if length is enough,
2257: // if this code-block was not refused in a
2258: // previous layer and if no code-block was
2259: // refused in current component
2260: if (cb.len[l] < nBytes[t]
2261: && !reject) {
2262: nBytes[t] -= cb.len[l];
2263: anbytes += cb.len[l];
2264: } else {
2265: // Refuses code-block
2266: // Forgets code-block's data
2267: cb.len[l] = cb.off[l] = cb.ntp[l] = 0;
2268: // Refuses all other code-block in
2269: // current and next component
2270: reject = true;
2271: }
2272:
2273: } // End loop on components
2274: } // End loop on horiz. code-blocks
2275: } // End loop on vert. code-blocks
2276: } // End loop on subbands
2277: } // End loop on resolutions
2278: } // End loop on layers
2279: } else {
2280: // No parsing for this tile, adds tile's body to the total
2281: // number of read bytes.
2282: anbytes += totTileLen[t] - totTileHeadLen[t];
2283: if (t < getNumTiles() - 1) {
2284: nBytes[t + 1] += nBytes[t]
2285: - (totTileLen[t] - totTileHeadLen[t]);
2286: }
2287: }
2288:
2289: // In this method nBytes[t] might be changed. This change will affect
2290: // to decode this tile next time. So cache the old nByte[t] and
2291: // recover it here. -- Qinghuai Gao
2292: nBytes[t] = oldNBytes;
2293: }
2294:
2295: /**
2296: * Changes the current tile, given the new indexes. An
2297: * IllegalArgumentException is thrown if the indexes do not correspond to
2298: * a valid tile.
2299: *
2300: * @param x The horizontal indexes the tile.
2301: *
2302: * @param y The vertical indexes of the new tile.
2303: * */
2304: public void setTile(int x, int y) {
2305:
2306: int i; // counter
2307: // Check validity of tile indexes
2308: if (x < 0 || y < 0 || x >= ntX || y >= ntY) {
2309: throw new IllegalArgumentException();
2310: }
2311: int t = (y * ntX + x);
2312: try {
2313: initTile(t);
2314: } catch (IOException ioe) {
2315: // XXX Do something!
2316: }
2317:
2318: // Reset number of read bytes if needed
2319: if (t == 0) {
2320: anbytes = headLen;
2321: if (!isTruncMode) {
2322: anbytes += 2;
2323: }
2324: // Restore values of nBytes
2325: for (int tIdx = 0; tIdx < nt; tIdx++) {
2326: nBytes[tIdx] = baknBytes[tIdx];
2327: }
2328: }
2329:
2330: // Set the new current tile
2331: ctX = x;
2332: ctY = y;
2333: // Calculate tile relative points
2334: int ctox = (x == 0) ? ax : px + x * ntW;
2335: int ctoy = (y == 0) ? ay : py + y * ntH;
2336: for (i = nc - 1; i >= 0; i--) {
2337: culx[i] = (ctox + hd.getCompSubsX(i) - 1)
2338: / hd.getCompSubsX(i);
2339: culy[i] = (ctoy + hd.getCompSubsY(i) - 1)
2340: / hd.getCompSubsY(i);
2341: offX[i] = (px + x * ntW + hd.getCompSubsX(i) - 1)
2342: / hd.getCompSubsX(i);
2343: offY[i] = (py + y * ntH + hd.getCompSubsY(i) - 1)
2344: / hd.getCompSubsY(i);
2345: }
2346:
2347: // Initialize subband tree and number of resolution levels
2348: subbTrees = new SubbandSyn[nc];
2349: mdl = new int[nc];
2350: derived = new boolean[nc];
2351: params = new StdDequantizerParams[nc];
2352: gb = new int[nc];
2353:
2354: for (int c = 0; c < nc; c++) {
2355: derived[c] = decSpec.qts.isDerived(t, c);
2356: params[c] = (StdDequantizerParams) decSpec.qsss
2357: .getTileCompVal(t, c);
2358: gb[c] = ((Integer) decSpec.gbs.getTileCompVal(t, c))
2359: .intValue();
2360: mdl[c] = ((Integer) decSpec.dls.getTileCompVal(t, c))
2361: .intValue();
2362:
2363: subbTrees[c] = new SubbandSyn(
2364: getTileCompWidth(t, c, mdl[c]), getTileCompHeight(
2365: t, c, mdl[c]), getResULX(c, mdl[c]),
2366: getResULY(c, mdl[c]), mdl[c], decSpec.wfs
2367: .getHFilters(t, c), decSpec.wfs
2368: .getVFilters(t, c));
2369: initSubbandsFields(c, subbTrees[c]);
2370: }
2371:
2372: // Read tile's packets
2373: try {
2374: readTilePkts(t);
2375: } catch (IOException e) {
2376: e.printStackTrace();
2377: throw new Error("IO Error when reading tile " + x + " x "
2378: + y);
2379: }
2380: }
2381:
2382: /**
2383: * Advances to the next tile, in standard scan-line order (by rows
2384: * then columns). An NoNextElementException is thrown if the
2385: * current tile is the last one (i.e. there is no next tile).
2386: * */
2387: public void nextTile() {
2388: if (ctX == ntX - 1 && ctY == ntY - 1) { // Already at last tile
2389: throw new NoNextElementException();
2390: } else if (ctX < ntX - 1) { // If not at end of current tile line
2391: setTile(ctX + 1, ctY);
2392: } else { // Go to first tile at next line
2393: setTile(0, ctY + 1);
2394: }
2395: }
2396:
2397: /**
2398: * Returns the specified coded code-block, for the specified component, in
2399: * the current tile. The first layer to return is indicated by 'fl'. The
2400: * number of layers that is returned depends on 'nl' and the amount of
2401: * available data.
2402: *
2403: * <p>The argument 'fl' is to be used by subsequent calls to this method
2404: * for the same code-block. In this way supplemental data can be retrieved
2405: * at a later time. The fact that data from more than one layer can be
2406: * returned means that several packets from the same code-block, of the
2407: * same component, and the same tile, have been concatenated.</p>
2408: *
2409: * <p>The returned compressed code-block can have its progressive
2410: * attribute set. If this attribute is set it means that more data can be
2411: * obtained by subsequent calls to this method (subject to transmission
2412: * delays, etc). If the progressive attribute is not set it means that the
2413: * returned data is all the data that can be obtained for the specified
2414: * code-block.</p>
2415: *
2416: * <p>The compressed code-block is uniquely specified by the current tile,
2417: * the component (identified by 'c'), the subband (indentified by 'sb')
2418: * and the code-block vertical and horizontal indexes 'n' and 'm'.</p>
2419: *
2420: * <p>The 'ulx' and 'uly' members of the returned 'DecLyrdCBlk' object
2421: * contain the coordinates of the top-left corner of the block, with
2422: * respect to the tile, not the subband.</p>
2423: *
2424: * @param c The index of the component, from 0 to N-1.
2425: *
2426: * @param m The vertical index of the code-block to return, in the
2427: * specified subband.
2428: *
2429: * @param n The horizontal index of the code-block to return, in the
2430: * specified subband.
2431: *
2432: * @param sb The subband in whic the requested code-block is.
2433: *
2434: * @param fl The first layer to return.
2435: *
2436: * @param nl The number of layers to return, if negative all available
2437: * layers are returned, starting at 'fl'.
2438: *
2439: * @param ccb If not null this object is used to return the compressed
2440: * code-block. If null a new object is created and returned. If the data
2441: * array in ccb is not null then it can be reused to return the compressed
2442: * data.
2443: * @return The compressed code-block, with a certain number of layers
2444: * determined by the available data and 'nl'.
2445: * */
2446: public DecLyrdCBlk getCodeBlock(int c, int m, int n, SubbandSyn sb,
2447: int fl, int nl, DecLyrdCBlk ccb) {
2448:
2449: int t = getTileIdx();
2450: CBlkInfo rcb; // requested code-block
2451: int r = sb.resLvl; // Resolution level
2452: int s = sb.sbandIdx; // Subband index
2453: int tpidx;
2454: int passtype;
2455:
2456: // Number of layers
2457: int numLayers = ((Integer) decSpec.nls.getTileDef(t))
2458: .intValue();
2459: int options = ((Integer) decSpec.ecopts.getTileCompVal(t, c))
2460: .intValue();
2461: if (nl < 0) {
2462: nl = numLayers - fl + 1;
2463: }
2464:
2465: // If the l quit condition is used, Make sure that no layer
2466: // after lquit is returned
2467: if (lQuit != -1 && fl + nl > lQuit) {
2468: nl = lQuit - fl;
2469: }
2470:
2471: // Check validity of resquested resolution level (according to the
2472: // "-res" option).
2473: int maxdl = getSynSubbandTree(t, c).resLvl;
2474: /* XXX Suppress error check for speed performance reasons.
2475: if(r>targetRes+maxdl-decSpec.dls.getMin()) {
2476: throw new Error("JJ2000 error: requesting a code-block "+
2477: "disallowed by the '-res' option.");
2478: }
2479: */
2480:
2481: // Check validity of all the arguments
2482: try {
2483: rcb = cbI[c][r][s][m][n];
2484:
2485: if (fl < 1 || fl > numLayers || fl + nl - 1 > numLayers) {
2486: throw new IllegalArgumentException();
2487: }
2488: } catch (ArrayIndexOutOfBoundsException e) {
2489: throw new IllegalArgumentException("Code-block (t:" + t
2490: + ", c:" + c + ", r:" + r + ", s:" + s + ", " + m
2491: + "x" + +n + ") not found in codestream");
2492: } catch (NullPointerException e) {
2493: throw new IllegalArgumentException("Code-block (t:" + t
2494: + ", c:" + c + ", r:" + r + ", s:" + s + ", " + m
2495: + "x" + n + ") not found in bit stream");
2496: }
2497:
2498: // Create DecLyrdCBlk object if necessary
2499: if (ccb == null) {
2500: ccb = new DecLyrdCBlk();
2501: }
2502: ccb.m = m;
2503: ccb.n = n;
2504: ccb.nl = 0;
2505: ccb.dl = 0;
2506: ccb.nTrunc = 0;
2507:
2508: if (rcb == null) {
2509: // This code-block was skipped when reading. Returns no data
2510: ccb.skipMSBP = 0;
2511: ccb.prog = false;
2512: ccb.w = ccb.h = ccb.ulx = ccb.uly = 0;
2513: return ccb;
2514: }
2515:
2516: // ccb initialization
2517: ccb.skipMSBP = rcb.msbSkipped;
2518: ccb.ulx = rcb.ulx;
2519: ccb.uly = rcb.uly;
2520: ccb.w = rcb.w;
2521: ccb.h = rcb.h;
2522: ccb.ftpIdx = 0;
2523:
2524: // Search for index of first truncation point (first layer where
2525: // length of data is not zero)
2526: int l = 0;
2527: while ((l < rcb.len.length) && (rcb.len[l] == 0)) {
2528: ccb.ftpIdx += rcb.ntp[l];
2529: l++;
2530: }
2531:
2532: // Calculate total length, number of included layer and number of
2533: // truncation points
2534: for (l = fl - 1; l < fl + nl - 1; l++) {
2535: ccb.nl++;
2536: ccb.dl += rcb.len[l];
2537: ccb.nTrunc += rcb.ntp[l];
2538: }
2539:
2540: // Calculate number of terminated segments
2541: int nts;
2542: if ((options & OPT_TERM_PASS) != 0) {
2543: // Regular termination in use One segment per pass
2544: // (i.e. truncation point)
2545: nts = ccb.nTrunc - ccb.ftpIdx;
2546: } else if ((options & OPT_BYPASS) != 0) {
2547: // Selective arithmetic coding bypass mode in use, but no regular
2548: // termination: 1 segment upto the end of the last pass of the 4th
2549: // most significant bit-plane, and, in each following bit-plane,
2550: // one segment upto the end of the 2nd pass and one upto the end
2551: // of the 3rd pass.
2552:
2553: if (ccb.nTrunc <= FIRST_BYPASS_PASS_IDX) {
2554: nts = 1;
2555: } else {
2556: nts = 1;
2557: // Adds one for each terminated pass
2558: for (tpidx = ccb.ftpIdx; tpidx < ccb.nTrunc; tpidx++) {
2559: if (tpidx >= FIRST_BYPASS_PASS_IDX - 1) {
2560: passtype = (tpidx + NUM_EMPTY_PASSES_IN_MS_BP)
2561: % NUM_PASSES;
2562: if (passtype == 1 || passtype == 2) {
2563: // lazy pass just before MQ pass or MQ pass just
2564: // before lazy pass => terminated
2565: nts++;
2566: }
2567: }
2568: }
2569: }
2570: } else {
2571: // Nothing special in use, just one terminated segment
2572: nts = 1;
2573: }
2574:
2575: // ccb.data creation
2576: if (ccb.data == null || ccb.data.length < ccb.dl) {
2577: ccb.data = new byte[ccb.dl];
2578: }
2579:
2580: // ccb.tsLengths creation
2581: if (nts > 1
2582: && (ccb.tsLengths == null || ccb.tsLengths.length < nts)) {
2583: ccb.tsLengths = new int[nts];
2584: } else if (nts > 1
2585: && (options & (OPT_BYPASS | OPT_TERM_PASS)) == OPT_BYPASS) {
2586: ArrayUtil.intArraySet(ccb.tsLengths, 0);
2587: }
2588:
2589: // Fill ccb with compressed data
2590: int dataIdx = -1;
2591: tpidx = ccb.ftpIdx;
2592: int ctp = ccb.ftpIdx; // Cumulative number of truncation
2593: // point for the current layer layer
2594: int tsidx = 0;
2595: int j;
2596:
2597: for (l = fl - 1; l < fl + nl - 1; l++) {
2598: ctp += rcb.ntp[l];
2599: // No data in this layer
2600: if (rcb.len[l] == 0)
2601: continue;
2602:
2603: // Read data
2604: // NOTE: we should never get an EOFException here since all
2605: // data is checked to be within the file.
2606: try {
2607: in.seek(rcb.off[l]);
2608: in.readFully(ccb.data, dataIdx + 1, rcb.len[l]);
2609: dataIdx += rcb.len[l];
2610: } catch (IOException e) {
2611: JJ2KExceptionHandler.handleException(e);
2612: }
2613:
2614: // Get the terminated segment lengths, if any
2615: if (nts == 1)
2616: continue;
2617: if ((options & OPT_TERM_PASS) != 0) {
2618: // Regular termination => each pass is terminated
2619: for (j = 0; tpidx < ctp; j++, tpidx++) {
2620: if (rcb.segLen[l] != null) {
2621: ccb.tsLengths[tsidx++] = rcb.segLen[l][j];
2622: } else { // Only one terminated segment in packet
2623: ccb.tsLengths[tsidx++] = rcb.len[l];
2624: }
2625: }
2626: } else {
2627: // Lazy coding without regular termination
2628: for (j = 0; tpidx < ctp; tpidx++) {
2629: if (tpidx >= FIRST_BYPASS_PASS_IDX - 1) {
2630: passtype = (tpidx + NUM_EMPTY_PASSES_IN_MS_BP)
2631: % NUM_PASSES;
2632: if (passtype != 0) {
2633: // lazy pass just before MQ pass or MQ
2634: // pass just before lazy pass =>
2635: // terminated
2636: if (rcb.segLen[l] != null) {
2637: ccb.tsLengths[tsidx++] += rcb.segLen[l][j++];
2638: rcb.len[l] -= rcb.segLen[l][j - 1];
2639: } else { // Only one terminated segment in packet
2640: ccb.tsLengths[tsidx++] += rcb.len[l];
2641: rcb.len[l] = 0;
2642: }
2643: }
2644:
2645: }
2646: }
2647:
2648: // Last length in packet always in (either terminated segment
2649: // or contribution to terminated segment)
2650: if (rcb.segLen[l] != null && j < rcb.segLen[l].length) {
2651: ccb.tsLengths[tsidx] += rcb.segLen[l][j];
2652: rcb.len[l] -= rcb.segLen[l][j];
2653: } else { // Only one terminated segment in packet
2654: if (tsidx < nts) {
2655: ccb.tsLengths[tsidx] += rcb.len[l];
2656: rcb.len[l] = 0;
2657: }
2658: }
2659: }
2660: }
2661: if (nts == 1 && ccb.tsLengths != null) {
2662: ccb.tsLengths[0] = ccb.dl;
2663: }
2664:
2665: // Set the progressive flag
2666: int lastlayer = fl + nl - 1;
2667: if (lastlayer < numLayers - 1) {
2668: for (l = lastlayer + 1; l < numLayers; l++) {
2669: // It remains data for this code-block in the bit stream
2670: if (rcb.len[l] != 0) {
2671: ccb.prog = true;
2672: }
2673: }
2674: }
2675:
2676: return ccb;
2677: }
2678:
2679: }
|