jj2000.j2k.codestream.reader.FileBitstreamReaderAgent Maven / Gradle / Ivy
Show all versions of jai-imageio-jpeg2000 Show documentation
/*
* $RCSfile: FileBitstreamReaderAgent.java,v $
* $Revision: 1.4 $
* $Date: 2006/10/05 01:10:31 $
* $State: Exp $
*
* Class: FileBitstreamReaderAgent
*
* Description: Retrieve code-blocks codewords in the bit stream
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package jj2000.j2k.codestream.reader;
import java.awt.Point;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Vector;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import jj2000.j2k.JJ2KExceptionHandler;
import jj2000.j2k.NoNextElementException;
import jj2000.j2k.NotImplementedError;
import jj2000.j2k.codestream.CorruptedCodestreamException;
import jj2000.j2k.codestream.HeaderInfo;
import jj2000.j2k.codestream.Markers;
import jj2000.j2k.codestream.PrecInfo;
import jj2000.j2k.codestream.ProgressionType;
import jj2000.j2k.decoder.DecoderSpecs;
import jj2000.j2k.entropy.StdEntropyCoderOptions;
import jj2000.j2k.entropy.decoder.DecLyrdCBlk;
import jj2000.j2k.io.RandomAccessIO;
import jj2000.j2k.quantization.dequantizer.StdDequantizerParams;
import jj2000.j2k.util.ArrayUtil;
import jj2000.j2k.util.FacilityManager;
import jj2000.j2k.util.MathUtil;
import jj2000.j2k.util.MsgLogger;
import jj2000.j2k.wavelet.synthesis.SubbandSyn;
import com.github.jaiimageio.jpeg2000.impl.J2KImageReadParamJava;
/**
* This class reads the bit stream (with the help of HeaderDecoder for tile
* headers and PktDecoder for packets header and body) and retrives location
* of all code-block's codewords.
*
* Note: All tile-parts headers are read by the constructor whereas packets
* are processed when decoding related tile (when setTile method is called).
*
*
In parsing mode, the reader simulates a virtual layer-resolution
* progressive bit stream with the same truncation points in each code-block,
* whereas in truncation mode, only the first bytes are taken into account (it
* behaves like if it is a real truncated codestream).
*
* @see HeaderDecoder
* @see PktDecoder
* */
public class FileBitstreamReaderAgent extends BitstreamReaderAgent
implements Markers, ProgressionType, StdEntropyCoderOptions{
/** Whether or not the last read Psot value was zero. Only the Psot in the
* last tile-part in the codestream can have such a value. */
private boolean isPsotEqualsZero = true;
/** Reference to the PktDecoder instance */
public PktDecoder pktDec;
/** Reference to the J2KImageReadParamJava instance */
private J2KImageReadParamJava j2krparam;
/** The RandomAccessIO where to get data from */
private RandomAccessIO in;
/** The number of tiles in the image */
private int nt;
/** Offset of the first packet in each tile-part in each tile */
private int[][] firstPackOff;
/**
* Returns the number of tile-part found for a given tile
*
* @param t Tile index
*
* */
public int getNumTileParts(int t) {
if(firstPackOff==null || firstPackOff[t]==null) {
throw new Error("Tile "+t+" not found in input codestream.");
}
return firstPackOff[t].length;
}
/** Number of bytes allocated to each tile. In parsing mode, this number
* is related to the tile length in the codestream whereas in truncation
* mode all the rate is affected to the first tiles. */
private int[] nBytes;
/** Whether or not to print information found in codestream */
private boolean printInfo = false;
/**
* Backup of the number of bytes allocated to each tile. This array is
* used to restore the number of bytes to read in each tile when the
* codestream is read several times (for instance when decoding an R,G,B
* image to three output files)
* */
private int[] baknBytes;
/** Length of each tile-part (written in Psot) */
private int[][] tilePartLen;
/** Total length of each tile */
private int[] totTileLen;
/** Total length of tiles' header */
private int[] totTileHeadLen;
/** First tile part header length*/
private int firstTilePartHeadLen;
/** Total length of all tile parts in all tiles */
private double totAllTileLen;
/** Length of main header */
private int mainHeadLen;
/** Length of main and tile-parts headers */
private int headLen = 0;
/** Length of all tile-part headers */
private int[][] tilePartHeadLen;
/** Length of each packet head found in the tile */
private Vector pktHL;
/** True if truncation mode is used. False if parsing mode */
private boolean isTruncMode;
/** The number of tile-parts that remain to read */
private int remainingTileParts;
/** The number of tile-parts read so far for each tile */
private int[] tilePartsRead;
/** Thetotal number of tile-parts read so far */
private int totTilePartsRead=0;
/** The number of tile-parts in each tile */
private int[] tileParts;
/** The total number of tile-parts in each tile */
private int[] totTileParts;
/** The current tile part being used */
private int curTilePart;
/** The number of the tile-part in the codestream */
private int[][] tilePartNum;
/** Whether or not a EOC marker has been found instead of a SOT */
private boolean isEOCFound = false;
/** Reference to the HeaderInfo instance (used when reading SOT marker
* segments) */
private HeaderInfo hi;
/** Array containing info. for all the code-blocks:
* - 1st dim: component index.
* - 2nd dim: resolution level index.
* - 3rd dim: subband index.
* - 4th/5th dim: code-block index (vert. and horiz.).
*/
private CBlkInfo[][][][][] cbI;
/** Gets the reference to the CBlkInfo array */
public CBlkInfo[][][][][] getCBlkInfo() {
return cbI;
}
/** The maximum number of layers to decode for any code-block */
private int lQuit;
/** Whether or not to use only first progression order */
private boolean usePOCQuit = false;
/**
* Reads all tiles headers and keep offset of their first
* packet. Finally it calls the rate allocation method.
*
* @param hd HeaderDecoder of the codestream.
*
* @param ehs The input stream where to read bit-stream.
*
* @param decSpec The decoder specifications
*
* @param j2krparam The J2KImageReadParam instance created from the
* command-line arguments.
*
* @param cdstrInfo Whether or not to print information found in
* codestream.
*
* @see #allocateRate
* */
public FileBitstreamReaderAgent(HeaderDecoder hd,RandomAccessIO ehs,
DecoderSpecs decSpec,
J2KImageReadParamJava j2krparam,
boolean cdstrInfo,HeaderInfo hi)
throws IOException {
super(hd,decSpec);
this.j2krparam = j2krparam;
this.printInfo = cdstrInfo;
this.hi = hi;
String strInfo = printInfo ?
"Codestream elements information in bytes "+
"(offset, total length, header length):\n\n" : null;
// Check whether quit conditiosn used
//usePOCQuit = j2krparam.getPOCQuit();
// Get decoding rate
if (j2krparam.getDecodingRate() == Double.MAX_VALUE)
tnbytes = Integer.MAX_VALUE;
else
tnbytes = (int)(j2krparam.getDecodingRate() * hd.getMaxCompImgWidth() *
hd.getMaxCompImgHeight()) / 8;
//isTruncMode = !j2krparam.getParsing();
isTruncMode = true;
// Check if quit conditions are being used
//int ncbQuit = j2krparam.getNCBQuit();
int ncbQuit = -1;
if(ncbQuit != -1 && !isTruncMode){
throw new Error("Cannot use -parsing and -ncb_quit condition at "+
"the same time.");
}
// lQuit = j2krparam.getLQuit();
lQuit = -1;
// initializations
nt = ntX * ntY;
in = ehs;
pktDec = new PktDecoder(decSpec,hd,ehs,this,isTruncMode, ncbQuit);
tileParts = new int[nt];
totTileParts = new int[nt];
totTileLen = new int[nt];
tilePartLen = new int[nt][];
tilePartNum = new int[nt][];
firstPackOff = new int[nt][];
tilePartsRead = new int[nt];
totTileHeadLen = new int[nt];
tilePartHeadLen = new int[nt][];
nBytes = new int[nt];
baknBytes = new int[nt];
hd.nTileParts = new int[nt];
this.isTruncMode = isTruncMode;
// Keeps main header's length, takes file format overhead into account
cdstreamStart = hd.mainHeadOff; // Codestream offset in the file
mainHeadLen = in.getPos() - cdstreamStart;
headLen = mainHeadLen;
// If ncb and lbody quit conditions are used, headers are not counted
if(ncbQuit == -1) {
anbytes = mainHeadLen;
} else {
anbytes = 0;
}
if(printInfo)
strInfo += "Main header length : "+cdstreamStart+", "+mainHeadLen+
", "+mainHeadLen+"\n";
// If cannot even read the first tile-part
if(anbytes>tnbytes) {
throw new Error("Requested bitrate is too small.");
}
// Initialize variables used when reading tile-part headers.
totAllTileLen = 0;
remainingTileParts = nt; // at least as many tile-parts as tiles
maxPos = lastPos = in.getPos();
// Update 'res' value according to the parameter and the main header.
if(j2krparam.getResolution()== -1) {
targetRes = decSpec.dls.getMin();
} else {
targetRes = j2krparam.getResolution();
if(targetRes<0) {
throw new
IllegalArgumentException("Specified negative "+
"resolution level index: "+
targetRes);
}
}
// Verify reduction in resolution level
int mdl = decSpec.dls.getMin();
if(targetRes>mdl) {
FacilityManager.getMsgLogger().
printmsg(MsgLogger.WARNING,
"Specified resolution level ("+targetRes+
") is larger"+
" than the maximum possible. Setting it to "+
mdl +" (maximum possible)");
targetRes = mdl;
}
// Initialize tile part positions from TLM marker segment.
initTLM();
}
// An array of the positions of tile parts:
// - length of tilePartPositions is nt.
// - length of tilePartPositions[i] is totTileParts[i].
long[][] tilePartPositions = null;
//
// Initialize the tilePartPositions positions array if a TLM marker
// segment is present in the main header. If no such marker segment
// is present the array will remain null. This method rewinds to the
// start of the codestream and scans until the first SOT marker is
// encountered. Before return the stream is returned to its position
// when the method was invoked.
//
private void initTLM() throws IOException {
// Save the position to return to at the end of this method.
int savePos = in.getPos();
// Array to store contents of TLM segments. The first index is
// Ztlm. The contents of tlmSegments[i] is the bytes in the TLM
// segment with Ztlm == i after the Ztlm byte.
byte[][] tlmSegments = null;
// Number of TLM segments. The first numTLM elements of tlmSegments
// should be non-null if the segments are correct.
int numTLM = 0;
try {
// Rewind to the start of the main header.
in.seek(cdstreamStart + 2); // skip SOC
// Loop over marker segments.
short marker;
while((marker = in.readShort()) != SOT) {
// Get the length (which includes the 2-byte length parameter).
int markerLength = in.readUnsignedShort();
// Process TLM segments.
if(marker == TLM) {
numTLM++;
if(tlmSegments == null) {
tlmSegments = new byte[256][]; // 0 <= Ztlm <= 255
}
// Save contents after Ztlm in array.
int Ztlm = in.read();
tlmSegments[Ztlm] = new byte[markerLength - 3];
in.readFully(tlmSegments[Ztlm], 0, markerLength - 3);
} else {
in.skipBytes(markerLength - 2);
}
}
} catch(IOException e) {
// Reset so that the TLM segments are not processed further.
tlmSegments = null;
}
if(tlmSegments != null) {
ArrayList[] tlmOffsets = null;
// Tiles start after the main header.
long tilePos = cdstreamStart + mainHeadLen;
// Tile counter for when tile indexes are not included.
int tileCounter = 0;
for(int itlm = 0; itlm < numTLM; itlm++) {
if(tlmSegments[itlm] == null) {
// Null segment among first numTLM entries: error.
tlmOffsets = null;
break;
} else if(tlmOffsets == null) {
tlmOffsets = new ArrayList[nt];
}
// Create a stream.
ByteArrayInputStream bais =
new ByteArrayInputStream(tlmSegments[itlm]);
ImageInputStream iis = new MemoryCacheImageInputStream(bais);
try {
int Stlm = iis.read();
int ST = (Stlm >> 4) & 0x3;
int SP = (Stlm >> 6) & 0x1;
int tlmLength = tlmSegments[itlm].length;
while(iis.getStreamPosition() < tlmLength) {
int tileIndex = tileCounter;
switch(ST) {
case 1:
tileIndex = iis.read();
break;
case 2:
tileIndex = iis.readUnsignedShort();
}
if(tlmOffsets[tileIndex] == null) {
tlmOffsets[tileIndex] = new ArrayList();
}
tlmOffsets[tileIndex].add(new Long(tilePos));
long tileLength = 0L;
switch(SP) {
case 0:
tileLength = iis.readUnsignedShort();
break;
case 1:
tileLength = iis.readUnsignedInt();
break;
}
tilePos += tileLength;
if(ST == 0) tileCounter++;
}
} catch(IOException e) {
// XXX?
}
}
if(tlmOffsets != null) {
tilePartPositions = new long[nt][];
for(int i = 0; i < nt; i++) {
if(tlmOffsets[i] == null) {
tilePartPositions = null;
break;
} else {
ArrayList list = tlmOffsets[i];
int count = list.size();
tilePartPositions[i] = new long[count];
long[] tpPos = tilePartPositions[i];
for(int j = 0; j < count; j++) {
tpPos[j] = ((Long)list.get(j)).longValue();
}
}
}
}
}
in.seek(savePos);
}
int cdstreamStart = 0;
int t=0, pos=-1, tp=0, tptot=0;
int tilePartStart = 0;
boolean rateReached = false;
int numtp = 0;
int maxTP = nt; // If maximum 1 tile part per tile specified
int lastPos = 0, maxPos = 0;
/**
* Read all tile-part headers of the requested tile. All tile-part
* headers prior to the last tile-part header of the current tile will
* also be read.
*
* @param tileNum The index of the tile for which to read tile-part
* headers.
*/
private void initTile(int tileNum) throws IOException {
if(tilePartPositions == null) in.seek(lastPos);
String strInfo = "";
int ncbQuit = -1;
boolean isTilePartRead = false;
boolean isEOFEncountered = false;
try {
int tpNum = 0;
while(remainingTileParts!=0 &&
(totTileParts[tileNum] == 0 ||
tilePartsRead[tileNum] < totTileParts[tileNum])) {
isTilePartRead = true;
if(tilePartPositions != null) {
in.seek((int)tilePartPositions[tileNum][tpNum++]);
}
tilePartStart = in.getPos();
// Read tile-part header
try {
t = readTilePartHeader();
if(isEOCFound) { // Some tiles are missing but the
// codestream is OK
break;
}
tp = tilePartsRead[t];
if(isPsotEqualsZero) { // Psot may equals zero for the
// last tile-part: it is assumed that this tile-part
// contain all data until EOC
tilePartLen[t][tp] = in.length()-2-tilePartStart;
}
} catch(EOFException e) {
firstPackOff[t][tp] = in.length();
throw e;
}
pos = in.getPos();
// In truncation mode, if target decoding rate is reached in
// tile-part header, skips the tile-part and stop reading
// unless the ncb and lbody quit condition is in use
if(isTruncMode && ncbQuit == -1) {
if((pos-cdstreamStart)>tnbytes) {
firstPackOff[t][tp] = in.length();
rateReached = true;
break;
}
}
// Set tile part position and header length
firstPackOff[t][tp] = pos;
tilePartHeadLen[t][tp] = (pos-tilePartStart);
if(printInfo)
strInfo += "Tile-part "+tp+" of tile "+t+" : "+tilePartStart
+", "+tilePartLen[t][tp]+", "+tilePartHeadLen[t][tp]+"\n";
// Update length counters
totTileLen[t] += tilePartLen[t][tp];
totTileHeadLen[t] += tilePartHeadLen[t][tp];
totAllTileLen += tilePartLen[t][tp];
if(isTruncMode) {
if(anbytes+tilePartLen[t][tp]>tnbytes) {
anbytes += tilePartHeadLen[t][tp];
headLen += tilePartHeadLen[t][tp];
rateReached = true;
nBytes[t] += (tnbytes-anbytes);
break;
} else {
anbytes += tilePartHeadLen[t][tp];
headLen += tilePartHeadLen[t][tp];
nBytes[t] += (tilePartLen[t][tp]-
tilePartHeadLen[t][tp]);
}
} else {
if(anbytes+tilePartHeadLen[t][tp]>tnbytes) {
break;
} else {
anbytes += tilePartHeadLen[t][tp];
headLen += tilePartHeadLen[t][tp];
}
}
// If this is first tile-part, remember header length
if(tptot==0)
firstTilePartHeadLen = tilePartHeadLen[t][tp];
// Go to the beginning of next tile part
tilePartsRead[t]++;
int nextMarkerPos = tilePartStart+tilePartLen[t][tp];
if(tilePartPositions == null) {
in.seek(nextMarkerPos);
}
if(nextMarkerPos > maxPos) {
maxPos = nextMarkerPos;
}
remainingTileParts--;
maxTP--;
tptot++;
// If Psot of the current tile-part was equal to zero, it is
// assumed that it contains all data until the EOC marker
if(isPsotEqualsZero) {
if(remainingTileParts!=0) {
FacilityManager.getMsgLogger().printmsg
(MsgLogger.WARNING,"Some tile-parts have not "+
"been found. The codestream may be corrupted.");
}
break;
}
}
} catch(EOFException e) {
isEOFEncountered = true;
if(printInfo) {
FacilityManager.getMsgLogger().
printmsg(MsgLogger.INFO,strInfo);
}
FacilityManager.getMsgLogger().
printmsg(MsgLogger.WARNING,"Codestream truncated in tile "+t);
// Set specified rate to end of file if valid
int fileLen = in.length();
if(fileLenmdl) {
FacilityManager.getMsgLogger().
printmsg(MsgLogger.WARNING,
"Specified resolution level ("+targetRes+
") is larger"+
" than the maximum possible. Setting it to "+
mdl +" (maximum possible)");
targetRes = mdl;
}
XXX: END */
if(!isEOFEncountered) {
if(printInfo) {
FacilityManager.getMsgLogger().printmsg(MsgLogger.INFO,strInfo);
}
if(remainingTileParts == 0) {
// Check presence of EOC marker is decoding rate not reached or
// if this marker has not been found yet
if(!isEOCFound && !isPsotEqualsZero && !rateReached) {
try {
int savePos = in.getPos();
in.seek(maxPos);
if(in.readShort()!=EOC) {
FacilityManager.getMsgLogger().
printmsg(MsgLogger.WARNING,"EOC marker not found. "+
"Codestream is corrupted.");
}
in.seek(savePos);
} catch(EOFException e) {
FacilityManager.getMsgLogger().
printmsg(MsgLogger.WARNING,"EOC marker is missing");
}
}
}
}
// Bit-rate allocation
if(!isTruncMode) {
allocateRate();
} else if(remainingTileParts == 0 && !isEOFEncountered) {
// Take EOC into account if rate is not reached
if(in.getPos()>=tnbytes)
anbytes += 2;
}
if(tilePartPositions == null) lastPos = in.getPos();
// Backup nBytes
for (int tIdx=0; tIdx stopOff){
throw new Error("Requested bitrate is too small for parsing");
}
// Calculate bitrate for each tile
int rem = stopOff-anbytes;
int totnByte = rem;
for(int t=nt-1; t>0; t--){
rem -= nBytes[t]=(int)(totnByte*(totTileLen[t]/totAllTileLen));
}
nBytes[0] = rem;
}
/**
* Reads SOT marker segment of the tile-part header and calls related
* methods of the HeaderDecoder to read other markers segments. The
* tile-part header is entirely read when a SOD marker is encountered.
*
* @return The tile number of the tile part that was read
* */
private int readTilePartHeader() throws IOException{
HeaderInfo.SOT ms = hi.getNewSOT();
// SOT marker
short marker = in.readShort();
if(marker!=SOT) {
if(marker==EOC) {
isEOCFound = true;
return -1;
} else {
throw new CorruptedCodestreamException("SOT tag not found "+
"in tile-part start");
}
}
isEOCFound = false;
// Lsot (shall equals 10)
int lsot = in.readUnsignedShort();
ms.lsot = lsot;
if(lsot!=10)
throw new CorruptedCodestreamException("Wrong length for "+
"SOT marker segment: "+
lsot);
// Isot
int tile = in.readUnsignedShort();
ms.isot = tile;
if(tile>65534){
throw new CorruptedCodestreamException("Tile index too high in "+
"tile-part.");
}
// Psot
int psot = in.readInt();
ms.psot = psot;
isPsotEqualsZero = (psot!=0) ? false : true;
if(psot<0) {
throw new NotImplementedError("Tile length larger "+
"than maximum supported");
}
// TPsot
int tilePart = in.read();
ms.tpsot = tilePart;
if( tilePart!=tilePartsRead[tile] || tilePart<0 || tilePart>254 ) {
throw new CorruptedCodestreamException("Out of order tile-part");
}
// TNsot
int nrOfTileParts = in.read();
ms.tnsot = nrOfTileParts;
hi.sot.put("t"+tile+"_tp"+tilePart,ms);
if(nrOfTileParts==0) { // The number of tile-part is not specified in
// this tile-part header.
// Assumes that there will be another tile-part in the codestream
// that will indicate the number of tile-parts for this tile)
int nExtraTp;
if(tileParts[tile]==0 || tileParts[tile]==tilePartLen.length ) {
// Then there are two tile-parts (one is the current and the
// other will indicate the number of tile-part for this tile)
nExtraTp = 2;
remainingTileParts += 1;
} else {
// There is already one scheduled extra tile-part. In this
// case just add place for the current one
nExtraTp = 1;
}
tileParts[tile] += nExtraTp;
nrOfTileParts = tileParts[tile];
FacilityManager.getMsgLogger().
printmsg(MsgLogger.WARNING,"Header of tile-part "+tilePart+
" of tile "+tile+", does not indicate the total"+
" number of tile-parts. Assuming that there are "+
nrOfTileParts+" tile-parts for this tile.");
// Increase and re-copy tilePartLen array
int[] tmpA = tilePartLen[tile];
tilePartLen[tile] = new int[nrOfTileParts];
for(int i=0; i nrOfTileParts ) {
// Already found more tile-parts than signaled here
throw new CorruptedCodestreamException("Invalid number "+
"of tile-parts in"+
" tile "+tile+": "+
nrOfTileParts);
} else { // Signaled number of tile-part fits with number of
// previously found tile-parts
remainingTileParts += nrOfTileParts-tileParts[tile];
if(tileParts[tile]!=nrOfTileParts) {
// Increase and re-copy tilePartLen array
int[] tmpA = tilePartLen[tile];
tilePartLen[tile] = new int[nrOfTileParts];
for(int i=0; i=mdl.length) continue;
for(int r=ress; r=mdl.length) continue;
// Checks if resolution level exists
if(r>=lys[c].length) continue;
if(r>mdl[c]) continue;
// Checks if layer exists
if(l=numLayers) continue;
nPrec = pktDec.getNumPrecinct(c,r);
for(int p=0; plastByte &&
curTilePart=mdl.length) continue;
for(int r=ress; rmdl[c]) continue;
if(lys[c]!=null && r=mdl.length) continue;
// Checks if resolution level exists
if(r>mdl[c]) continue;
if(r>=lys[c].length) continue;
// Checks if layer exists
if(l=numLayers) continue;
nPrec = pktDec.getNumPrecinct(c,r);
for(int p=0; plastByte &&
curTilePart=mdl.length) continue;
if(r>mdl[c]) continue;
nextPrec[c] = new int[mdl[c]+1];
if (lys[c]!=null && r=0; p--) {
prec = pktDec.getPrecInfo(c,r,p);
if(prec.rgulx!=tx0) {
if(prec.rgulxmaxx) maxx = prec.rgulx;
}
if(prec.rguly!=ty0) {
if(prec.rgulymaxy) maxy = prec.rguly;
}
if(nPrec==0) {
gcd_x = prec.rgw;
gcd_y = prec.rgh;
} else {
gcd_x = MathUtil.gcd(gcd_x,prec.rgw);
gcd_y = MathUtil.gcd(gcd_y,prec.rgh);
}
nPrec++;
} // precincts
} // resolution levels
} // components
if(nPrec==0) {
throw new Error("Image cannot have no precinct");
}
int pyend = (maxy-miny)/gcd_y+1;
int pxend = (maxx-minx)/gcd_x+1;
int x,y;
int hlen,plen;
int start;
boolean status = false;
int lastByte = firstPackOff[t][curTilePart]+
tilePartLen[t][curTilePart]-1-
tilePartHeadLen[t][curTilePart];
int numLayers = ((Integer)decSpec.nls.getTileDef(t)).intValue();
String strInfo = printInfo ?
"Tile "+getTileIdx()+" (tile-part:"+curTilePart+
"): offset, length, header length\n" : null;
boolean pph = false;
if(((Boolean)decSpec.pphs.getTileDef(t)).booleanValue()) {
pph = true;
}
for(int r=ress; r=mdl.length) continue;
if(r>mdl[c]) continue;
if(nextPrec[c][r]>=pktDec.getNumPrecinct(c,r)) {
continue;
}
prec = pktDec.getPrecInfo(c,r,nextPrec[c][r]);
if((prec.rgulx!=x) || (prec.rguly!=y)) {
continue;
}
for(int l=minlys; l=lys[c].length) continue;
if(l=numLayers) continue;
start = in.getPos();
// If packed packet headers are used, there is no
// need to check that there are bytes enough to
// read header
if(pph) {
pktDec.readPktHead(l,r,c,nextPrec[c][r],
cbI[c][r],nBytes);
}
// If we are about to read outside of tile-part,
// skip to next tile-part
if(start>lastByte &&
curTilePart=mdl.length) continue;
if(r>mdl[c]) continue;
nextPrec[c] = new int[mdl[c]+1];
if (lys[c]!=null && r=0; p--) {
prec = pktDec.getPrecInfo(c,r,p);
if(prec.rgulx!=tx0) {
if(prec.rgulxmaxx) maxx = prec.rgulx;
}
if(prec.rguly!=ty0) {
if(prec.rgulymaxy) maxy = prec.rguly;
}
if(nPrec==0) {
gcd_x = prec.rgw;
gcd_y = prec.rgh;
} else {
gcd_x = MathUtil.gcd(gcd_x,prec.rgw);
gcd_y = MathUtil.gcd(gcd_y,prec.rgh);
}
nPrec++;
} // precincts
} // resolution levels
} // components
if(nPrec==0) {
throw new Error("Image cannot have no precinct");
}
int pyend = (maxy-miny)/gcd_y+1;
int pxend = (maxx-minx)/gcd_x+1;
int hlen,plen;
int start;
boolean status = false;
int lastByte = firstPackOff[t][curTilePart]+
tilePartLen[t][curTilePart]-1-
tilePartHeadLen[t][curTilePart];
int numLayers = ((Integer)decSpec.nls.getTileDef(t)).intValue();
String strInfo = printInfo ?
"Tile "+getTileIdx()+" (tile-part:"+curTilePart+
"): offset, length, header length\n" : null;
boolean pph = false;
if(((Boolean)decSpec.pphs.getTileDef(t)).booleanValue()) {
pph = true;
}
int y = ty0;
int x = tx0;
for(int py=0; py<=pyend; py++) { // Vertical precincts
for(int px=0; px<=pxend; px++) { // Horiz. precincts
for(int c=comps; c=mdl.length) continue;
for(int r=ress; rmdl[c]) continue;
if(nextPrec[c][r]>=pktDec.getNumPrecinct(c,r)) {
continue;
}
prec = pktDec.getPrecInfo(c,r,nextPrec[c][r]);
if((prec.rgulx!=x) || (prec.rguly!=y)) {
continue;
}
for(int l=minlys; l=lys[c].length) continue;
if(l=numLayers) continue;
start = in.getPos();
// If packed packet headers are used, there is no
// need to check that there are bytes enough to
// read header
if(pph) {
pktDec.readPktHead(l,r,c,nextPrec[c][r],
cbI[c][r],nBytes);
}
// Read SOP marker segment if necessary
status = pktDec.readSOPMarker(nBytes,
nextPrec[c][r],c,r);
if(status) {
if(printInfo) {
FacilityManager.getMsgLogger().
printmsg(MsgLogger.INFO,strInfo);
}
return true;
}
if(!pph) {
status =
pktDec.readPktHead(l,r,c,
nextPrec[c][r],
cbI[c][r],nBytes);
}
if(status) {
if(printInfo) {
FacilityManager.getMsgLogger().
printmsg(MsgLogger.INFO,strInfo);
}
return true;
}
// Store packet's head length
hlen = in.getPos()-start;
pktHL.addElement(new Integer(hlen));
// Reads packet's body
status = pktDec.readPktBody(l,r,c,nextPrec[c][r],
cbI[c][r],nBytes);
plen = in.getPos()-start;
if(printInfo)
strInfo+= " Pkt l="+l+",r="+r+",c="+c+",p="+
nextPrec[c][r]+": "+
start+", "+plen+", "+hlen+"\n";
if(status) {
if(printInfo) {
FacilityManager.getMsgLogger().
printmsg(MsgLogger.INFO,strInfo);
}
return true;
}
} // layers
nextPrec[c][r]++;
} // Resolution levels
} // Components
if(px!=pxend) {
x = minx+px*gcd_x;
} else {
x = tx0;
}
} // Horizontal precincts
if(py!=pyend) {
y = miny+py*gcd_y;
} else {
y = ty0;
}
} // Vertical precincts
if(printInfo) {
FacilityManager.getMsgLogger().printmsg(MsgLogger.INFO,strInfo);
}
return false; // Decoding rate was not reached
}
/**
* Reads packets of the current tile according to the
* component-position-resolution-layer progressiveness.
*
* @param lys Index of the first layer for each component and resolution.
*
* @param lye Index of the last layer.
*
* @param ress Index of the first resolution level.
*
* @param rese Index of the last resolution level.
*
* @param comps Index of the first component.
*
* @param compe Index of the last component.
*
* @return True if rate has been reached.
* */
private boolean readCompPosResLy(int lys[][],int lye,int ress,int rese,
int comps,int compe)
throws IOException {
Point nTiles = getNumTiles(null);
Point tileI = getTile(null);
int x0siz = hd.getImgULX();
int y0siz = hd.getImgULY();
int xsiz = x0siz + hd.getImgWidth();
int ysiz = y0siz + hd.getImgHeight();
int xt0siz = getTilePartULX();
int yt0siz = getTilePartULY();
int xtsiz = getNomTileWidth();
int ytsiz = getNomTileHeight();
int tx0 = (tileI.x==0) ? x0siz : xt0siz+tileI.x*xtsiz;
int ty0 = (tileI.y==0) ? y0siz : yt0siz+tileI.y*ytsiz;
int tx1 = (tileI.x!=nTiles.x-1) ? xt0siz+(tileI.x+1)*xtsiz : xsiz;
int ty1 = (tileI.y!=nTiles.y-1) ? yt0siz+(tileI.y+1)*ytsiz : ysiz;
// Get precinct information (number,distance between two consecutive
// precincts in the reference grid) in each component and resolution
// level
int t = getTileIdx(); // Current tile index
PrecInfo prec; // temporary variable
int p; // Current precinct index
int gcd_x = 0; // Horiz. distance between 2 precincts in the ref. grid
int gcd_y = 0; // Vert. distance between 2 precincts in the ref. grid
int nPrec = 0; // Total number of found precincts
int[][] nextPrec = new int [compe][]; // Next precinct index in each
// component and resolution level
int minlys = 100000; // minimum layer start index of each component
int minx = tx1; // Horiz. offset of the second precinct in the
// reference grid
int miny = ty1; // Vert. offset of the second precinct in the
// reference grid.
int maxx = tx0; // Max. horiz. offset of precincts in the ref. grid
int maxy = ty0; // Max. vert. offset of precincts in the ref. grid
Point numPrec;
for(int c=comps; c=mdl.length) continue;
if(r>mdl[c]) continue;
nextPrec[c] = new int[mdl[c]+1];
if (lys[c]!=null && r=0; p--) {
prec = pktDec.getPrecInfo(c,r,p);
if(prec.rgulx!=tx0) {
if(prec.rgulxmaxx) maxx = prec.rgulx;
}
if(prec.rguly!=ty0) {
if(prec.rgulymaxy) maxy = prec.rguly;
}
if(nPrec==0) {
gcd_x = prec.rgw;
gcd_y = prec.rgh;
} else {
gcd_x = MathUtil.gcd(gcd_x,prec.rgw);
gcd_y = MathUtil.gcd(gcd_y,prec.rgh);
}
nPrec++;
} // precincts
} // resolution levels
} // components
if(nPrec==0) {
throw new Error("Image cannot have no precinct");
}
int pyend = (maxy-miny)/gcd_y+1;
int pxend = (maxx-minx)/gcd_x+1;
int hlen,plen;
int start;
boolean status = false;
int lastByte = firstPackOff[t][curTilePart]+
tilePartLen[t][curTilePart]-1-
tilePartHeadLen[t][curTilePart];
int numLayers = ((Integer)decSpec.nls.getTileDef(t)).intValue();
String strInfo = printInfo ?
"Tile "+getTileIdx()+" (tile-part:"+curTilePart+
"): offset, length, header length\n" : null;
boolean pph = false;
if(((Boolean)decSpec.pphs.getTileDef(t)).booleanValue()) {
pph = true;
}
int x,y;
for(int c=comps; c=mdl.length) continue;
y = ty0;
x = tx0;
for(int py=0; py<=pyend; py++) { // Vertical precincts
for(int px=0; px<=pxend; px++) { // Horiz. precincts
for(int r=ress; rmdl[c]) continue;
if(nextPrec[c][r]>=pktDec.getNumPrecinct(c,r)) {
continue;
}
prec = pktDec.getPrecInfo(c,r,nextPrec[c][r]);
if((prec.rgulx!=x) || (prec.rguly!=y)) {
continue;
}
for(int l=minlys; l=lys[c].length) continue;
if(llastByte &&
curTilePartThen, if a parsing output rate is defined, it keeps information of
* first layers only. This operation simulates a creation of a
* layer-resolution-component progressive bit-stream which will be next
* truncated and decoded.
*
* @param t Tile index
*
* @see PktDecoder
* */
private void readTilePkts(int t) throws IOException {
pktHL = new Vector();
int oldNBytes = nBytes[t];
// Number of layers
int nl = ((Integer)decSpec.nls.getTileDef(t)).intValue();
// If packed packet headers was used, get the packet headers for this
// tile
if(((Boolean)decSpec.pphs.getTileDef(t)).booleanValue()) {
// Gets packed headers as separate input stream
ByteArrayInputStream pphbais = hd.getPackedPktHead(t);
// Restarts PktDecoder instance
cbI = pktDec.restart(nc,mdl,nl,cbI,true,pphbais);
} else {
// Restarts PktDecoder instance
cbI = pktDec.restart(nc,mdl,nl,cbI,false,null);
}
// Reads packets of the tile according to the progression order
int[][] pocSpec = ((int[][])decSpec.pcs.getTileDef(t));
int nChg = (pocSpec==null) ? 1 : pocSpec.length;
// Create an array containing information about changes (progression
// order type, layers index start, layer index end, resolution level
// start, resolution level end, component index start, component index
// end). There is one row per progresion order
int[][] change = new int[nChg][6];
int idx = 0; // Index of the current progression order
change[0][1] = 0; // layer start
if(pocSpec==null) {
change[idx][0] = ((Integer)decSpec.pos.getTileDef(t)).intValue();
// Progression type found in COx marker segments
change[idx][1] = nl; // Layer index end
change[idx][2] = 0; // resolution level start
change[idx][3] = decSpec.dls.getMaxInTile(t)+1; // res. level end
change[idx][4] = 0; // Component index start
change[idx][5] = nc; // Component index end
} else {
for(idx=0; idx=lys.length) continue;
for(int r=ress; r=lys[c].length) continue;
lys[c][r] = lye;
}
}
if(status || usePOCQuit) {
break;
}
}
} catch(EOFException e) {
// Should never happen. Truncated codestream are normally found by
// the class constructor
throw e;
}
// In truncation mode, update the number of read bytes
if(isTruncMode) {
anbytes += nb-nBytes[t];
// If truncation rate is reached
if(status) {
nBytes[t] = 0;
}
} else if(nBytes[t]<(totTileLen[t]-totTileHeadLen[t])) {
// In parsing mode, if there is not enough rate to entirely read the
// tile. Then, parses the bit stream so as to create a virtual
// layer-resolution-component progressive bit stream that will be
// truncated and decoded afterwards.
CBlkInfo cb;
// Systematicaly reject all remaining code-blocks if one
// code-block, at least, is refused.
boolean reject;
// Stop reading any data from the bit stream
boolean stopCount = false;
// Length of each packet's head (in an array)
int[] pktHeadLen = new int[pktHL.size()];
for(int i=pktHL.size()-1;i>=0;i--) {
pktHeadLen[i] = ((Integer)pktHL.elementAt(i)).intValue();
}
// Parse each code-block, layer per layer until nBytes[t] is
// reached
reject = false;
for(int l=0; lmres)
mres = cbI[c].length;
}
for(int r=0; rmsub)
msub = cbI[c][r].length;
}
for(int s=0; s 0
continue;
}
int mnby=0;
for(int c=0; cmnby)
mnby = cbI[c][r][s].length;
}
for(int m=0; mmnbx)
mnbx = cbI[c][r][s][m].length;
}
for(int n=0; n=ntX || y>=ntY) {
throw new IllegalArgumentException();
}
int t = (y*ntX+x);
try {
initTile(t);
} catch(IOException ioe) {
// XXX Do something!
}
// Reset number of read bytes if needed
if(t==0) {
anbytes = headLen;
if(!isTruncMode) {
anbytes += 2;
}
// Restore values of nBytes
for(int tIdx=0; tIdx=0; i--) {
culx[i] = (ctox+hd.getCompSubsX(i)-1)/hd.getCompSubsX(i);
culy[i] = (ctoy+hd.getCompSubsY(i)-1)/hd.getCompSubsY(i);
offX[i] = (px+x*ntW+hd.getCompSubsX(i)-1)/hd.getCompSubsX(i);
offY[i] = (py+y*ntH+hd.getCompSubsY(i)-1)/hd.getCompSubsY(i);
}
// Initialize subband tree and number of resolution levels
subbTrees = new SubbandSyn[nc];
mdl = new int[nc];
derived = new boolean[nc];
params = new StdDequantizerParams[nc];
gb = new int[nc];
for(int c=0; cThe argument 'fl' is to be used by subsequent calls to this method
* for the same code-block. In this way supplemental data can be retrieved
* at a later time. The fact that data from more than one layer can be
* returned means that several packets from the same code-block, of the
* same component, and the same tile, have been concatenated.
*
* The returned compressed code-block can have its progressive
* attribute set. If this attribute is set it means that more data can be
* obtained by subsequent calls to this method (subject to transmission
* delays, etc). If the progressive attribute is not set it means that the
* returned data is all the data that can be obtained for the specified
* code-block.
*
* The compressed code-block is uniquely specified by the current tile,
* the component (identified by 'c'), the subband (indentified by 'sb')
* and the code-block vertical and horizontal indexes 'n' and 'm'.
*
* The 'ulx' and 'uly' members of the returned 'DecLyrdCBlk' object
* contain the coordinates of the top-left corner of the block, with
* respect to the tile, not the subband.
*
* @param c The index of the component, from 0 to N-1.
*
* @param m The vertical index of the code-block to return, in the
* specified subband.
*
* @param n The horizontal index of the code-block to return, in the
* specified subband.
*
* @param sb The subband in whic the requested code-block is.
*
* @param fl The first layer to return.
*
* @param nl The number of layers to return, if negative all available
* layers are returned, starting at 'fl'.
*
* @param ccb If not null this object is used to return the compressed
* code-block. If null a new object is created and returned. If the data
* array in ccb is not null then it can be reused to return the compressed
* data.
* @return The compressed code-block, with a certain number of layers
* determined by the available data and 'nl'.
* */
public DecLyrdCBlk getCodeBlock(int c,int m,int n,SubbandSyn sb,int fl,
int nl,DecLyrdCBlk ccb) {
int t = getTileIdx();
CBlkInfo rcb; // requested code-block
int r = sb.resLvl; // Resolution level
int s = sb.sbandIdx; // Subband index
int tpidx;
int passtype;
// Number of layers
int numLayers = ((Integer)decSpec.nls.getTileDef(t)).intValue();
int options = ((Integer)decSpec.ecopts.getTileCompVal(t,c)).intValue();
if(nl<0) {
nl = numLayers-fl+1;
}
// If the l quit condition is used, Make sure that no layer
// after lquit is returned
if(lQuit != -1 && fl+nl>lQuit){
nl = lQuit - fl;
}
// Check validity of resquested resolution level (according to the
// "-res" option).
int maxdl = getSynSubbandTree(t,c).resLvl;
/* XXX Suppress error check for speed performance reasons.
if(r>targetRes+maxdl-decSpec.dls.getMin()) {
throw new Error("JJ2000 error: requesting a code-block "+
"disallowed by the '-res' option.");
}
*/
// Check validity of all the arguments
try {
rcb = cbI[c][r][s][m][n];
if(fl<1 || fl>numLayers || fl+nl-1>numLayers) {
throw new IllegalArgumentException();
}
} catch(ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException("Code-block (t:"+t+", c:"+
c+", r:"+r+", s:"+s+", "+m+"x"+
+n+") not found in codestream");
} catch(NullPointerException e) {
throw new IllegalArgumentException("Code-block (t:"+t+", c:"+
c+", r:"+r+", s:"+s+", "+m+"x"
+n+") not found in bit stream");
}
// Create DecLyrdCBlk object if necessary
if(ccb==null) {
ccb = new DecLyrdCBlk();
}
ccb.m = m;
ccb.n = n;
ccb.nl = 0;
ccb.dl = 0;
ccb.nTrunc = 0;
if(rcb==null) {
// This code-block was skipped when reading. Returns no data
ccb.skipMSBP = 0;
ccb.prog = false;
ccb.w = ccb.h = ccb.ulx = ccb.uly = 0;
return ccb;
}
// ccb initialization
ccb.skipMSBP = rcb.msbSkipped;
ccb.ulx = rcb.ulx;
ccb.uly = rcb.uly;
ccb.w = rcb.w;
ccb.h = rcb.h;
ccb.ftpIdx = 0;
// Search for index of first truncation point (first layer where
// length of data is not zero)
int l=0;
while( (l= FIRST_BYPASS_PASS_IDX-1) {
passtype =
(tpidx+NUM_EMPTY_PASSES_IN_MS_BP)%NUM_PASSES;
if (passtype == 1 || passtype == 2) {
// lazy pass just before MQ pass or MQ pass just
// before lazy pass => terminated
nts++;
}
}
}
}
} else {
// Nothing special in use, just one terminated segment
nts = 1;
}
// ccb.data creation
if(ccb.data==null || ccb.data.length1 && (ccb.tsLengths==null || ccb.tsLengths.length1 &&
(options & (OPT_BYPASS|OPT_TERM_PASS)) == OPT_BYPASS) {
ArrayUtil.intArraySet(ccb.tsLengths,0);
}
// Fill ccb with compressed data
int dataIdx = -1;
tpidx = ccb.ftpIdx;
int ctp = ccb.ftpIdx; // Cumulative number of truncation
// point for the current layer layer
int tsidx=0;
int j;
for(l=fl-1; l each pass is terminated
for(j=0; tpidx=FIRST_BYPASS_PASS_IDX-1) {
passtype =
(tpidx+NUM_EMPTY_PASSES_IN_MS_BP)%NUM_PASSES;
if(passtype!=0) {
// lazy pass just before MQ pass or MQ
// pass just before lazy pass =>
// terminated
if(rcb.segLen[l]!=null) {
ccb.tsLengths[tsidx++] += rcb.segLen[l][j++];
rcb.len[l] -= rcb.segLen[l][j-1];
} else { // Only one terminated segment in packet
ccb.tsLengths[tsidx++] += rcb.len[l];
rcb.len[l] = 0;
}
}
}
}
// Last length in packet always in (either terminated segment
// or contribution to terminated segment)
if(rcb.segLen[l]!=null && j