/*
* $RCSfile: PktEncoder.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:03 $
* $State: Exp $
*
* Class: PktEncoder
*
* Description: Builds bit stream packets and keeps
* interpacket dependencies.
*
*
*
* 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.writer;
import java.awt.Point;
import jj2000.j2k.wavelet.analysis.*;
import jj2000.j2k.entropy.encoder.*;
import jj2000.j2k.codestream.*;
import jj2000.j2k.wavelet.*;
import jj2000.j2k.image.*;
import jj2000.j2k.util.*;
import jj2000.j2k.*;
import java.util.Vector;
import com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageWriteParamJava;
/**
* This class builds packets and keeps the state information of packet
* interdependencies. It also supports saving the state and reverting
* (restoring) to the last saved state, with the save() and restore() methods.
*
* <P>Each time the encodePacket() method is called a new packet is encoded,
* the packet header is returned by the method, and the packet body can be
* obtained with the getLastBodyBuf() and getLastBodyLen() methods.
* */
public class PktEncoder {
/** The prefix for packet encoding options: 'P' */
public final static char OPT_PREFIX = 'P';
/** The list of parameters that is accepted for packet encoding.*/
private final static String [][] pinfo = {
{ "Psop", "[<tile idx>] true|false"+
"[ [<tile idx>] true|false ...]",
"Specifies whether start of packet (SOP) markers should be used. "+
"'true' enables, 'false' disables it.","false"},
{ "Peph", "[<tile idx>] true|false"+
"[ [<tile idx>] true|false ...]",
"Specifies whether end of packet header (EPH) markers should be "+
" used. 'true' enables, 'false' disables it.","false"}
};
/** The initial value for the lblock */
private final static int INIT_LBLOCK = 3;
/** The source object */
private CodedCBlkDataSrcEnc infoSrc;
/** The encoder specs */
J2KImageWriteParamJava wp;
/**
* The tag tree for inclusion information. The indexes are outlined
* below. Note that the layer indexes start at 1, therefore, the layer
* index minus 1 is used. The subband indices are used as they are defined
* in the Subband class. The tile indices start at 0 and follow a
* lexicographical order.
*
* <ul>
* <li>1st index: tile index, in lexicographical order</li>
* <li>2nd index: component index </li>
* <li>3rd index: resolution level </li>
* <li>4th index: subband index </li>
* <li>5th index: precinct index </li>
* </ul>
**/
private TagTreeEncoder ttIncl[][][][][];
/**
* The tag tree for the maximum significant bit-plane. The indexes are
* outlined below. Note that the layer indexes start at 1, therefore, the
* layer index minus 1 is used. The subband indices are used as they are
* defined in the Subband class. The tile indices start at 0 and follow a
* lexicographical order.
*
* <ul>
* <li>1st index: tile index, in lexicographical order</li>
* <li>2nd index: component index </li>
* <li>3rd index: resolution level </li>
* <li>4th index: subband index - subband index offset </li>
* <li>5th index: precinct index </li>
* </ul>
* */
private TagTreeEncoder ttMaxBP[][][][][];
/**
* The base number of bits for sending code-block length information
* (referred as Lblock in the JPEG 2000 standard). The indexes are
* outlined below. Note that the layer indexes start at 1, therefore, the
* layer index minus 1 is used. The subband indices are used as they are
* defined in the Subband class. The tile indices start at 0 and follow a
* lexicographical order.
*
* <ul>
* <li>1st index: tile index, in lexicographical order </li>
* <li>2nd index: component index </li>
* <li>3rd index: resolution level </li>
* <li>4th index: subband index - subband index offset </li>
* <li>5th index: code-block index, in lexicographical order</li>
* </ul>
* */
private int lblock[][][][][];
/**
* The last encoded truncation point for each code-block. A negative value
* means that no information has been included for the block, yet. The
* indexes are outlined below. The subband indices are used as they are
* defined in the Subband class. The tile indices start at 0 and follow a
* lexicographical order. The code-block indices follow a lexicographical
* order within the subband tile.
*
* <P>What is actually stored is the index of the element in
* CBlkRateDistStats.truncIdxs that gives the real truncation point.
*
* <ul>
* <li>1st index: tile index, in lexicographical order </li>
* <li>2nd index: component index </li>
* <li>3rd index: resolution level </li>
* <li>4th index: subband index - subband index offset </li>
* <li>5th index: code-block index, in lexicographical order </li>
* </ul>
* */
private int prevtIdxs[][][][][];
/**
* The saved base number of bits for sending code-block length
* information. It is used for restoring previous saved state by
* restore(). The indexes are outlined below. Note that the layer indexes
* start at 1, therefore, the layer index minus 1 is used. The subband
* indices are used as they are defined in the Subband class. The tile
* indices start at 0 and follow a lexicographical order.
*
* <ul>
* <li>1st index: tile index, in lexicographical order </li>
* <li>2nd index: component index </li>
* <li>3rd index: resolution level </li>
* <li>4th index: subband index - subband index offset </li>
* <li>5th index: code-block index, in lexicographical order</li>
* </ul>
* */
private int bak_lblock[][][][][];
/**
* The saved last encoded truncation point for each code-block. It is used
* for restoring previous saved state by restore(). A negative value means
* that no information has been included for the block, yet. The indexes
* are outlined below. The subband indices are used as they are defined in
* the Subband class. The tile indices start at 0 and follow a
* lexicographical order. The code-block indices follow a lexicographical
* order within the subband tile.
*
* <ul>
* <li>1st index: tile index, in lexicographical order </li>
* <li>2nd index: component index </li>
* <li>3rd index: resolution level </li>
* <li>4th index: subband index - subband index offset </li>
* <li>5th index: code-block index, in lexicographical order </li>
* </ul>
* */
private int bak_prevtIdxs[][][][][];
/** The body buffer of the last encoded packet */
private byte[] lbbuf;
/** The body length of the last encoded packet */
private int lblen;
/** The saved state */
private boolean saved;
/** Whether or not there is ROI information in the last encoded Packet */
private boolean roiInPkt = false;
/** Length to read in current packet body to get all the ROI information */
private int roiLen = 0;
/**
* Array containing the coordinates, width, height, indexes, ... of the
* precincts.
*
* <ul>
* <li> 1st dim: tile index.</li>
* <li> 2nd dim: component index.</li>
* <li> 3rd dim: resolution level index.</li>
* <li> 4th dim: precinct index.</li>
* </ul>
* */
private PrecInfo ppinfo[][][][];
/** Whether or not the current packet is writable */
private boolean packetWritable;
/**
* Creates a new packet header encoder, using the information from the
* 'infoSrc' object. The information used is the number of components,
* number of tiles, subband decomposition, etc.
*
* <P>Note that this constructor visits all the tiles in the 'infoSrc'
* object. The 'infoSrc' object is left at the original tile (i.e. the
* current tile before calling this constructor), but any side effects of
* visiting the tiles is not reverted.
*
* @param infoSrc The source of information to construct the
* object.
*
* @param encSpec The parameters for the encoding
*
* @param maxNumPrec Maximum number of precinct in each tile, component
* and resolution level.
*
* @param pl ParameterList instance that holds command line options
* */
public PktEncoder(CodedCBlkDataSrcEnc infoSrc, J2KImageWriteParamJava wp,
Point[][][] numPrec) {
this.infoSrc = infoSrc;
this.wp = wp;
// this.numPrec = numPrec;
// Get number of components and tiles
int nc = infoSrc.getNumComps();
int nt = infoSrc.getNumTiles();
// Do initial allocation
ttIncl = new TagTreeEncoder[nt][nc][][][];
ttMaxBP = new TagTreeEncoder[nt][nc][][][];
lblock = new int[nt][nc][][][];
prevtIdxs = new int[nt][nc][][][];
ppinfo = new PrecInfo[nt][nc][][];
// Finish allocation
SubbandAn root,sb;
int maxs,mins;
int mrl;
Point tmpCoord = null;
int numcb; // Number of code-blocks
Vector cblks = null;
infoSrc.setTile(0,0);
for (int t=0; t<nt; t++) { // Loop on tiles
for (int c=0; c<nc; c++) { // Loop on components
// Get number of resolution levels
root = infoSrc.getAnSubbandTree(t,c);
mrl = root.resLvl;
lblock[t][c] = new int[mrl+1][][];
ttIncl[t][c] = new TagTreeEncoder[mrl+1][][];
ttMaxBP[t][c] = new TagTreeEncoder[mrl+1][][];
prevtIdxs[t][c] = new int[mrl+1][][];
ppinfo[t][c] = new PrecInfo[mrl+1][];
for(int r=0; r<=mrl; r++) { // Loop on resolution levels
mins = (r==0) ? 0 : 1;
maxs = (r==0) ? 1 : 4;
int maxPrec = numPrec[t][c][r].x*numPrec[t][c][r].y;
ttIncl[t][c][r] = new TagTreeEncoder[maxPrec][maxs];
ttMaxBP[t][c][r] = new TagTreeEncoder[maxPrec][maxs];
prevtIdxs[t][c][r] = new int[maxs][];
lblock[t][c][r] = new int[maxs][];
// Precincts and code-blocks
ppinfo[t][c][r] = new PrecInfo[maxPrec];
fillPrecInfo(t,c,r);
for(int s=mins; s<maxs; s++) {
// Loop on subbands
sb = (SubbandAn)root.getSubbandByIdx(r,s);
numcb = sb.numCb.x*sb.numCb.y;
lblock[t][c][r][s] = new int[numcb];
ArrayUtil.intArraySet(lblock[t][c][r][s],INIT_LBLOCK);
prevtIdxs[t][c][r][s] = new int[numcb];
ArrayUtil.intArraySet(prevtIdxs[t][c][r][s],-1);
}
}
}
if(t!=nt-1) infoSrc.nextTile();
}
}
/**
* Retrives precincts and code-blocks coordinates in the given resolution,
* component and tile. It terminates TagTreeEncoder initialization as
* well.
*
* @param t Tile index.
*
* @param c Component index.
*
* @param r Resolution level index.
* */
private void fillPrecInfo(int t,int c,int r) {
if(ppinfo[t][c][r].length==0) return; // No precinct in this
// resolution level
Point tileI = infoSrc.getTile(null);
Point nTiles = infoSrc.getNumTiles(null);
int x0siz = infoSrc.getImgULX();
int y0siz = infoSrc.getImgULY();
int xsiz = x0siz + infoSrc.getImgWidth();
int ysiz = y0siz + infoSrc.getImgHeight();
int xt0siz = infoSrc.getTilePartULX();
int yt0siz = infoSrc.getTilePartULY();
int xtsiz = infoSrc.getNomTileWidth();
int ytsiz = infoSrc.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;
int xrsiz = infoSrc.getCompSubsX(c);
int yrsiz = infoSrc.getCompSubsY(c);
int tcx0 = (int)Math.ceil(tx0/(double)(xrsiz));
int tcy0 = (int)Math.ceil(ty0/(double)(yrsiz));
int tcx1 = (int)Math.ceil(tx1/(double)(xrsiz));
int tcy1 = (int)Math.ceil(ty1/(double)(yrsiz));
int ndl = infoSrc.getAnSubbandTree(t,c).resLvl-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 = infoSrc.getCbULX();
int cb0y = infoSrc.getCbULY();
double twoppx = (double)wp.getPrecinctPartition().getPPX(t,c,r);
double twoppy = (double)wp.getPrecinctPartition().getPPY(t,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[t][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;
SubbandAn root = infoSrc.getAnSubbandTree(t,c);
SubbandAn 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;
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[t][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 = (SubbandAn)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[t][c][r][nPrec].nblk[0] = 0;
ttIncl[t][c][r][nPrec][0] = new TagTreeEncoder(0,0);
ttMaxBP[t][c][r][nPrec][0] = new TagTreeEncoder(0,0);
} else {
ttIncl[t][c][r][nPrec][0] =
new TagTreeEncoder(kend-kstart+1,lend-lstart+1);
ttMaxBP[t][c][r][nPrec][0] =
new TagTreeEncoder(kend-kstart+1,lend-lstart+1);
ppinfo[t][c][r][nPrec].cblk[0] =
new CBlkCoordInfo[kend-kstart+1][lend-lstart+1];
ppinfo[t][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);
ppinfo[t][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 = (SubbandAn)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[t][c][r][nPrec].nblk[1] = 0;
ttIncl[t][c][r][nPrec][1] = new TagTreeEncoder(0,0);
ttMaxBP[t][c][r][nPrec][1] = new TagTreeEncoder(0,0);
} else {
ttIncl[t][c][r][nPrec][1] =
new TagTreeEncoder(kend-kstart+1,lend-lstart+1);
ttMaxBP[t][c][r][nPrec][1] =
new TagTreeEncoder(kend-kstart+1,lend-lstart+1);
ppinfo[t][c][r][nPrec].cblk[1] =
new CBlkCoordInfo[kend-kstart+1][lend-lstart+1];
ppinfo[t][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);
ppinfo[t][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 = (SubbandAn)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[t][c][r][nPrec].nblk[2] = 0;
ttIncl[t][c][r][nPrec][2] = new TagTreeEncoder(0,0);
ttMaxBP[t][c][r][nPrec][2] = new TagTreeEncoder(0,0);
} else {
ttIncl[t][c][r][nPrec][2] =
new TagTreeEncoder(kend-kstart+1,lend-lstart+1);
ttMaxBP[t][c][r][nPrec][2] =
new TagTreeEncoder(kend-kstart+1,lend-lstart+1);
ppinfo[t][c][r][nPrec].cblk[2] =
new CBlkCoordInfo[kend-kstart+1][lend-lstart+1];
ppinfo[t][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);
ppinfo[t][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 = (SubbandAn)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[t][c][r][nPrec].nblk[3] = 0;
ttIncl[t][c][r][nPrec][3] = new TagTreeEncoder(0,0);
ttMaxBP[t][c][r][nPrec][3] = new TagTreeEncoder(0,0);
} else {
ttIncl[t][c][r][nPrec][3] =
new TagTreeEncoder(kend-kstart+1,lend-lstart+1);
ttMaxBP[t][c][r][nPrec][3] =
new TagTreeEncoder(kend-kstart+1,lend-lstart+1);
ppinfo[t][c][r][nPrec].cblk[3] =
new CBlkCoordInfo[kend-kstart+1][lend-lstart+1];
ppinfo[t][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);
ppinfo[t][c][r][nPrec].
cblk[3][k-kstart][l-lstart] = cb;
} // Horizontal code-blocks
} // Vertical code-blocks
}
}
} // Horizontal precincts
} // Vertical precincts
}
/**
* Encodes a packet and returns the buffer containing the encoded packet
* header. The code-blocks appear in a 3D array of CBlkRateDistStats,
* 'cbs'. The first index is the tile index in lexicographical order, the
* second index is the subband index (as defined in the Subband class),
* and the third index is the code-block index (whithin the subband tile)
* in lexicographical order as well. The indexes of the new truncation
* points for each code-block are specified by the 3D array of int
* 'tIndx'. The indices of this array are the same as for cbs. The
* truncation point indices in 'tIndx' are the indices of the elements of
* the 'truncIdxs' array, of the CBlkRateDistStats class, that give the
* real truncation points. If a truncation point index is negative it
* means that the code-block has not been included in any layer yet. If
* the truncation point is less than or equal to the highest truncation
* point used in previous layers then the code-block is not included in
* the packet. Otherwise, if larger, the code-block is included in the
* packet. The body of the packet can be obtained with the
* getLastBodyBuf() and getLastBodyLen() methods.
*
* <p>Layers must be coded in increasing order, in consecutive manner, for
* each tile, component and resolution level (e.g., layer 1, then layer 2,
* etc.). For different tile, component and/or resolution level no
* particular order must be followed.</p>
*
* @param ly The layer index (starts at 1).
*
* @param c The component index.
*
* @param r The resolution level
*
* @param t Index of the current tile
*
* @param cbs The 3D array of coded code-blocks.
*
* @param tIndx The truncation point indices for each code-block.
*
* @param hbuf The header buffer. If null a new BitOutputBuffer is created
* and returned. This buffer is reset before anything is written to it.
*
* @param bbuf The body buffer. If null a new one is created. If not large
* enough a new one is created.
*
* @param pIdx The precinct index.
*
* @return The buffer containing the packet header.
* */
public BitOutputBuffer encodePacket(int ly,int c,int r,int t,
CBlkRateDistStats cbs[][],
int tIndx[][],BitOutputBuffer hbuf,
byte bbuf[],int pIdx) {
int b,i,maxi;
int ncb;
int thmax;
int newtp;
int cblen;
int prednbits,nbits,deltabits;
TagTreeEncoder cur_ttIncl,cur_ttMaxBP; // inclusion and bit-depth tag
// trees
int cur_prevtIdxs[]; // last encoded truncation points
CBlkRateDistStats cur_cbs[];
int cur_tIndx[]; // truncation points to encode
int minsb = (r==0) ? 0 : 1;
int maxsb = (r==0) ? 1 : 4;
Point cbCoord = null;
SubbandAn root = infoSrc.getAnSubbandTree(t,c);
SubbandAn sb;
roiInPkt = false;
roiLen = 0;
int mend,nend;
// Checks if a precinct with such an index exists in this resolution
// level
if(pIdx>=ppinfo[t][c][r].length) {
packetWritable = false;
return hbuf;
}
PrecInfo prec = ppinfo[t][c][r][pIdx];
// First, we check if packet is empty (i.e precinct 'pIdx' has no
// code-block in any of the subbands)
boolean isPrecVoid = true;
for(int s=minsb; s<maxsb; s++) {
if(prec.nblk[s]==0) {
// The precinct has no code-block in this subband.
continue;
} else {
// The precinct is not empty in at least one subband ->
// stop
isPrecVoid = false;
break;
}
}
if(isPrecVoid) {
packetWritable = true;
if(hbuf == null) {
hbuf = new BitOutputBuffer();
} else {
hbuf.reset();
}
if (bbuf == null) {
lbbuf = bbuf = new byte[1];
}
hbuf.writeBit(0);
lblen = 0;
return hbuf;
}
if(hbuf == null) {
hbuf = new BitOutputBuffer();
} else {
hbuf.reset();
}
// Invalidate last body buffer
lbbuf = null;
lblen = 0;
// Signal that packet is present
hbuf.writeBit(1);
for(int s=minsb; s<maxsb; s++) { // Loop on subbands
sb = (SubbandAn)root.getSubbandByIdx(r,s);
// Go directly to next subband if the precinct has no code-block
// in the current one.
if(prec.nblk[s]==0) {
continue;
}
cur_ttIncl = ttIncl[t][c][r][pIdx][s];
cur_ttMaxBP = ttMaxBP[t][c][r][pIdx][s];
cur_prevtIdxs = prevtIdxs[t][c][r][s];
cur_cbs = cbs[s];
cur_tIndx = tIndx[s];
// Set tag tree values for code-blocks in this precinct
mend = (prec.cblk[s]==null) ? 0 : prec.cblk[s].length;
for(int m=0; m<mend; m++) {
nend = (prec.cblk[s][m]==null) ? 0 : prec.cblk[s][m].length;
for (int n=0; n<nend; n++) {
cbCoord = prec.cblk[s][m][n].idx;
b = cbCoord.x+cbCoord.y*sb.numCb.x;
if (cur_tIndx[b]>cur_prevtIdxs[b] && cur_prevtIdxs[b]<0) {
// First inclusion
cur_ttIncl.setValue(m,n,ly-1);
}
if (ly==1) { // First layer, need to set the skip of MSBP
cur_ttMaxBP.setValue(m,n,cur_cbs[b].skipMSBP);
}
}
}
// Now encode the information
for(int m=0; m<prec.cblk[s].length; m++) { // Vertical code-blocks
for (int n=0; n<prec.cblk[s][m].length; n++) { // Horiz. cblks
cbCoord = prec.cblk[s][m][n].idx;
b = cbCoord.x+cbCoord.y*sb.numCb.x;
// 1) Inclusion information
if (cur_tIndx[b]>cur_prevtIdxs[b]) {
// Code-block included in this layer
if (cur_prevtIdxs[b]<0) { // First inclusion
// Encode layer info
cur_ttIncl.encode(m,n,ly,hbuf);
// 2) Max bitdepth info. Encode value
thmax = cur_cbs[b].skipMSBP+1;
for (i=1; i<=thmax; i++) {
cur_ttMaxBP.encode(m,n,i,hbuf);
}
// Count body size for packet
lblen += cur_cbs[b].
truncRates[cur_cbs[b].truncIdxs[cur_tIndx[b]]];
} else { // Already in previous layer
// Send "1" bit
hbuf.writeBit(1);
// Count body size for packet
lblen +=
cur_cbs[b].
truncRates[cur_cbs[b].
truncIdxs[cur_tIndx[b]]] -
cur_cbs[b].
truncRates[cur_cbs[b].
truncIdxs[cur_prevtIdxs[b]]];
}
// 3) Truncation point information
if (cur_prevtIdxs[b]<0) {
newtp = cur_cbs[b].truncIdxs[cur_tIndx[b]];
} else {
newtp = cur_cbs[b].truncIdxs[cur_tIndx[b]]-
cur_cbs[b].truncIdxs[cur_prevtIdxs[b]]-1;
}
// Mix of switch and if is faster
switch (newtp) {
case 0:
hbuf.writeBit(0); // Send one "0" bit
break;
case 1:
hbuf.writeBits(2,2); // Send one "1" and one "0"
break;
case 2:
case 3:
case 4:
// Send two "1" bits followed by 2 bits
// representation of newtp-2
hbuf.writeBits((3<<2)|(newtp-2),4);
break;
default:
if (newtp <= 35) {
// Send four "1" bits followed by a five bits
// representation of newtp-5
hbuf.writeBits((15<<5)|(newtp-5),9);
} else if (newtp <= 163) {
// Send nine "1" bits followed by a seven bits
// representation of newtp-36
hbuf.writeBits((511<<7)|(newtp-36),16);
} else {
throw new
ArithmeticException("Maximum number "+
"of truncation "+
"points exceeded");
}
}
} else { // Block not included in this layer
if (cur_prevtIdxs[b]>=0) {
// Already in previous layer. Send "0" bit
hbuf.writeBit(0);
} else { // Not in any previous layers
cur_ttIncl.encode(m,n,ly,hbuf);
}
// Go to the next one.
continue;
}
// Code-block length
// We need to compute the maximum number of bits needed to
// signal the length of each terminated segment and the
// final truncation point.
newtp = 1;
maxi = cur_cbs[b].truncIdxs[cur_tIndx[b]];
cblen = (cur_prevtIdxs[b]<0) ? 0 :
cur_cbs[b].truncRates[cur_cbs[b].
truncIdxs[cur_prevtIdxs[b]]];
// Loop on truncation points
i = (cur_prevtIdxs[b]<0) ? 0 :
cur_cbs[b].truncIdxs[cur_prevtIdxs[b]]+1;
int minbits = 0;
for (; i<maxi; i++, newtp++) {
// If terminated truncation point calculate length
if (cur_cbs[b].isTermPass != null &&
cur_cbs[b].isTermPass[i]) {
// Calculate length
cblen = cur_cbs[b].truncRates[i] - cblen;
// Calculate number of needed bits
prednbits = lblock[t][c][r][s][b] +
MathUtil.log2(newtp);
minbits = ((cblen>0) ? MathUtil.log2(cblen) : 0)+1;
// Update Lblock increment if needed
for(int j=prednbits; j<minbits; j++) {
lblock[t][c][r][s][b]++;
hbuf.writeBit(1);
}
// Initialize for next length
newtp = 0;
cblen = cur_cbs[b].truncRates[i];
}
}
// Last truncation point length always sent
// Calculate length
cblen = cur_cbs[b].truncRates[i] - cblen;
// Calculate number of bits
prednbits = lblock[t][c][r][s][b] + MathUtil.log2(newtp);
minbits = ((cblen>0) ? MathUtil.log2(cblen) : 0)+1;
// Update Lblock increment if needed
for(int j=prednbits; j<minbits; j++) {
lblock[t][c][r][s][b]++;
hbuf.writeBit(1);
}
// End of comma-code increment
hbuf.writeBit(0);
// There can be terminated several segments, send length
// info for all terminated truncation points in addition
// to final one
newtp = 1;
maxi = cur_cbs[b].truncIdxs[cur_tIndx[b]];
cblen = (cur_prevtIdxs[b]<0) ? 0 :
cur_cbs[b].truncRates[cur_cbs[b].
truncIdxs[cur_prevtIdxs[b]]];
// Loop on truncation points and count the groups
i = (cur_prevtIdxs[b]<0) ? 0 :
cur_cbs[b].truncIdxs[cur_prevtIdxs[b]]+1;
for (; i<maxi; i++, newtp++) {
// If terminated truncation point, send length
if (cur_cbs[b].isTermPass != null &&
cur_cbs[b].isTermPass[i]) {
cblen = cur_cbs[b].truncRates[i] - cblen;
nbits = MathUtil.log2(newtp)+lblock[t][c][r][s][b];
hbuf.writeBits(cblen,nbits);
// Initialize for next length
newtp = 0;
cblen = cur_cbs[b].truncRates[i];
}
}
// Last truncation point length is always signalled
// First calculate number of bits needed to signal
// Calculate length
cblen = cur_cbs[b].truncRates[i] - cblen;
nbits = MathUtil.log2(newtp) + lblock[t][c][r][s][b];
hbuf.writeBits(cblen,nbits);
} // End loop on horizontal code-blocks
} // End loop on vertical code-blocks
} // End loop on subband
// -> Copy the data to the body buffer
// Ensure size for body data
if (bbuf==null || bbuf.length<lblen){
bbuf = new byte[lblen];
}
lbbuf = bbuf;
lblen = 0;
for (int s=minsb; s<maxsb; s++) { // Loop on subbands
sb = (SubbandAn)root.getSubbandByIdx(r,s);
cur_prevtIdxs = prevtIdxs[t][c][r][s];
cur_cbs = cbs[s];
cur_tIndx = tIndx[s];
ncb = cur_prevtIdxs.length;
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++) { // Horiz. cblks
cbCoord = prec.cblk[s][m][n].idx;
b = cbCoord.x+cbCoord.y*sb.numCb.x;
if (cur_tIndx[b]>cur_prevtIdxs[b]) {
// Block included in this precinct -> Copy data to
// body buffer and get code-size
if (cur_prevtIdxs[b]<0) {
cblen = cur_cbs[b].
truncRates[cur_cbs[b].truncIdxs[cur_tIndx[b]]];
System.arraycopy(cur_cbs[b].data,0,
lbbuf,lblen,cblen);
} else {
cblen = cur_cbs[b].
truncRates[cur_cbs[b].
truncIdxs[cur_tIndx[b]]] -
cur_cbs[b].
truncRates[cur_cbs[b].
truncIdxs[cur_prevtIdxs[b]]];
System.
arraycopy(cur_cbs[b].data,
cur_cbs[b].
truncRates[cur_cbs[b].
truncIdxs[cur_prevtIdxs
[b]]],
lbbuf,lblen,cblen);
}
lblen += cblen;
// Verifies if this code-block contains new ROI
// information
if(cur_cbs[b].nROIcoeff!=0 &&
(cur_prevtIdxs[b]==-1 ||
cur_cbs[b].truncIdxs[cur_prevtIdxs[b]] <=
cur_cbs[b].nROIcp-1) ) {
roiInPkt = true;
roiLen = lblen;
}
// Update truncation point
cur_prevtIdxs[b] = cur_tIndx[b];
}
} // End loop on horizontal code-blocks
} // End loop on vertical code-blocks
} // End loop on subbands
packetWritable = true;
// Must never happen
if(hbuf.getLength()==0) {
throw new Error("You have found a bug in PktEncoder, method:"+
" encodePacket");
}
return hbuf;
}
/**
* Returns the buffer of the body of the last encoded packet. The length
* of the body can be retrieved with the getLastBodyLen() method. The
* length of the array returned by this method may be larger than the
* actual body length.
*
* @return The buffer of body of the last encoded packet.
*
* @exception IllegalArgumentException If no packet has been coded since
* last reset(), last restore(), or object creation.
*
* @see #getLastBodyLen
* */
public byte[] getLastBodyBuf() {
if (lbbuf == null) {
throw new IllegalArgumentException();
}
return lbbuf;
}
/**
* Returns the length of the body of the last encoded packet, in
* bytes. The body itself can be retrieved with the getLastBodyBuf()
* method.
*
* @return The length of the body of last encoded packet, in bytes.
*
* @see #getLastBodyBuf
* */
public int getLastBodyLen() {
return lblen;
}
/**
* Saves the current state of this object. The last saved state
* can be restored with the restore() method.
*
* @see #restore
* */
public void save() {
int maxsbi,minsbi;
// Have we done any save yet?
if (bak_lblock==null) {
// Allocate backup buffers
bak_lblock = new int[ttIncl.length][][][][];
bak_prevtIdxs = new int[ttIncl.length][][][][];
for (int t=ttIncl.length-1; t>=0; t--) {
bak_lblock[t] = new int[ttIncl[t].length][][][];
bak_prevtIdxs[t] = new int[ttIncl[t].length][][][];
for (int c=ttIncl[t].length-1; c>=0; c--) {
bak_lblock[t][c] = new int[lblock[t][c].length][][];
bak_prevtIdxs[t][c] = new int[ttIncl[t][c].length][][];
for (int r=lblock[t][c].length-1; r>=0; r--) {
bak_lblock[t][c][r] =
new int[lblock[t][c][r].length][];
bak_prevtIdxs[t][c][r] =
new int[prevtIdxs[t][c][r].length][];
minsbi = (r==0) ? 0 : 1;
maxsbi = (r==0) ? 1 : 4;
for (int s=minsbi; s<maxsbi; s++) {
bak_lblock[t][c][r][s] =
new int[lblock[t][c][r][s].length];
bak_prevtIdxs[t][c][r][s] =
new int[prevtIdxs[t][c][r][s].length];
}
}
}
}
}
//-- Save the data
// Use reference caches to minimize array access overhead
TagTreeEncoder
ttIncl_t_c[][][],
ttMaxBP_t_c[][][],
ttIncl_t_c_r[][],
ttMaxBP_t_c_r[][];
int
lblock_t_c[][][],
bak_lblock_t_c[][][],
prevtIdxs_t_c_r[][],
bak_prevtIdxs_t_c_r[][];
// Loop on tiles
for (int t=ttIncl.length-1; t>=0; t--) {
// Loop on components
for (int c=ttIncl[t].length-1; c>=0; c--) {
// Initialize reference caches
lblock_t_c = lblock[t][c];
bak_lblock_t_c = bak_lblock[t][c];
ttIncl_t_c = ttIncl[t][c];
ttMaxBP_t_c = ttMaxBP[t][c];
// Loop on resolution levels
for (int r=lblock_t_c.length-1; r>=0; r--) {
// Initialize reference caches
ttIncl_t_c_r = ttIncl_t_c[r];
ttMaxBP_t_c_r = ttMaxBP_t_c[r];
prevtIdxs_t_c_r = prevtIdxs[t][c][r];
bak_prevtIdxs_t_c_r = bak_prevtIdxs[t][c][r];
// Loop on subbands
minsbi = (r==0) ? 0 : 1;
maxsbi = (r==0) ? 1 : 4;
for (int s=minsbi; s<maxsbi; s++) {
// Save 'lblock'
System.arraycopy(lblock_t_c[r][s],0,
bak_lblock_t_c[r][s],0,
lblock_t_c[r][s].length);
// Save 'prevtIdxs'
System.arraycopy(prevtIdxs_t_c_r[s],0,
bak_prevtIdxs_t_c_r[s],0,
prevtIdxs_t_c_r[s].length);
} // End loop on subbands
// Loop on precincts
for(int p=ppinfo[t][c][r].length-1; p>=0; p--) {
if(p<ttIncl_t_c_r.length) {
// Loop on subbands
for(int s=minsbi; s<maxsbi; s++) {
ttIncl_t_c_r[p][s].save();
ttMaxBP_t_c_r[p][s].save();
} // End loop on subbands
}
} // End loop on precincts
} // End loop on resolutions
} // End loop on components
} // End loop on tiles
// Set the saved state
saved = true;
}
/**
* Restores the last saved state of this object. An
* IllegalArgumentException is thrown if no state has been saved.
*
* @see #save
* */
public void restore() {
int maxsbi,minsbi;
if (!saved) {
throw new IllegalArgumentException();
}
// Invalidate last encoded body buffer
lbbuf = null;
//-- Restore tha data
// Use reference caches to minimize array access overhead
TagTreeEncoder ttIncl_t_c[][][],ttMaxBP_t_c[][][],ttIncl_t_c_r[][],
ttMaxBP_t_c_r[][];
int lblock_t_c[][][],bak_lblock_t_c[][][],prevtIdxs_t_c_r[][],
bak_prevtIdxs_t_c_r[][];
// Loop on tiles
for (int t=ttIncl.length-1; t>=0; t--) {
// Loop on components
for (int c=ttIncl[t].length-1; c>=0; c--) {
// Initialize reference caches
lblock_t_c = lblock[t][c];
bak_lblock_t_c = bak_lblock[t][c];
ttIncl_t_c = ttIncl[t][c];
ttMaxBP_t_c = ttMaxBP[t][c];
// Loop on resolution levels
for (int r=lblock_t_c.length-1; r>=0; r--) {
// Initialize reference caches
ttIncl_t_c_r = ttIncl_t_c[r];
ttMaxBP_t_c_r = ttMaxBP_t_c[r];
prevtIdxs_t_c_r = prevtIdxs[t][c][r];
bak_prevtIdxs_t_c_r = bak_prevtIdxs[t][c][r];
// Loop on subbands
minsbi = (r==0) ? 0 : 1;
maxsbi = (r==0) ? 1 : 4;
for (int s=minsbi; s<maxsbi; s++) {
// Restore 'lblock'
System.arraycopy(bak_lblock_t_c[r][s],0,
lblock_t_c[r][s],0,
lblock_t_c[r][s].length);
// Restore 'prevtIdxs'
System.arraycopy(bak_prevtIdxs_t_c_r[s],0,
prevtIdxs_t_c_r[s],0,
prevtIdxs_t_c_r[s].length);
} // End loop on subbands
// Loop on precincts
for(int p=ppinfo[t][c][r].length-1; p>=0; p--) {
if(p<ttIncl_t_c_r.length) {
// Loop on subbands
for(int s=minsbi; s<maxsbi; s++) {
ttIncl_t_c_r[p][s].restore();
ttMaxBP_t_c_r[p][s].restore();
} // End loop on subbands
}
} // End loop on precincts
} // End loop on resolution levels
} // End loop on components
} // End loop on tiles
}
/**
* Resets the state of the object to the initial state, as if the object
* was just created.
* */
public void reset() {
int maxsbi,minsbi;
// Invalidate save
saved = false;
// Invalidate last encoded body buffer
lbbuf = null;
// Reinitialize each element in the arrays
// Use reference caches to minimize array access overhead
TagTreeEncoder ttIncl_t_c[][][],ttMaxBP_t_c[][][],ttIncl_t_c_r[][],
ttMaxBP_t_c_r[][];
int lblock_t_c[][][],prevtIdxs_t_c_r[][];
// Loop on tiles
for (int t=ttIncl.length-1; t>=0; t--) {
// Loop on components
for (int c=ttIncl[t].length-1; c>=0; c--) {
// Initialize reference caches
lblock_t_c = lblock[t][c];
ttIncl_t_c = ttIncl[t][c];
ttMaxBP_t_c = ttMaxBP[t][c];
// Loop on resolution levels
for (int r=lblock_t_c.length-1; r>=0; r--) {
// Initialize reference caches
ttIncl_t_c_r = ttIncl_t_c[r];
ttMaxBP_t_c_r = ttMaxBP_t_c[r];
prevtIdxs_t_c_r = prevtIdxs[t][c][r];
// Loop on subbands
minsbi = (r==0) ? 0 : 1;
maxsbi = (r==0) ? 1 : 4;
for (int s=minsbi; s<maxsbi; s++) {
// Reset 'prevtIdxs'
ArrayUtil.intArraySet(prevtIdxs_t_c_r[s],-1);
// Reset 'lblock'
ArrayUtil.intArraySet(lblock_t_c[r][s],INIT_LBLOCK);
} // End loop on subbands
// Loop on precincts
for(int p=ppinfo[t][c][r].length-1; p>=0; p--) {
if(p<ttIncl_t_c_r.length) {
// Loop on subbands
for(int s=minsbi; s<maxsbi; s++) {
ttIncl_t_c_r[p][s].reset();
ttMaxBP_t_c_r[p][s].reset();
} // End loop on subbands
}
} // End loop on precincts
} // End loop on resolution levels
} // End loop on components
} // End loop on tiles
}
/**
* Returns true if the current packet is writable i.e. should be written.
* Returns false otherwise.
* */
public boolean isPacketWritable() {
return packetWritable;
}
/**
* Tells if there was ROI information in the last written packet
* */
public boolean isROIinPkt(){
return roiInPkt;
}
/** Gives the length to read in current packet body to get all ROI
* information */
public int getROILen(){
return roiLen;
}
/**
* Returns the parameters that are used in this class and implementing
* classes. It returns a 2D String array. Each of the 1D arrays is for a
* different option, and they have 3 elements. The first element is the
* option name, the second one is the synopsis, the third one is a long
* description of what the parameter is and the fourth is its default
* value. The synopsis or description may be 'null', in which case it is
* assumed that there is no synopsis or description of the option,
* respectively. Null may be returned if no options are supported.
*
* @return the options name, their synopsis and their explanation,
* or null if no options are supported.
* */
public static String[][] getParameterInfo() {
return pinfo;
}
/**
* Returns information about a given precinct
*
* @param t Tile index.
*
* @param c Component index.
*
* @param r Resolution level index.
*
* @param p Precinct index
* */
public PrecInfo getPrecInfo(int t,int c,int r,int p) {
return ppinfo[t][c][r][p];
}
}