/*
 * #%L
 * Fork of JAI Image I/O Tools.
 * %%
 * Copyright (C) 2008 - 2015 Open Microscopy Environment:
 *   - Board of Regents of the University of Wisconsin-Madison
 *   - Glencoe Software, Inc.
 *   - University of Dundee
 * %%
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * #L%
 */

/*
 * $RCSfile: PktDecoder.java,v $
 * $Revision: 1.1 $
 * $Date: 2005/02/11 05:02:01 $
 * $State: Exp $
 *
 * Class:                   PktDecoder
 *
 * Description:             Reads packets heads and keeps location of
 *                          code-blocks' codewords
 *
 *
 * 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 jj2000.j2k.wavelet.synthesis.*;
import jj2000.j2k.codestream.*;
import jj2000.j2k.entropy.*;
import jj2000.j2k.wavelet.*;
import jj2000.j2k.decoder.*;
import jj2000.j2k.image.*;
import jj2000.j2k.util.*;
import jj2000.j2k.io.*;

import java.util.*;
import java.io.*;

/**
 * This class is used to read packet's head and body. All the members must be
 * re-initialized at the beginning of each tile thanks to the restart()
 * method.
 * */
public class PktDecoder implements StdEntropyCoderOptions{

    /** Reference to the codestream reader agent */
    private BitstreamReaderAgent src;

    /** Flag indicating whether packed packet header was used for this tile*/
    private boolean pph=false;

    /** The packed packet header if it was used */
    private ByteArrayInputStream pphbais;

    /** Reference to decoder specifications */
    private DecoderSpecs decSpec;

    /** Reference to the HeaderDecoder */
    private HeaderDecoder hd;

    /** Initial value of the state variable associated with code-block
     * length. */
    private final int INIT_LBLOCK = 3;

    /** The wrapper to read bits for the packet heads */
    private PktHeaderBitReader bin;

    /** Reference to the stream where to read from */
    private RandomAccessIO ehs;

    /**
     * Maximum number of precincts :
     *
     * <ul>
     * <li> 1st dim: component index.</li>
     * <li> 2nd dim: resolution level index.</li>
     * </ul>
     * */
    private Point[][] numPrec;

    /** Index of the current tile */
    private int tIdx;

    /** 
     * Array containing the coordinates, width, height, indexes, ... of the
     * precincts in the current tile:
     * 
     * <ul>
     * <li> 1st dim: component index.</li>
     * <li> 2nd dim: resolution level index.</li>
     * <li> 3rd dim: precinct index.</li>
     * </ul>
     * */
    private PrecInfo[][][] ppinfo;

    /**
     * Lblock value used to read code size information in each packet head:
     *
     * <ul>
     * <li> 1st dim: component index.</li>
     * <li> 2nd dim: resolution level index.</li>
     * <li> 3rd dim: subband index.</li>
     * <li> 4th/5th dim: code-block index (vert. and horiz.).</li>
     * </ul>
     * */
    private int[][][][][] lblock;

    /** 
     * Tag tree used to read inclusion informations in packet's head:
     *
     * <ul>   
     * <li> 1st dim: component index.</li>
     * <li> 2nd dim: resolution level index.</li>
     * <li> 3rd dim: precinct index.</li> 
     * <li> 4th dim: subband index.</li>
     * */
    private TagTreeDecoder[][][][] ttIncl;

    /** 
     * Tag tree used to read bit-depth information in packet's head:
     * 
     * <ul>
     * <li> 1st dim: component index.</li>
     * <li> 2nd dim: resolution level index.</li>
     * <li> 3rd dim: precinct index.</li>
     * <li> 4th dim: subband index.</li>
     * </ul>
     * */
    private TagTreeDecoder[][][][] ttMaxBP;

    /** Number of layers in t he current tile */
    private int nl = 0;

    /** The number of components */
    private int nc;

    /** Whether or not SOP marker segment are used */
    private boolean sopUsed = false;

    /** Whether or not EPH marker are used */
    private boolean ephUsed = false;

    /** Index of the current packet in the tile. Used with SOP marker
        segment*/
    private int pktIdx;

    /** List of code-blocks found in last read packet head (one list
     * per subband) */
    private Vector[] cblks;

    /** Number of codeblocks encountered. used for ncb quit condition*/
    private int ncb;

    /** Maximum number of codeblocks to read before ncb quit condition is
     * reached */
    private int maxCB;

    /** Flag indicating whether ncb quit condition has been reached */
    private boolean ncbQuit;

    /** The tile in which the ncb quit condition was reached */
    private int tQuit;

    /** The component in which the ncb quit condition was reached */
    private int cQuit;

    /** The subband in which the ncb quit condition was reached */
    private int sQuit;

    /** The resolution in which the ncb quit condition was reached */
    private int rQuit;

    /** The x position of the last code block before ncb quit reached */
    private int xQuit;

    /** The y position of the last code block before ncb quit reached  */
    private int yQuit;

    /** True if truncation mode is used. False if it is parsing mode */
    private boolean isTruncMode;

    /** 
     * Creates an empty PktDecoder object associated with given decoder
     * specifications and HeaderDecoder. This object must be initialized
     * thanks to the restart method before being used.
     *
     * @param decSpec The decoder specifications.
     *
     * @param hd The HeaderDecoder instance.
     *
     * @param ehs The stream where to read data from.
     *
     * @param src The bit stream reader agent.
     *
     * @param isTruncMode Whether or not truncation mode is required.
     *
     * @param maxCB The maximum number of code-blocks to read before ncbquit
     *
     * */
    public PktDecoder(DecoderSpecs decSpec,HeaderDecoder hd,
                      RandomAccessIO ehs,BitstreamReaderAgent src,
                      boolean isTruncMode, int maxCB) {
        this.decSpec = decSpec;
        this.hd = hd;
        this.ehs = ehs;
        this.isTruncMode = isTruncMode;
        bin = new PktHeaderBitReader(ehs);
        this.src = src;
        ncb = 0;
        ncbQuit = false;
        this.maxCB = maxCB;
    }

    /** 
     * Re-initialize the PktDecoder instance at the beginning of a new tile.
     * 
     * @param nc The number of components in this tile
     *
     * @param mdl The maximum number of decomposition level in each component
     * of this tile
     *
     * @param nl The number of layers in  this tile
     *
     * @param cbI The code-blocks array
     *
     * @param pph Flag indicating whether packed packet headers was used
     *
     * @param pphbais Stream containing the packed packet headers
     * */
    public CBlkInfo[][][][][] restart(int nc,int[] mdl,int nl,
                                      CBlkInfo[][][][][] cbI, boolean pph,
                                      ByteArrayInputStream pphbais) {
        this.nc = nc;
        this.nl = nl;
        this.tIdx = src.getTileIdx();
        this.pph = pph;
        this.pphbais = pphbais;

        sopUsed = ((Boolean)decSpec.sops.getTileDef(tIdx)).booleanValue();
        pktIdx = 0;
        ephUsed = ((Boolean)decSpec.ephs.getTileDef(tIdx)).booleanValue();

        cbI = new CBlkInfo[nc][][][][];
        lblock = new int[nc][][][][];
        ttIncl = new TagTreeDecoder[nc][][][];
        ttMaxBP = new TagTreeDecoder[nc][][][];
        numPrec = new Point[nc][];
        ppinfo = new PrecInfo[nc][][];

        // Used to compute the maximum number of precincts for each resolution
        // level
        int tcx0, tcy0, tcx1, tcy1; // Current tile position in the domain of
        // the image component
        int trx0, try0, trx1, try1; // Current tile position in the reduced
        // resolution image domain
        int xrsiz, yrsiz; // Component sub-sampling factors

        SubbandSyn root,sb;
        int mins,maxs;
        Point nBlk = null;
        int cb0x = src.getCbULX();
        int cb0y = src.getCbULY();

        for(int c=0; c<nc; c++) {
            cbI[c] = new CBlkInfo[mdl[c]+1][][][];
            lblock[c] = new int[mdl[c]+1][][][];
            ttIncl[c] = new TagTreeDecoder[mdl[c]+1][][];
            ttMaxBP[c] = new TagTreeDecoder[mdl[c]+1][][];
            numPrec[c] = new Point[mdl[c]+1];
            ppinfo[c] = new PrecInfo[mdl[c]+1][];

            // Get the tile-component coordinates on the reference grid
            tcx0 = src.getResULX(c,mdl[c]);
            tcy0 = src.getResULY(c,mdl[c]);
            tcx1 = tcx0 + src.getTileCompWidth(tIdx,c,mdl[c]);
            tcy1 = tcy0 + src.getTileCompHeight(tIdx,c,mdl[c]);

            for(int r=0; r<=mdl[c]; r++) {

                // Tile's coordinates in the reduced resolution image domain
                trx0 = (int)Math.ceil(tcx0/(double)(1<<(mdl[c]-r)));
                try0 = (int)Math.ceil(tcy0/(double)(1<<(mdl[c]-r)));
                trx1 = (int)Math.ceil(tcx1/(double)(1<<(mdl[c]-r)));
                try1 = (int)Math.ceil(tcy1/(double)(1<<(mdl[c]-r)));

                // Calculate the maximum number of precincts for each
                // resolution level taking into account tile specific options.
                double twoppx = (double)getPPX(tIdx,c,r);
                double twoppy = (double)getPPY(tIdx,c,r);
                numPrec[c][r] = new Point();
                if (trx1>trx0) {
                    numPrec[c][r].x = (int)Math.ceil((trx1-cb0x)/twoppx)
                        - (int)Math.floor((trx0-cb0x)/twoppx);
                } else {
                    numPrec[c][r].x = 0;
                }
                if (try1>try0) {
                    numPrec[c][r].y = (int)Math.ceil((try1-cb0y)/twoppy)
                        - (int)Math.floor((try0-cb0y)/twoppy);
                } else {
                    numPrec[c][r].y = 0;
                }

                // First and last subbands indexes
                mins = (r==0) ? 0 : 1;
                maxs = (r==0) ? 1 : 4;

                int maxPrec = numPrec[c][r].x * numPrec[c][r].y;

                ttIncl[c][r] = new TagTreeDecoder[maxPrec][maxs+1];
                ttMaxBP[c][r] = new TagTreeDecoder[maxPrec][maxs+1];
                cbI[c][r] = new CBlkInfo[maxs+1][][];
                lblock[c][r] = new int[maxs+1][][];

                ppinfo[c][r] = new PrecInfo[maxPrec];
                fillPrecInfo(c,r,mdl[c]);

                root = (SubbandSyn)src.getSynSubbandTree(tIdx,c);
                for(int s=mins; s<maxs; s++){
                    sb = (SubbandSyn)root.getSubbandByIdx(r,s);
                    nBlk = sb.numCb;

                    cbI[c][r][s] = new CBlkInfo[nBlk.y][nBlk.x];
                    lblock[c][r][s] = new int[nBlk.y][nBlk.x];

                    for(int i=nBlk.y-1;i>=0;i--) {
                        ArrayUtil.intArraySet(lblock[c][r][s][i],INIT_LBLOCK);
                    }
                } // loop on subbands
            } // End loop on resolution levels
        } // End loop on components

        return cbI;
    }

    /** 
     * Retrives precincts and code-blocks coordinates in the given resolution,
     * level and component. Finishes TagTreeEncoder initialization as well.
     *
     * @param c Component index.
     *
     * @param r Resolution level index.
     *
     * @param mdl Number of decomposition level in component <tt>c</tt>.
     * */
    private void fillPrecInfo(int c,int r,int mdl) {
        if(ppinfo[c][r].length==0) return; // No precinct in this
        // resolution level

        Point tileI = src.getTile(null);
        Point nTiles = src.getNumTiles(null);

        int xsiz,ysiz,x0siz,y0siz;
        int xt0siz,yt0siz;
        int xtsiz,ytsiz;

        xt0siz = src.getTilePartULX();
        yt0siz = src.getTilePartULY();
        xtsiz = src.getNomTileWidth();
        ytsiz = src.getNomTileHeight();
        x0siz = hd.getImgULX();
        y0siz = hd.getImgULY();
        xsiz = hd.getImgWidth();
        ysiz = hd.getImgHeight();

        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;

        int xrsiz = hd.getCompSubsX(c);
        int yrsiz = hd.getCompSubsY(c);

        int tcx0 = src.getResULX(c,mdl);
        int tcy0 = src.getResULY(c,mdl);
        int tcx1 = tcx0 + src.getTileCompWidth(tIdx,c,mdl);
        int tcy1 = tcy0 + src.getTileCompHeight(tIdx,c,mdl);

        int ndl = mdl-r;
        int trx0 = (int)Math.ceil(tcx0/(double)(1<<ndl));
        int try0 = (int)Math.ceil(tcy0/(double)(1<<ndl));
        int trx1 = (int)Math.ceil(tcx1/(double)(1<<ndl));
        int try1 = (int)Math.ceil(tcy1/(double)(1<<ndl));

        int cb0x = src.getCbULX();
        int cb0y = src.getCbULY();

        double twoppx = (double)getPPX(tIdx,c,r);
        double twoppy = (double)getPPY(tIdx,c,r);
        int twoppx2 = (int)(twoppx/2);
        int twoppy2 = (int)(twoppy/2);

        // Precincts are located at (cb0x+i*twoppx,cb0y+j*twoppy)
        // Valid precincts are those which intersect with the current
        // resolution level
        int maxPrec = ppinfo[c][r].length;
        int nPrec = 0;

        int istart = (int)Math.floor((try0-cb0y)/twoppy);
        int iend = (int)Math.floor((try1-1-cb0y)/twoppy);
        int jstart = (int)Math.floor((trx0-cb0x)/twoppx);
        int jend = (int)Math.floor((trx1-1-cb0x)/twoppx);

        int acb0x,acb0y;

        SubbandSyn root = src.getSynSubbandTree(tIdx,c);
        SubbandSyn sb = null;

        int p0x,p0y,p1x,p1y; // Precinct projection in subband
        int s0x,s0y,s1x,s1y; // Active subband portion
        int cw,ch;
        int kstart,kend,lstart,lend,k0,l0;
        int prg_ulx,prg_uly;
        int prg_w = (int)twoppx<<ndl;
        int prg_h = (int)twoppy<<ndl;
        int tmp1,tmp2;

        CBlkCoordInfo cb;

        for(int i=istart; i<=iend; i++) { // Vertical precincts
            for(int j=jstart; j<=jend; j++,nPrec++) { // Horizontal precincts
                if(j==jstart && (trx0-cb0x)%(xrsiz*((int)twoppx))!=0) {
                    prg_ulx = tx0;
                } else {
                    prg_ulx = cb0x+j*xrsiz*((int)twoppx<<ndl);
                }
                if(i==istart && (try0-cb0y)%(yrsiz*((int)twoppy))!=0) {
                    prg_uly = ty0;
                } else {
                    prg_uly = cb0y+i*yrsiz*((int)twoppy<<ndl);
                }

                ppinfo[c][r][nPrec] =
                    new PrecInfo(r,(int)(cb0x+j*twoppx),(int)(cb0y+i*twoppy),
                                 (int)twoppx,(int)twoppy,
                                 prg_ulx,prg_uly,prg_w,prg_h);

                if(r==0) { // LL subband
                    acb0x = cb0x;
                    acb0y = cb0y;

                    p0x = acb0x+j*(int)twoppx;
                    p1x = p0x + (int)twoppx;
                    p0y = acb0y+i*(int)twoppy;
                    p1y = p0y + (int)twoppy;

                    sb = (SubbandSyn)root.getSubbandByIdx(0,0);
                    s0x = (p0x<sb.ulcx) ? sb.ulcx : p0x;
                    s1x = (p1x>sb.ulcx+sb.w) ? sb.ulcx+sb.w : p1x;
                    s0y = (p0y<sb.ulcy) ? sb.ulcy : p0y;
                    s1y = (p1y>sb.ulcy+sb.h) ? sb.ulcy+sb.h : p1y;

                    // Code-blocks are located at (acb0x+k*cw,acb0y+l*ch)
                    cw = sb.nomCBlkW;
                    ch = sb.nomCBlkH;
                    k0 = (int)Math.floor((sb.ulcy-acb0y)/(double)ch);
                    kstart = (int)Math.floor((s0y-acb0y)/(double)ch);
                    kend = (int)Math.floor((s1y-1-acb0y)/(double)ch);
                    l0 = (int)Math.floor((sb.ulcx-acb0x)/(double)cw);
                    lstart = (int)Math.floor((s0x-acb0x)/(double)cw);
                    lend = (int)Math.floor((s1x-1-acb0x)/(double)cw);

                    if(s1x-s0x<=0 || s1y-s0y<=0) {
                        ppinfo[c][r][nPrec].nblk[0] = 0;
                        ttIncl[c][r][nPrec][0] = new TagTreeDecoder(0,0);
                        ttMaxBP[c][r][nPrec][0] = new TagTreeDecoder(0,0);
                    } else {
                        ttIncl[c][r][nPrec][0] =
                            new TagTreeDecoder(kend-kstart+1,lend-lstart+1);
                        ttMaxBP[c][r][nPrec][0] =
                            new TagTreeDecoder(kend-kstart+1,lend-lstart+1);
                        ppinfo[c][r][nPrec].cblk[0] =
                            new CBlkCoordInfo[kend-kstart+1][lend-lstart+1];
                        ppinfo[c][r][nPrec].
                            nblk[0] = (kend-kstart+1)*(lend-lstart+1);

                        for(int k=kstart; k<=kend; k++) { // Vertical cblks
                            for(int l=lstart; l<=lend; l++) { // Horiz. cblks
                                cb = new CBlkCoordInfo(k-k0,l-l0);
                                if(l==l0) {
                                    cb.ulx = sb.ulx;
                                } else {
                                    cb.ulx = sb.ulx+l*cw-(sb.ulcx-acb0x);
                                }
                                if(k==k0) {
                                    cb.uly = sb.uly;
                                } else {
                                    cb.uly = sb.uly+k*ch-(sb.ulcy-acb0y);
                                }
                                tmp1 = acb0x+l*cw;
                                tmp1 = (tmp1>sb.ulcx) ? tmp1 : sb.ulcx;
                                tmp2 = acb0x+(l+1)*cw;
                                tmp2 = (tmp2>sb.ulcx+sb.w) ?
                                    sb.ulcx+sb.w : tmp2;
                                cb.w = tmp2-tmp1;
                                tmp1 = acb0y+k*ch;
                                tmp1 = (tmp1>sb.ulcy) ? tmp1 : sb.ulcy;
                                tmp2 = acb0y+(k+1)*ch;
                                tmp2 = (tmp2>sb.ulcy+sb.h) ?
                                    sb.ulcy+sb.h : tmp2;
                                cb.h = tmp2-tmp1;
                                ppinfo[c][r][nPrec].
                                    cblk[0][k-kstart][l-lstart] = cb;
                            } // Horizontal code-blocks
                        } // Vertical code-blocks
                    }
                } else { // HL, LH and HH subbands
                    // HL subband
                    acb0x = 0;
                    acb0y = cb0y;

                    p0x = acb0x+j*twoppx2;
                    p1x = p0x + twoppx2;
                    p0y = acb0y+i*twoppy2;
                    p1y = p0y + twoppy2;

                    sb = (SubbandSyn)root.getSubbandByIdx(r,1);
                    s0x = (p0x<sb.ulcx) ? sb.ulcx : p0x;
                    s1x = (p1x>sb.ulcx+sb.w) ? sb.ulcx+sb.w : p1x;
                    s0y = (p0y<sb.ulcy) ? sb.ulcy : p0y;
                    s1y = (p1y>sb.ulcy+sb.h) ? sb.ulcy+sb.h : p1y;

                    // Code-blocks are located at (acb0x+k*cw,acb0y+l*ch)
                    cw = sb.nomCBlkW;
                    ch = sb.nomCBlkH;
                    k0 = (int)Math.floor((sb.ulcy-acb0y)/(double)ch);
                    kstart = (int)Math.floor((s0y-acb0y)/(double)ch);
                    kend = (int)Math.floor((s1y-1-acb0y)/(double)ch);
                    l0 = (int)Math.floor((sb.ulcx-acb0x)/(double)cw);
                    lstart = (int)Math.floor((s0x-acb0x)/(double)cw);
                    lend = (int)Math.floor((s1x-1-acb0x)/(double)cw);

                    if(s1x-s0x<=0 || s1y-s0y<=0) {
                        ppinfo[c][r][nPrec].nblk[1] = 0;
                        ttIncl[c][r][nPrec][1] = new TagTreeDecoder(0,0);
                        ttMaxBP[c][r][nPrec][1] = new TagTreeDecoder(0,0);
                    } else {
                        ttIncl[c][r][nPrec][1] =
                            new TagTreeDecoder(kend-kstart+1,lend-lstart+1);
                        ttMaxBP[c][r][nPrec][1] =
                            new TagTreeDecoder(kend-kstart+1,lend-lstart+1);
                        ppinfo[c][r][nPrec].cblk[1] =
                            new CBlkCoordInfo[kend-kstart+1][lend-lstart+1];
                        ppinfo[c][r][nPrec].
                            nblk[1] = (kend-kstart+1)*(lend-lstart+1);

                        for(int k=kstart; k<=kend; k++) { // Vertical cblks
                            for(int l=lstart; l<=lend; l++) { // Horiz. cblks
                                cb = new CBlkCoordInfo(k-k0,l-l0);
                                if(l==l0) {
                                    cb.ulx = sb.ulx;
                                } else {
                                    cb.ulx = sb.ulx+l*cw-(sb.ulcx-acb0x);
                                }
                                if(k==k0) {
                                    cb.uly = sb.uly;
                                } else {
                                    cb.uly = sb.uly+k*ch-(sb.ulcy-acb0y);
                                }
                                tmp1 = acb0x+l*cw;
                                tmp1 = (tmp1>sb.ulcx) ? tmp1 : sb.ulcx;
                                tmp2 = acb0x+(l+1)*cw;
                                tmp2 = (tmp2>sb.ulcx+sb.w) ?
                                    sb.ulcx+sb.w : tmp2;
                                cb.w = tmp2-tmp1;
                                tmp1 = acb0y+k*ch;
                                tmp1 = (tmp1>sb.ulcy) ? tmp1 : sb.ulcy;
                                tmp2 = acb0y+(k+1)*ch;
                                tmp2 = (tmp2>sb.ulcy+sb.h) ?
                                    sb.ulcy+sb.h : tmp2;
                                cb.h = tmp2-tmp1;
                                ppinfo[c][r][nPrec].
                                    cblk[1][k-kstart][l-lstart] = cb;
                            } // Horizontal code-blocks
                        } // Vertical code-blocks
                    }

                    // LH subband
                    acb0x = cb0x;
                    acb0y = 0;

                    p0x = acb0x+j*twoppx2;
                    p1x = p0x + twoppx2;
                    p0y = acb0y+i*twoppy2;
                    p1y = p0y + twoppy2;

                    sb = (SubbandSyn)root.getSubbandByIdx(r,2);
                    s0x = (p0x<sb.ulcx) ? sb.ulcx : p0x;
                    s1x = (p1x>sb.ulcx+sb.w) ? sb.ulcx+sb.w : p1x;
                    s0y = (p0y<sb.ulcy) ? sb.ulcy : p0y;
                    s1y = (p1y>sb.ulcy+sb.h) ? sb.ulcy+sb.h : p1y;

                    // Code-blocks are located at (acb0x+k*cw,acb0y+l*ch)
                    cw = sb.nomCBlkW;
                    ch = sb.nomCBlkH;
                    k0 = (int)Math.floor((sb.ulcy-acb0y)/(double)ch);
                    kstart = (int)Math.floor((s0y-acb0y)/(double)ch);
                    kend = (int)Math.floor((s1y-1-acb0y)/(double)ch);
                    l0 = (int)Math.floor((sb.ulcx-acb0x)/(double)cw);
                    lstart = (int)Math.floor((s0x-acb0x)/(double)cw);
                    lend = (int)Math.floor((s1x-1-acb0x)/(double)cw);

                    if(s1x-s0x<=0 || s1y-s0y<=0) {
                        ppinfo[c][r][nPrec].nblk[2] = 0;
                        ttIncl[c][r][nPrec][2] = new TagTreeDecoder(0,0);
                        ttMaxBP[c][r][nPrec][2] = new TagTreeDecoder(0,0);
                    } else {
                        ttIncl[c][r][nPrec][2] =
                            new TagTreeDecoder(kend-kstart+1,lend-lstart+1);
                        ttMaxBP[c][r][nPrec][2] =
                            new TagTreeDecoder(kend-kstart+1,lend-lstart+1);
                        ppinfo[c][r][nPrec].cblk[2] =
                            new CBlkCoordInfo[kend-kstart+1][lend-lstart+1];
                        ppinfo[c][r][nPrec].
                            nblk[2] = (kend-kstart+1)*(lend-lstart+1);

                        for(int k=kstart; k<=kend; k++) { // Vertical cblks
                            for(int l=lstart; l<=lend; l++) { // Horiz cblks
                                cb = new CBlkCoordInfo(k-k0,l-l0);
                                if(l==l0) {
                                    cb.ulx = sb.ulx;
                                } else {
                                    cb.ulx = sb.ulx+l*cw-(sb.ulcx-acb0x);
                                }
                                if(k==k0) {
                                    cb.uly = sb.uly;
                                } else {
                                    cb.uly = sb.uly+k*ch-(sb.ulcy-acb0y);
                                }
                                tmp1 = acb0x+l*cw;
                                tmp1 = (tmp1>sb.ulcx) ? tmp1 : sb.ulcx;
                                tmp2 = acb0x+(l+1)*cw;
                                tmp2 = (tmp2>sb.ulcx+sb.w) ?
                                    sb.ulcx+sb.w : tmp2;
                                cb.w = tmp2-tmp1;
                                tmp1 = acb0y+k*ch;
                                tmp1 = (tmp1>sb.ulcy) ? tmp1 : sb.ulcy;
                                tmp2 = acb0y+(k+1)*ch;
                                tmp2 = (tmp2>sb.ulcy+sb.h) ?
                                    sb.ulcy+sb.h : tmp2;
                                cb.h = tmp2-tmp1;
                                ppinfo[c][r][nPrec].
                                    cblk[2][k-kstart][l-lstart] = cb;
                            } // Horizontal code-blocks
                        } // Vertical code-blocks
                    }

                    // HH subband
                    acb0x = 0;
                    acb0y = 0;

                    p0x = acb0x+j*twoppx2;
                    p1x = p0x + twoppx2;
                    p0y = acb0y+i*twoppy2;
                    p1y = p0y + twoppy2;

                    sb = (SubbandSyn)root.getSubbandByIdx(r,3);
                    s0x = (p0x<sb.ulcx) ? sb.ulcx : p0x;
                    s1x = (p1x>sb.ulcx+sb.w) ? sb.ulcx+sb.w : p1x;
                    s0y = (p0y<sb.ulcy) ? sb.ulcy : p0y;
                    s1y = (p1y>sb.ulcy+sb.h) ? sb.ulcy+sb.h : p1y;

                    // Code-blocks are located at (acb0x+k*cw,acb0y+l*ch)
                    cw = sb.nomCBlkW;
                    ch = sb.nomCBlkH;
                    k0 = (int)Math.floor((sb.ulcy-acb0y)/(double)ch);
                    kstart = (int)Math.floor((s0y-acb0y)/(double)ch);
                    kend = (int)Math.floor((s1y-1-acb0y)/(double)ch);
                    l0 = (int)Math.floor((sb.ulcx-acb0x)/(double)cw);
                    lstart = (int)Math.floor((s0x-acb0x)/(double)cw);
                    lend = (int)Math.floor((s1x-1-acb0x)/(double)cw);

                    if(s1x-s0x<=0 || s1y-s0y<=0) {
                        ppinfo[c][r][nPrec].nblk[3] = 0;
                        ttIncl[c][r][nPrec][3] = new TagTreeDecoder(0,0);
                        ttMaxBP[c][r][nPrec][3] = new TagTreeDecoder(0,0);
                    } else {
                        ttIncl[c][r][nPrec][3] =
                            new TagTreeDecoder(kend-kstart+1,lend-lstart+1);
                        ttMaxBP[c][r][nPrec][3] =
                            new TagTreeDecoder(kend-kstart+1,lend-lstart+1);
                        ppinfo[c][r][nPrec].cblk[3] =
                            new CBlkCoordInfo[kend-kstart+1][lend-lstart+1];
                        ppinfo[c][r][nPrec].
                            nblk[3] = (kend-kstart+1)*(lend-lstart+1);

                        for(int k=kstart; k<=kend; k++) { // Vertical cblks
                            for(int l=lstart; l<=lend; l++) { // Horiz cblks
                                cb = new CBlkCoordInfo(k-k0,l-l0);
                                if(l==l0) {
                                    cb.ulx = sb.ulx;
                                } else {
                                    cb.ulx = sb.ulx+l*cw-(sb.ulcx-acb0x);
                                }
                                if(k==k0) {
                                    cb.uly = sb.uly;
                                } else {
                                    cb.uly = sb.uly+k*ch-(sb.ulcy-acb0y);
                                }
                                tmp1 = acb0x+l*cw;
                                tmp1 = (tmp1>sb.ulcx) ? tmp1 : sb.ulcx;
                                tmp2 = acb0x+(l+1)*cw;
                                tmp2 = (tmp2>sb.ulcx+sb.w) ?
                                    sb.ulcx+sb.w : tmp2;
                                cb.w = tmp2-tmp1;
                                tmp1 = acb0y+k*ch;
                                tmp1 = (tmp1>sb.ulcy) ? tmp1 : sb.ulcy;
                                tmp2 = acb0y+(k+1)*ch;
                                tmp2 = (tmp2>sb.ulcy+sb.h) ?
                                    sb.ulcy+sb.h : tmp2;
                                cb.h = tmp2-tmp1;
                                ppinfo[c][r][nPrec].
                                    cblk[3][k-kstart][l-lstart] = cb;
                            } // Horizontal code-blocks
                        } // Vertical code-blocks
                    }

                }
            } // Horizontal precincts
        } // Vertical precincts
   }

   /** 
     * Gets the number of precincts in a given component and resolution level.
     *
     * @param c Component index
     *
     * @param r Resolution index
     * */
    public int getNumPrecinct(int c,int r) {
        return numPrec[c][r].x*numPrec[c][r].y;
    }

    /** 
     * Read specified packet head and found length of each code-block's piece
     * of codewords as well as number of skipped most significant bit-planes.
     *
     * @param l layer index
     *
     * @param r Resolution level index
     *
     * @param c Component index
     *
     * @param p Precinct index
     *
     * @param cbI CBlkInfo array of relevant component and resolution
     * level.
     *
     * @param nb The number of bytes to read in each tile before reaching
     * output rate (used by truncation mode)
     *
     * @return True if specified output rate or EOF is reached.
     * */
    public boolean readPktHead(int l,int r,int c,int p,CBlkInfo[][][] cbI,
                               int[] nb) throws IOException {

        CBlkInfo ccb;
        int nSeg;                   // number of segment to read
        int cbLen;                  // Length of cblk's code-words
        int ltp;                    // last truncation point index
        int passtype;               // coding pass type
        TagTreeDecoder tdIncl,tdBD;
        int tmp,tmp2,totnewtp,lblockCur,tpidx;
        int sumtotnewtp = 0;
        Point cbc;
        int startPktHead = ehs.getPos();
        if(startPktHead>=ehs.length()) {
            // EOF reached at the beginning of this packet head
            return true;
        }
        int tIdx = src.getTileIdx();
        PktHeaderBitReader bin;
        int mend,nend;
        int b;
        SubbandSyn sb;
        SubbandSyn root = src.getSynSubbandTree(tIdx,c);

        // If packed packet headers was used, use separate stream for reading
        // of packet headers
        if(pph) {
            bin = new PktHeaderBitReader(pphbais);
        } else {
            bin = this.bin;
        }

        int mins = (r==0) ? 0 : 1;
        int maxs = (r==0) ? 1 : 4;

        boolean precFound = false;
        for(int s=mins; s<maxs; s++) {
            if(p<ppinfo[c][r].length) {
                precFound = true;
            }
        }
        if(!precFound) {
            return false;
        }

        PrecInfo prec = ppinfo[c][r][p];

        // Synchronize for bit reading
        bin.sync();

        // If packet is empty there is no info in it (i.e. no code-blocks)
        if(bin.readBit()==0) {
            // No code-block is included
            cblks = new Vector[maxs+1];
            for(int s=mins; s<maxs; s++){
                cblks[s] = new Vector();
            }
            pktIdx++;

            // If truncation mode, checks if output rate is reached
            // unless ncb quit condition is used in which case headers
            // are not counted
            if(isTruncMode && maxCB == -1) {
                tmp = ehs.getPos()-startPktHead;
                if(tmp>nb[tIdx]) {
                    nb[tIdx] = 0;
                    return true;
                } else {
                    nb[tIdx] -= tmp;
                }
            }

            // Read EPH marker if needed
            if(ephUsed) {
                readEPHMarker(bin);
            }
            return false;
        }

        // Packet is not empty => decode info
        // Loop on each subband in this resolution level
        if(cblks==null || cblks.length<maxs+1) {
            cblks = new Vector[maxs+1];
        }

        for(int s=mins; s<maxs; s++) {
            if(cblks[s]==null) {
                cblks[s] = new Vector();
            } else {
                cblks[s].removeAllElements();
            }
            sb = (SubbandSyn)root.getSubbandByIdx(r,s);
            // No code-block in this precinct
            if(prec.nblk[s]==0) {
                // Go to next subband
                continue;
            }

            tdIncl = ttIncl[c][r][p][s];
            tdBD = ttMaxBP[c][r][p][s];

            mend = (prec.cblk[s]==null) ? 0 : prec.cblk[s].length;
            for(int m=0; m<mend; m++) { // Vertical code-blocks
                nend = (prec.cblk[s][m]==null) ? 0 : prec.cblk[s][m].length;
                for (int n=0; n<nend; n++) { // Horizontal code-blocks
                    cbc = prec.cblk[s][m][n].idx;
                    b = cbc.x+cbc.y*sb.numCb.x;

                    ccb = cbI[s][cbc.y][cbc.x];

                    try {
                        // If code-block not included in previous layer(s)
                        if(ccb==null || ccb.ctp==0) {
                            if(ccb==null) {
                                ccb = cbI[s][cbc.y][cbc.x] =
                                    new CBlkInfo(prec.cblk[s][m][n].ulx,
                                                 prec.cblk[s][m][n].uly,
                                                 prec.cblk[s][m][n].w,
                                                 prec.cblk[s][m][n].h,nl);
                            }
                            ccb.pktIdx[l] = pktIdx;

                            // Read inclusion using tag-tree
                            tmp = tdIncl.update(m,n,l+1,bin);
                            if(tmp>l) { // Not included
                                continue;
                            }

                            // Read bitdepth using tag-tree
                            tmp = 1;// initialization
                            for(tmp2=1; tmp>=tmp2; tmp2++) {
                                tmp = tdBD.update(m,n,tmp2,bin);
                            }
                            ccb.msbSkipped = tmp2-2;

                            // New code-block => at least one truncation point
                            totnewtp = 1;
                            ccb.addNTP(l,0);

                            // Check whether ncb quit condition is reached
                            ncb++;
                            if(maxCB != -1 && !ncbQuit && ncb == maxCB){
                                // ncb quit contidion reached
                                ncbQuit = true;
                                tQuit = tIdx;
                                cQuit = c;
                                sQuit = s;
                                rQuit = r;
                                xQuit = cbc.x;
                                yQuit = cbc.y;
                           }

                        } else { // If code-block already included in one of
                            // the previous layers.

                            ccb.pktIdx[l] = pktIdx;

                            // If not inclused
                            if(bin.readBit()!=1) {
                                continue;
                            }

                            // At least 1 more truncation point than
                            // prev. packet
                            totnewtp = 1;
                        }

                        // Read new truncation points
                        if(bin.readBit()==1) {// if bit is 1
                            totnewtp++;

                            // if next bit is 0 do nothing
                            if(bin.readBit()==1) {//if is 1
                                totnewtp++;

                                tmp = bin.readBits(2);
                                totnewtp += tmp;
                                // If next 2 bits are not 11 do nothing
                                if(tmp==0x3) { //if 11
                                    tmp = bin.readBits(5);
                                    totnewtp += tmp;

                                // If next 5 bits are not 11111 do nothing
                                    if(tmp==0x1F) { //if 11111
                                        totnewtp += bin.readBits(7);
                                    }
                                }
                            }
                        }
                        ccb.addNTP(l,totnewtp);
                        sumtotnewtp += totnewtp;
                        cblks[s].addElement(prec.cblk[s][m][n]);

                        // Code-block length

                        // -- Compute the number of bit to read to obtain
                        // code-block length.  
                        // numBits = betaLamda + log2(totnewtp);

                        // The length is signalled for each segment in
                        // addition to the final one. The total length is the
                        // sum of all segment lengths.

                        // If regular termination in use, then there is one
                        // segment per truncation point present. Otherwise, if
                        // selective arithmetic bypass coding mode is present,
                        // then there is one termination per bypass/MQ and
                        // MQ/bypass transition. Otherwise the only
                        // termination is at the end of the code-block.
                        int options =
                            ((Integer)decSpec.ecopts.getTileCompVal(tIdx,c)).
                            intValue();

                        if( (options&OPT_TERM_PASS) != 0) {
                            // Regular termination in use, one segment per new
                            // pass (i.e. truncation point)
                            nSeg = totnewtp;
                        } else if( (options&OPT_BYPASS) != 0) {
                            // Selective arithmetic coding bypass coding mode
                            // in use, but no regular termination 1 segment up
                            // to the end of the last pass of the 4th most
                            // significant bit-plane, and, in each following
                            // bit-plane, one segment upto the end of the 2nd
                            // pass and one upto the end of the 3rd pass.

                            if(ccb.ctp<=FIRST_BYPASS_PASS_IDX) {
                                nSeg = 1;
                            } else {
                                nSeg = 1; // One at least for last pass
                                // And one for each other terminated pass
                                for(tpidx = ccb.ctp-totnewtp;
                                    tpidx < ccb.ctp-1; tpidx++) {
                                    if(tpidx >= FIRST_BYPASS_PASS_IDX-1) {
                                        passtype =
                                            (tpidx+NUM_EMPTY_PASSES_IN_MS_BP)%
                                            NUM_PASSES;
                                        if (passtype==1 || passtype==2) {
                                            // bypass coding just before MQ
                                            // pass or MQ pass just before
                                            // bypass coding => terminated
                                            nSeg++;
                                        }
                                    }
                                }
                            }
                        } else {
                            // Nothing special in use, just one segment
                            nSeg = 1;
                        }

                        // Reads lblock increment (common to all segments)
                        while(bin.readBit()!=0) {
                            lblock[c][r][s][cbc.y][cbc.x]++;
                        }

                        if(nSeg==1) { // Only one segment in packet
                            cbLen = bin.readBits(lblock[c][r][s][cbc.y][cbc.x]+
                                                 MathUtil.log2(totnewtp));
                        } else {
                            // We must read one length per segment
                            ccb.segLen[l] = new int[nSeg];
                            cbLen = 0;
                            int j;
                            if((options&OPT_TERM_PASS) != 0) {
                                // Regular termination: each pass is terminated
                                for(tpidx=ccb.ctp-totnewtp,j=0;
                                    tpidx<ccb.ctp;tpidx++,j++) {

                                    lblockCur = lblock[c][r][s][cbc.y][cbc.x];

                                    tmp = bin.readBits(lblockCur);
                                    ccb.segLen[l][j] = tmp;
                                    cbLen += tmp;
                                }
                            } else {
                                // Bypass coding: only some passes are
                                // terminated
                                ltp = ccb.ctp-totnewtp-1;
                                for(tpidx = ccb.ctp-totnewtp, j=0;
                                    tpidx<ccb.ctp-1;tpidx++) {
                                    if(tpidx >= FIRST_BYPASS_PASS_IDX-1) {
                                        passtype =
                                            (tpidx+NUM_EMPTY_PASSES_IN_MS_BP)%
                                            NUM_PASSES;
                                        if (passtype==0) continue;

                                        lblockCur =
                                            lblock[c][r][s][cbc.y][cbc.x];
                                        tmp =
                                            bin.
                                            readBits(lblockCur+
                                                     MathUtil.log2(tpidx-ltp));
                                        ccb.segLen[l][j] = tmp;
                                        cbLen += tmp;
                                        ltp = tpidx;
                                        j++;
                                    }
                                }
                                // Last pass has always the length sent
                                lblockCur = lblock[c][r][s][cbc.y][cbc.x];
                                tmp = bin.readBits(lblockCur+
                                                   MathUtil.log2(tpidx-ltp));
                                cbLen += tmp;
                                ccb.segLen[l][j] = tmp;
                            }
                        }
                        ccb.len[l] = cbLen;

                        // If truncation mode, checks if output rate is reached
                        // unless ncb and lbody quit contitions used.
                        if(isTruncMode && maxCB==-1) {
                            tmp = ehs.getPos()-startPktHead;
                            if(tmp>nb[tIdx]) {
                                nb[tIdx] = 0;
                                // Remove found information in this code-block
                                if(l==0) {
                                    cbI[s][cbc.y][cbc.x] = null;
                                } else {
                                    ccb.off[l]=ccb.len[l]=0;
                                    ccb.ctp -= ccb.ntp[l];
                                    ccb.ntp[l] = 0;
                                    ccb.pktIdx[l] = -1;
                                }
                                return true;
                            }
                        }

                    } catch(EOFException e) {
                        // Remove found information in this code-block
                        if(l==0) {
                            cbI[s][cbc.y][cbc.x] = null;
                        } else {
                            ccb.off[l]=ccb.len[l]=0;
                            ccb.ctp -= ccb.ntp[l];
                            ccb.ntp[l] = 0;
                            ccb.pktIdx[l] = -1;
                        }
//                         throw new EOFException();
                        return true;
                    }
                } // End loop on horizontal code-blocks
            } // End loop on vertical code-blocks
        } // End loop on subbands

        // Read EPH marker if needed
        if(ephUsed) {
            readEPHMarker(bin);
        }

        pktIdx++;

        // If truncation mode, checks if output rate is reached
        if(isTruncMode && maxCB == -1) {
            tmp = ehs.getPos()-startPktHead;
            if(tmp>nb[tIdx]) {
                nb[tIdx] = 0;
                return true;
            } else {
                nb[tIdx] -= tmp;
            }
        }
        return false;
    }

    /** 
     * Reads specificied packet body in order to find offset of each
     * code-block's piece of codeword. This use the list of found code-blocks
     * in previous red packet head.
     *
     * @param l layer index
     *
     * @param r Resolution level index
     *
     * @param c Component index
     *
     * @param p Precinct index
     *
     * @param cbI CBlkInfo array of relevant component and resolution
     * level.
     *
     * @param nb The remainding number of bytes to read from the bit stream in
     * each tile before reaching the decoding rate (in truncation mode)
     *
     * @return True if decoding rate is reached 
     * */
    public boolean readPktBody(int l,int r,int c,int p,CBlkInfo[][][] cbI,
                               int[] nb) throws IOException {
        int curOff = ehs.getPos();
        Point curCB;
        CBlkInfo ccb;
        boolean stopRead = false;
        int tIdx = src.getTileIdx();
        Point cbc;

        boolean precFound = false;
        int mins = (r==0) ? 0 : 1;
        int maxs = (r==0) ? 1 : 4;
        for(int s=mins; s<maxs; s++) {
            if(p<ppinfo[c][r].length) {
                precFound = true;
            }
        }
        if(!precFound) {
            return false;
        }

        for(int s=mins; s<maxs; s++) {
            for(int numCB=0; numCB<cblks[s].size(); numCB++) {
                cbc = ((CBlkCoordInfo)cblks[s].elementAt(numCB)).idx;
                ccb = cbI[s][cbc.y][cbc.x];
                ccb.off[l] = curOff;
                curOff += ccb.len[l];
                try {
                    ehs.seek(curOff);
                } catch(EOFException e) {
                    if(l==0) {
                        cbI[s][cbc.y][cbc.x] = null;
                    } else {
                        ccb.off[l] = ccb.len[l]=0;
                        ccb.ctp -= ccb.ntp[l];
                        ccb.ntp[l] = 0;
                        ccb.pktIdx[l] = -1;
                    }
                    throw new EOFException();
                }

                // If truncation mode
                if(isTruncMode) {
                    if(stopRead || ccb.len[l]>nb[tIdx]) {
                        // Remove found information in this code-block
                        if(l==0) {
                            cbI[s][cbc.y][cbc.x] = null;
                        } else {
                            ccb.off[l] = ccb.len[l] = 0;
                            ccb.ctp -= ccb.ntp[l];
                            ccb.ntp[l] = 0;
                            ccb.pktIdx[l] = -1;
                        }
                        stopRead = true;
                    }
                    if(!stopRead) {
                        nb[tIdx] -= ccb.len[l];
                    }
                }
                // If ncb quit condition reached
                if(ncbQuit && r == rQuit && s == sQuit && cbc.x == xQuit &&
                   cbc.y == yQuit && tIdx == tQuit && c == cQuit) {
                    cbI[s][cbc.y][cbc.x] = null;
                    stopRead = true;
                }
            } // Loop on code-blocks
        } // End loop on subbands

        // Seek to the end of the packet
        ehs.seek(curOff);

        if(stopRead) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns the precinct partition width for the specified component,
     * resolution level and tile.
     *
     * @param t the tile index
     *
     * @param c The index of the component (between 0 and C-1)
     *
     * @param r The resolution level, from 0 to L.
     *
     * @return the precinct partition width for the specified component,
     * resolution level and tile.
     * */
    public final int getPPX(int t,int c,int r){
        return decSpec.pss.getPPX(t,c,r);
    }

    /**
     * Returns the precinct partition height for the specified component,
     * resolution level and tile.
     *
     * @param t the tile index
     *
     * @param c The index of the component (between 0 and C-1)
     *
     * @param rl The resolution level, from 0 to L.
     *
     * @return the precinct partition height in the specified component, for
     * the specified resolution level, for the current tile.
     * */
    public final int getPPY(int t,int c,int rl) {
        return decSpec.pss.getPPY(t,c,rl);
    }

    /**
     * Try to read a SOP marker and check that its sequence number if not out
     * of sequence. If so, an error is thrown.
     *
     * @param nBytes The number of bytes left to read from each tile
     *
     * @param p Precinct index
     *
     * @param r Resolution level index
     *
     * @param c Component index
     * */
    public boolean readSOPMarker(int[] nBytes,int p,int c,int r)
        throws IOException {
        int val;
        byte sopArray[] = new byte[6];
        int tIdx = src.getTileIdx();
        int mins = (r==0) ? 0 : 1;
        int maxs = (r==0) ? 1 : 4;
        boolean precFound = false;
        for(int s=mins; s<maxs; s++) {
            if(p<ppinfo[c][r].length) {
                precFound = true;
            }
        }
        if(!precFound) {
            return false;
        }

        // If SOP markers are not used, return
        if(!sopUsed) {
            return false;
        }

        // Check if SOP is used for this packet
        int pos = ehs.getPos();
        if( (short)((ehs.read()<<8) | ehs.read()) != Markers.SOP ) {
            ehs.seek(pos);
            return false;
        }
        ehs.seek(pos);

        // If length of SOP marker greater than remaining bytes to read for
        // this tile return true
        if(nBytes[tIdx]<6) {
            return true;
        }
        nBytes[tIdx] -= 6;

        // Read marker into array 'sopArray'
        ehs.readFully(sopArray,0,Markers.SOP_LENGTH);

        // Check if this is the correct marker
        val = sopArray[0];
        val <<= 8;
        val |= sopArray[1];
        if(val!=Markers.SOP) {
            throw new Error("Corrupted Bitstream: Could not parse SOP "+
                            "marker !");
        }

        // Check if length is correct
        val = (sopArray[2]&0xff);
        val <<= 8;
        val |= (sopArray[3]&0xff);
        if(val!=4) {
            throw new Error("Corrupted Bitstream: Corrupted SOP marker !");
        }

        // Check if sequence number if ok
        val = (sopArray[4]&0xff);
        val <<= 8;
        val |= (sopArray[5]&0xff);

        if(!pph && val!=pktIdx) {
            throw new Error("Corrupted Bitstream: SOP marker out of "
                            +"sequence !");
        }
        if(pph && val!=pktIdx-1) {
            // if packed packet headers are used, packet header was read
            // before SOP marker segment
            throw new Error("Corrupted Bitstream: SOP marker out of "
                            +"sequence !");
        }
        return false;
    }

    /**
     * Try to read an EPH marker. If it is not possible then an Error is
     * thrown.
     *
     * @param bin The packet header reader to read the EPH marker from
     * */
    public void readEPHMarker(PktHeaderBitReader bin) throws IOException {
        int val;
        byte ephArray[] = new byte[2];

        if(bin.usebais) {
            bin.bais.read(ephArray,0,Markers.EPH_LENGTH);
        } else {
            bin.in.readFully(ephArray,0,Markers.EPH_LENGTH);
        }

        // Check if this is the correct marker
        val = ephArray[0];
        val <<= 8;
        val |= ephArray[1];
        if (val!=Markers.EPH) {
            throw new Error("Corrupted Bitstream: Could not parse EPH "
                            +"marker ! ");
        }
    }

    /** 
     * Get PrecInfo instance of the specified resolution level, component and
     * precinct.
     *
     * @param c Component index.
     *
     * @param r Resolution level index.
     *
     * @param p Precinct index.
     * */
    public PrecInfo getPrecInfo(int c,int r,int p) {
        return ppinfo[c][r][p];
    }

}
