/*
* $RCSfile: HeaderEncoder.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:02 $
* $State: Exp $
*
* Class: HeaderEncoder
*
* Description: Write codestream headers.
*
*
*
* 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.quantization.quantizer.*;
import jj2000.j2k.wavelet.analysis.*;
import jj2000.j2k.entropy.encoder.*;
import jj2000.j2k.quantization.*;
import jj2000.j2k.image.input.*;
import jj2000.j2k.roi.encoder.*;
import jj2000.j2k.codestream.*;
import jj2000.j2k.wavelet.*;
import jj2000.j2k.entropy.*;
import jj2000.j2k.image.*;
import jj2000.j2k.util.*;
import jj2000.j2k.io.*;
import jj2000.j2k.*;
import java.util.*;
import java.io.*;
import com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageWriteParamJava;
/**
* This class writes almost of the markers and marker segments in main header
* and in tile-part headers. It is created by the run() method of the Encoder
* instance.
*
* <p>A marker segment includes a marker and eventually marker segment
* parameters. It is designed by the three letter code of the marker
* associated with the marker segment. JPEG 2000 part I defines 6 types of
* markers: <ul> <li> Delimiting : SOC,SOT,SOD,EOC (written in
* FileCodestreamWriter).</li> <li> Fixed information: SIZ.</li> <li>
* Functional: COD,COC,RGN,QCD,QCC,POC.</li> <li> In bit-stream: SOP,EPH.</li>
* <li> Pointer: TLM,PLM,PLT,PPM,PPT.</li> <li> Informational:
* CRG,COM.</li></ul>
*
* <p>Main Header is written when Encoder instance calls encodeMainHeader
* whereas tile-part headers are written when the EBCOTRateAllocator instance
* calls encodeTilePartHeader.
*
* @see Encoder
* @see Markers
* @see EBCOTRateAllocator
* */
public class HeaderEncoder implements Markers, StdEntropyCoderOptions {
/** Nominal range bit of the component defining default values in QCD for
* main header */
private int defimgn;
/** Nominal range bit of the component defining default values in QCD for
* tile headers */
private int deftilenr;
/** The number of components in the image */
protected int nComp;
/** Whether or not to write the JJ2000 COM marker segment */
private boolean enJJ2KMarkSeg = true;
/** Other COM marker segments specified in the command line */
private String otherCOMMarkSeg = null;
/** The ByteArrayOutputStream to store header data. This handler
* is kept in order to use methods not accessible from a general
* DataOutputStream. For the other methods, it's better to use
* variable hbuf.
*
* @see #hbuf */
protected ByteArrayOutputStream baos;
/** The DataOutputStream to store header data. This kind of object
* is useful to write short, int, .... It's constructor takes
* baos as parameter.
*
* @see #baos
**/
protected DataOutputStream hbuf;
/** The image data reader. Source of original data info */
protected ImgData origSrc;
/** An array specifying, for each component,if the data was signed
or not */
protected boolean isOrigSig[];
/** Reference to the rate allocator */
protected PostCompRateAllocator ralloc;
/** Reference to the DWT module */
protected ForwardWT dwt;
/** Reference to the tiler module */
protected Tiler tiler;
/** Reference to the ROI module */
protected ROIScaler roiSc;
/** The encoder specifications */
protected J2KImageWriteParamJava wp;
/**
* Initializes the header writer with the references to the coding chain.
*
* @param origsrc The original image data (before any component mixing,
* tiling, etc.)
*
* @param isorigsig An array specifying for each component if it was
* originally signed or not.
*
* @param dwt The discrete wavelet transform module.
*
* @param tiler The tiler module.
*
* @param encSpec The encoder specifications
*
* @param roiSc The ROI scaler module.
*
* @param ralloc The post compression rate allocator.
* */
public HeaderEncoder(ImgData origsrc, boolean isorigsig[],
ForwardWT dwt, Tiler tiler,J2KImageWriteParamJava wp,
ROIScaler roiSc, PostCompRateAllocator ralloc) {
if (origsrc.getNumComps() != isorigsig.length) {
throw new IllegalArgumentException();
}
this.origSrc = origsrc;
this.isOrigSig = isorigsig;
this.dwt = dwt;
this.tiler = tiler;
this.wp = wp;
this.roiSc = roiSc;
this.ralloc = ralloc;
baos = new ByteArrayOutputStream();
hbuf = new DataOutputStream(baos);
nComp = origsrc.getNumComps();
// enJJ2KMarkSeg = wp.getHjj2000_COM();
// otherCOMMarkSeg = wp.getHCOM();
}
/**
* Resets the contents of this HeaderEncoder to its initial state. It
* erases all the data in the header buffer and reactualizes the
* headerLength field of the bit stream writer.
* */
public void reset() {
baos.reset();
hbuf = new DataOutputStream(baos);
}
/**
* Returns the byte-buffer used to store the codestream header.
*
* @return A byte array countaining codestream header
* */
protected byte[] getBuffer(){
return baos.toByteArray();
}
/**
* Returns the length of the header.
*
* @return The length of the header in bytes
* */
public int getLength() {
return hbuf.size();
}
/**
* Writes the header to the specified BinaryDataOutput.
*
* @param out Where to write the header.
* */
public void writeTo(BinaryDataOutput out) throws IOException {
int i,len;
byte buf[];
buf = getBuffer();
len = getLength();
for (i=0; i<len; i++) {
out.writeByte(buf[i]);
}
}
/**
* Returns the number of bytes used in the codestream header's
* buffer.
*
* @return Header length in buffer (without any header
* overhead)
* */
protected int getBufferLength(){
return baos.size();
}
/**
* Writes the header to the specified OutputStream.
*
* @param out Where to write the header.
* */
public void writeTo(OutputStream out) throws IOException {
out.write(getBuffer(),0,getBufferLength());
}
/**
* Start Of Codestream marker (SOC) signalling the beginning of a
* codestream.
* */
private void writeSOC() throws IOException {
hbuf.writeShort(SOC);
}
/**
* Writes SIZ marker segment of the codestream header. It is a fixed
* information marker segment containing informations about image and tile
* sizes. It is required in the main header immediately after SOC marker
* segment.
* */
private void writeSIZ() throws IOException {
int tmp;
// SIZ marker
hbuf.writeShort(SIZ);
// Lsiz (Marker length) corresponding to
// Lsiz(2 bytes)+Rsiz(2)+Xsiz(4)+Ysiz(4)+XOsiz(4)+YOsiz(4)+
// XTsiz(4)+YTsiz(4)+XTOsiz(4)+YTOsiz(4)+Csiz(2)+
// (Ssiz(1)+XRsiz(1)+YRsiz(1))*nComp
// markSegLen = 38 + 3*nComp;
int markSegLen = 38 + 3*nComp;
hbuf.writeShort(markSegLen);
// Rsiz (codestream capabilities)
hbuf.writeShort(0); // JPEG 2000 - Part I
// Xsiz (original image width)
hbuf.writeInt(tiler.getImgWidth()+tiler.getImgULX());
// Ysiz (original image height)
hbuf.writeInt(tiler.getImgHeight()+tiler.getImgULY());
// XOsiz (horizontal offset from the origin of the reference
// grid to the left side of the image area)
hbuf.writeInt(tiler.getImgULX());
// YOsiz (vertical offset from the origin of the reference
// grid to the top side of the image area)
hbuf.writeInt(tiler.getImgULY());
// XTsiz (nominal tile width)
hbuf.writeInt(tiler.getNomTileWidth());
// YTsiz (nominal tile height)
hbuf.writeInt(tiler.getNomTileHeight());
Point torig = tiler.getTilingOrigin(null);
// XTOsiz (Horizontal offset from the origin of the reference
// grid to the left side of the first tile)
hbuf.writeInt(torig.x);
// YTOsiz (Vertical offset from the origin of the reference
// grid to the top side of the first tile)
hbuf.writeInt(torig.y);
// Csiz (number of components)
hbuf.writeShort(nComp);
// Bit-depth and downsampling factors.
for(int c=0; c<nComp; c++){ // Loop on each component
// Ssiz bit-depth before mixing
tmp = origSrc.getNomRangeBits(c)-1;
tmp |= ( (isOrigSig[c]?1:0)<<SSIZ_DEPTH_BITS );
hbuf.write(tmp);
// XRsiz (component sub-sampling value x-wise)
hbuf.write(tiler.getCompSubsX(c));
// YRsiz (component sub-sampling value y-wise)
hbuf.write(tiler.getCompSubsY(c));
} // End loop on each component
}
/**
* Writes COD marker segment. COD is a functional marker segment
* containing the code style default (coding style, decomposition,
* layering) used for compressing all the components in an image.
*
* <p>The values can be overriden for an individual component by a COC
* marker in either the main or the tile header.
*
* @param mh Flag indicating whether this marker belongs to the main
* header
*
* @param tileIdx Tile index if the marker belongs to a tile-part header
*
* @see #writeCOC
* */
protected void writeCOD(boolean mh, int tileIdx)
throws IOException {
AnWTFilter[][] filt;
boolean precinctPartitionUsed;
int tmp;
int mrl=0,a=0;
int ppx=0, ppy=0;
Progression[] prog;
if (mh) {
mrl = ((Integer)wp.getDecompositionLevel().getDefault()).intValue();
// get default precinct size
ppx = wp.getPrecinctPartition().getPPX(-1,-1,mrl);
ppy = wp.getPrecinctPartition().getPPY(-1,-1,mrl);
prog = (Progression[])(wp.getProgressionType().getDefault());
}
else {
mrl = ((Integer)wp.getDecompositionLevel().getTileDef(tileIdx)).intValue();
// get precinct size for specified tile
ppx = wp.getPrecinctPartition().getPPX(tileIdx,-1,mrl);
ppy = wp.getPrecinctPartition().getPPY(tileIdx,-1,mrl);
prog = (Progression[])(wp.getProgressionType().getTileDef(tileIdx));
}
if ( ppx != PRECINCT_PARTITION_DEF_SIZE ||
ppy != PRECINCT_PARTITION_DEF_SIZE ) {
precinctPartitionUsed = true;
}
else {
precinctPartitionUsed = false;
}
if ( precinctPartitionUsed ) {
// If precinct partition is used we add one byte per resolution
// level i.e. mrl+1 (+1 for resolution 0).
a = mrl+1;
}
// Write COD marker
hbuf.writeShort(COD);
// Lcod (marker segment length (in bytes)) Basic : Lcod(2
// bytes)+Scod(1)+SGcod(4)+SPcod(5+a) where:
// a=0 if no precinct partition is used
// a=mrl+1 if precinct partition used
int markSegLen = 12+a;
hbuf.writeShort(markSegLen);
// Scod (coding style parameter)
tmp=0;
if ( precinctPartitionUsed )
tmp=SCOX_PRECINCT_PARTITION;
// Are SOP markers used ?
if (mh) {
if( ((String)wp.getSOP().getDefault().toString())
.equalsIgnoreCase("true") ) {
tmp |= SCOX_USE_SOP;
}
}
else {
if ( ((String)wp.getSOP().getTileDef(tileIdx).toString())
.equalsIgnoreCase("true") ) {
tmp |= SCOX_USE_SOP;
}
}
// Are EPH markers used ?
if(mh){
if ( ((String)wp.getEPH().getDefault().toString())
.equalsIgnoreCase("true") ) {
tmp |= SCOX_USE_EPH;
}
}
else{
if ( ((String)wp.getEPH().getTileDef(tileIdx).toString())
.equalsIgnoreCase("true") ) {
tmp |= SCOX_USE_EPH;
}
}
if (dwt.getCbULX()!=0) tmp |= SCOX_HOR_CB_PART;
if (dwt.getCbULY()!=0) tmp |= SCOX_VER_CB_PART;
hbuf.write(tmp);
// SGcod
// Progression order
hbuf.write(prog[0].type);
// Number of layers
hbuf.writeShort(ralloc.getNumLayers());
// Multiple component transform
// CSsiz (Color transform)
String str = null;
if(mh)
str = (String)wp.getComponentTransformation().getDefault();
else
str = (String)wp.getComponentTransformation().getTileDef(tileIdx);
if(str.equals("none"))
hbuf.write(0);
else
hbuf.write(1);
// SPcod
// Number of decomposition levels
hbuf.write(mrl);
// Code-block width and height
if ( mh ) {
// main header, get default values
tmp = wp.getCodeBlockSize().
getCBlkWidth(ModuleSpec.SPEC_DEF,-1,-1);
hbuf.write(MathUtil.log2(tmp)-2);
tmp = wp.getCodeBlockSize().
getCBlkHeight(ModuleSpec.SPEC_DEF,-1,-1);
hbuf.write(MathUtil.log2(tmp)-2);
}
else {
// tile header, get tile default values
tmp = wp.getCodeBlockSize().
getCBlkWidth(ModuleSpec.SPEC_TILE_DEF,tileIdx,-1);
hbuf.write(MathUtil.log2(tmp)-2);
tmp = wp.getCodeBlockSize().
getCBlkHeight(ModuleSpec.SPEC_TILE_DEF,tileIdx,-1);
hbuf.write(MathUtil.log2(tmp)-2);
}
// Style of the code-block coding passes
tmp = 0;
if(mh){ // Main header
// Selective arithmetic coding bypass ?
if( ((String)wp.getBypass().getDefault()).equals("true")) {
tmp |= OPT_BYPASS;
}
// MQ reset after each coding pass ?
if( ((String)wp.getResetMQ().getDefault()).equals("true")) {
tmp |= OPT_RESET_MQ;
}
// MQ termination after each arithmetically coded coding pass ?
if( ((String)wp.getTerminateOnByte().getDefault()).equals("true") ) {
tmp |= OPT_TERM_PASS;
}
// Vertically stripe-causal context mode ?
if( ((String)wp.getCausalCXInfo().getDefault()).equals("true") ) {
tmp |= OPT_VERT_STR_CAUSAL;
}
// Predictable termination ?
if( ((String)wp.getMethodForMQTermination().getDefault()).equals("predict")){
tmp |= OPT_PRED_TERM;
}
// Error resilience segmentation symbol insertion ?
if( ((String)wp.getCodeSegSymbol().getDefault()).equals("true")) {
tmp |= OPT_SEG_SYMBOLS;
}
}
else{ // Tile header
// Selective arithmetic coding bypass ?
if( ((String)wp.getBypass().getTileDef(tileIdx)).equals("true")) {
tmp |= OPT_BYPASS;
}
// MQ reset after each coding pass ?
if( ((String)wp.getResetMQ().getTileDef(tileIdx)).equals("true")) {
tmp |= OPT_RESET_MQ;
}
// MQ termination after each arithmetically coded coding pass ?
if( ((String)wp.getTerminateOnByte().getTileDef(tileIdx)).equals("true") ) {
tmp |= OPT_TERM_PASS;
}
// Vertically stripe-causal context mode ?
if( ((String)wp.getCausalCXInfo().getTileDef(tileIdx)).equals("true") ) {
tmp |= OPT_VERT_STR_CAUSAL;
}
// Predictable termination ?
if( ((String)wp.getMethodForMQTermination().getTileDef(tileIdx)).equals("predict")){
tmp |= OPT_PRED_TERM;
}
// Error resilience segmentation symbol insertion ?
if( ((String)wp.getCodeSegSymbol().getTileDef(tileIdx)).equals("true")) {
tmp |= OPT_SEG_SYMBOLS;
}
}
hbuf.write(tmp);
// Wavelet transform
// Wavelet Filter
if(mh){
filt=((AnWTFilter[][])wp.getFilters().getDefault());
hbuf.write(filt[0][0].getFilterType());
}else{
filt=((AnWTFilter[][])wp.getFilters().getTileDef(tileIdx));
hbuf.write(filt[0][0].getFilterType());
}
// Precinct partition
if ( precinctPartitionUsed ) {
// Write the precinct size for each resolution level + 1
// (resolution 0) if precinct partition is used.
Vector v[] = null;
if ( mh ) {
v = (Vector[])wp.getPrecinctPartition().getDefault();
}
else {
v = (Vector[])wp.getPrecinctPartition().getTileDef(tileIdx);
}
for (int r=mrl ; r>=0 ; r--) {
if ( r>=v[1].size() ) {
tmp = ((Integer)v[1].elementAt(v[1].size()-1)).
intValue();
}
else {
tmp = ((Integer)v[1].elementAt(r)).intValue();
}
int yExp = (MathUtil.log2(tmp)<< 4) & 0x00F0;
if ( r>=v[0].size() ) {
tmp = ((Integer)v[0].elementAt(v[0].size()-1)).
intValue();
}
else {
tmp = ((Integer)v[0].elementAt(r)).intValue();
}
int xExp = MathUtil.log2(tmp) & 0x000F;
hbuf.write(yExp|xExp);
}
}
}
/**
* Writes COC marker segment . It is a functional marker containing the
* coding style for one component (coding style, decomposition, layering).
*
* <P>Its values overrides any value previously set in COD in the main
* header or in the tile header.
*
* @param mh Flag indicating whether the main header is to be written
*
* @param tileIdx Tile index
*
* @param compIdx index of the component which need use of the COC marker
* segment.
*
* @see #writeCOD
* */
protected void writeCOC(boolean mh, int tileIdx, int compIdx)
throws IOException {
AnWTFilter[][] filt;
boolean precinctPartitionUsed;
int tmp;
int mrl=0,a=0;
int ppx=0, ppy=0;
Progression[] prog;
if (mh) {
mrl = ((Integer)wp.getDecompositionLevel().getCompDef(compIdx)).intValue();
// Get precinct size for specified component
ppx = wp.getPrecinctPartition().getPPX(-1, compIdx, mrl);
ppy = wp.getPrecinctPartition().getPPY(-1, compIdx, mrl);
prog = (Progression[])(wp.getProgressionType().getCompDef(compIdx));
}
else {
mrl = ((Integer)wp.getDecompositionLevel().getTileCompVal(tileIdx,compIdx)).
intValue();
// Get precinct size for specified component/tile
ppx = wp.getPrecinctPartition().getPPX(tileIdx, compIdx, mrl);
ppy = wp.getPrecinctPartition().getPPY(tileIdx, compIdx, mrl);
prog = (Progression[])(wp.getProgressionType().getTileCompVal(tileIdx,compIdx));
}
if ( ppx != Markers.PRECINCT_PARTITION_DEF_SIZE ||
ppy != Markers.PRECINCT_PARTITION_DEF_SIZE ) {
precinctPartitionUsed = true;
}
else {
precinctPartitionUsed = false;
}
if ( precinctPartitionUsed ) {
// If precinct partition is used we add one byte per resolution
// level i.e. mrl+1 (+1 for resolution 0).
a = mrl+1;
}
// COC marker
hbuf.writeShort(COC);
// Lcoc (marker segment length (in bytes))
// Basic: Lcoc(2 bytes)+Scoc(1)+ Ccoc(1 or 2)+SPcod(5+a)
int markSegLen = 8 + ((nComp < 257) ? 1 : 2)+a;
// Rounded to the nearest even value greater or equals
hbuf.writeShort(markSegLen);
// Ccoc
if(nComp < 257) {
hbuf.write(compIdx);
}
else {
hbuf.writeShort(compIdx);
}
// Scod (coding style parameter)
tmp=0;
if ( precinctPartitionUsed ) {
tmp=SCOX_PRECINCT_PARTITION;
}
hbuf.write(tmp);
// SPcoc
// Number of decomposition levels
hbuf.write(mrl);
// Code-block width and height
if ( mh ) {
// main header, get component default values
tmp = wp.getCodeBlockSize().
getCBlkWidth(ModuleSpec.SPEC_COMP_DEF, -1, compIdx);
hbuf.write(MathUtil.log2(tmp)-2);
tmp = wp.getCodeBlockSize().
getCBlkHeight(ModuleSpec.SPEC_COMP_DEF, -1, compIdx);
hbuf.write(MathUtil.log2(tmp)-2);
}
else {
// tile header, get tile component values
tmp = wp.getCodeBlockSize().
getCBlkWidth(ModuleSpec.SPEC_TILE_COMP, tileIdx, compIdx);
hbuf.write(MathUtil.log2(tmp)-2);
tmp = wp.getCodeBlockSize().
getCBlkHeight(ModuleSpec.SPEC_TILE_COMP, tileIdx, compIdx);
hbuf.write(MathUtil.log2(tmp)-2);
}
// Entropy coding mode options
tmp = 0;
if(mh){ // Main header
// Lazy coding mode ?
if( ((String)wp.getBypass().getCompDef(compIdx)).equals("true")) {
tmp |= OPT_BYPASS;
}
// MQ reset after each coding pass ?
if( ((String)wp.getResetMQ().getCompDef(compIdx)).
equalsIgnoreCase("true")) {
tmp |= OPT_RESET_MQ;
}
// MQ termination after each arithmetically coded coding pass ?
if( ((String)wp.getTerminateOnByte().getCompDef(compIdx)).equals("true") ) {
tmp |= OPT_TERM_PASS;
}
// Vertically stripe-causal context mode ?
if( ((String)wp.getCausalCXInfo().getCompDef(compIdx)).equals("true") ) {
tmp |= OPT_VERT_STR_CAUSAL;
}
// Predictable termination ?
if( ((String)wp.getMethodForMQTermination().getCompDef(compIdx)).equals("predict")){
tmp |= OPT_PRED_TERM;
}
// Error resilience segmentation symbol insertion ?
if( ((String)wp.getCodeSegSymbol().getCompDef(compIdx)).equals("true")) {
tmp |= OPT_SEG_SYMBOLS;
}
}
else{ // Tile Header
if( ((String)wp.getBypass().getTileCompVal(tileIdx,compIdx)).
equals("true")) {
tmp |= OPT_BYPASS;
}
// MQ reset after each coding pass ?
if( ((String)wp.getResetMQ().getTileCompVal(tileIdx,compIdx)).
equals("true")) {
tmp |= OPT_RESET_MQ;
}
// MQ termination after each arithmetically coded coding pass ?
if( ((String)wp.getTerminateOnByte().getTileCompVal(tileIdx,compIdx)).
equals("true") ) {
tmp |= OPT_TERM_PASS;
}
// Vertically stripe-causal context mode ?
if( ((String)wp.getCausalCXInfo().getTileCompVal(tileIdx,compIdx)).
equals("true") ) {
tmp |= OPT_VERT_STR_CAUSAL;
}
// Predictable termination ?
if( ((String)wp.getMethodForMQTermination().getTileCompVal(tileIdx,compIdx)).
equals("predict")){
tmp |= OPT_PRED_TERM;
}
// Error resilience segmentation symbol insertion ?
if( ((String)wp.getCodeSegSymbol().getTileCompVal(tileIdx,compIdx)).
equals("true")) {
tmp |= OPT_SEG_SYMBOLS;
}
}
hbuf.write(tmp);
// Wavelet transform
// Wavelet Filter
if(mh){
filt=((AnWTFilter[][])wp.getFilters().getCompDef(compIdx));
hbuf.write(filt[0][0].getFilterType());
}else{
filt=((AnWTFilter[][])wp.getFilters().getTileCompVal(tileIdx,compIdx));
hbuf.write(filt[0][0].getFilterType());
}
// Precinct partition
if ( precinctPartitionUsed ) {
// Write the precinct size for each resolution level + 1
// (resolution 0) if precinct partition is used.
Vector v[] = null;
if ( mh ) {
v = (Vector[])wp.getPrecinctPartition().getCompDef(compIdx);
}
else {
v = (Vector[])wp.getPrecinctPartition().getTileCompVal(tileIdx, compIdx);
}
for (int r=mrl ; r>=0 ; r--) {
if ( r>=v[1].size() ) {
tmp = ((Integer)v[1].elementAt(v[1].size()-1)).
intValue();
}
else {
tmp = ((Integer)v[1].elementAt(r)).intValue();
}
int yExp = (MathUtil.log2(tmp)<< 4) & 0x00F0;
if ( r>=v[0].size() ) {
tmp = ((Integer)v[0].elementAt(v[0].size()-1)).
intValue();
}
else {
tmp = ((Integer)v[0].elementAt(r)).intValue();
}
int xExp = MathUtil.log2(tmp) & 0x000F;
hbuf.write(yExp|xExp);
}
}
}
/**
* Writes QCD marker segment in main header. QCD is a functional marker
* segment countaining the quantization default used for compressing all
* the components in an image. The values can be overriden for an
* individual component by a QCC marker in either the main or the tile
* header.
* */
protected void writeMainQCD() throws IOException{
float step;
String qType = (String)wp.getQuantizationType().getDefault();
float baseStep = ((Float)wp.getQuantizationStep().getDefault()).floatValue();
int gb = ((Integer)wp.getGuardBits().getDefault()).intValue();
boolean isDerived = qType.equals("derived");
boolean isReversible = qType.equals("reversible");
int mrl = ((Integer)wp.getDecompositionLevel().getDefault()).intValue();
int nt = dwt.getNumTiles();
int nc = dwt.getNumComps();
int tmpI;
int[] tcIdx = new int[2];
String tmpStr;
boolean notFound = true;
for(int t=0; t<nt && notFound; t++) {
for(int c=0; c<nc && notFound; c++) {
tmpI = ((Integer)wp.getDecompositionLevel().getTileCompVal(t,c)).intValue();
tmpStr = (String)wp.getQuantizationType().getTileCompVal(t,c);
if(tmpI==mrl && tmpStr.equals(qType)) {
tcIdx[0] = t; tcIdx[1] = c;
notFound = false;
}
}
}
if(notFound) {
throw new Error("Default representative for quantization type "+
" and number of decomposition levels not found "+
" in main QCD marker segment. "+
"You have found a JJ2000 bug.");
}
SubbandAn sb,csb,
sbRoot = dwt.getAnSubbandTree(tcIdx[0],tcIdx[1]);
defimgn = dwt.getNomRangeBits(tcIdx[1]);
int nqcd; // Number of quantization step-size to transmit
// Get the quantization style
int qstyle = (isReversible) ? SQCX_NO_QUANTIZATION :
((isDerived) ? SQCX_SCALAR_DERIVED : SQCX_SCALAR_EXPOUNDED);
// QCD marker
hbuf.writeShort(QCD);
// Compute the number of steps to send
switch (qstyle) {
case SQCX_SCALAR_DERIVED:
nqcd = 1; // Just the LL value
break;
case SQCX_NO_QUANTIZATION:
case SQCX_SCALAR_EXPOUNDED:
// One value per subband
nqcd=0;
sb=sbRoot;
// Get the subband at first resolution level
sb = (SubbandAn) sb.getSubbandByIdx(0,0);
// Count total number of subbands
for (int j=0; j<=mrl; j++) {
csb = sb;
while (csb != null) {
nqcd++;
csb = (SubbandAn) csb.nextSubband();
}
// Go up one resolution level
sb = (SubbandAn) sb.getNextResLevel();
}
break;
default:
throw new Error("Internal JJ2000 error");
}
// Lqcd (marker segment length (in bytes))
// Lqcd(2 bytes)+Sqcd(1)+ SPqcd (2*Nqcd)
int markSegLen = 3 + ((isReversible) ? nqcd : 2*nqcd);
// Rounded to the nearest even value greater or equals
hbuf.writeShort(markSegLen);
// Sqcd
hbuf.write(qstyle+(gb<<SQCX_GB_SHIFT));
// SPqcd
switch (qstyle) {
case SQCX_NO_QUANTIZATION:
sb = sbRoot;
sb = (SubbandAn)sb.getSubbandByIdx(0,0);
// Output one exponent per subband
for (int j=0; j<=mrl; j++) {
csb = sb;
while(csb != null) {
int tmp = (defimgn + csb.anGainExp);
hbuf.write(tmp<<SQCX_EXP_SHIFT);
csb = (SubbandAn)csb.nextSubband();
// Go up one resolution level
}
sb = (SubbandAn)sb.getNextResLevel();
}
break;
case SQCX_SCALAR_DERIVED:
sb = sbRoot;
sb = (SubbandAn)sb.getSubbandByIdx(0,0);
// Calculate subband step (normalized to unit
// dynamic range)
step = baseStep/(1<<sb.level);
// Write exponent-mantissa, 16 bits
hbuf.writeShort(StdQuantizer.
convertToExpMantissa(step));
break;
case SQCX_SCALAR_EXPOUNDED:
sb = sbRoot;
sb = (SubbandAn)sb.getSubbandByIdx(0,0);
// Output one step per subband
for (int j=0; j<=mrl; j++) {
csb = sb;
while(csb != null) {
// Calculate subband step (normalized to unit
// dynamic range)
step = baseStep/(csb.l2Norm*(1<<csb.anGainExp));
// Write exponent-mantissa, 16 bits
hbuf.writeShort(StdQuantizer.
convertToExpMantissa(step));
csb = (SubbandAn)csb.nextSubband();
}
// Go up one resolution level
sb = (SubbandAn)sb.getNextResLevel();
}
break;
default:
throw new Error("Internal JJ2000 error");
}
}
/**
* Writes QCC marker segment in main header. It is a functional
* marker segment countaining the quantization used for
* compressing the specified component in an image. The values
* override for the specified component what was defined by a QCC
* marker in either the main or the tile header.
*
* @param compIdx Index of the component which needs QCC marker
* segment.
* */
protected void writeMainQCC(int compIdx)
throws IOException{
int mrl;
int qstyle;
int tIdx = 0;
float step;
SubbandAn sb,sb2;
SubbandAn sbRoot;
int imgnr = dwt.getNomRangeBits(compIdx);
String qType = (String)wp.getQuantizationType().getCompDef(compIdx);
float baseStep = ((Float)wp.getQuantizationStep().getCompDef(compIdx)).floatValue();
int gb = ((Integer)wp.getGuardBits().getCompDef(compIdx)).intValue();
boolean isReversible = qType.equals("reversible");
boolean isDerived = qType.equals("derived");
mrl = ((Integer)wp.getDecompositionLevel().getCompDef(compIdx)).intValue();
int nt = dwt.getNumTiles();
int nc = dwt.getNumComps();
int tmpI;
String tmpStr;
boolean notFound = true;
for(int t=0; t<nt && notFound; t++) {
for(int c=0; c<nc && notFound; c++) {
tmpI = ((Integer)wp.getDecompositionLevel().getTileCompVal(t,c)).intValue();
tmpStr = (String)wp.getQuantizationType().getTileCompVal(t,c);
if(tmpI==mrl && tmpStr.equals(qType)) {
tIdx = t;
notFound = false;
}
}
}
if(notFound) {
throw new Error("Default representative for quantization type "+
" and number of decomposition levels not found "+
" in main QCC (c="+compIdx+") marker segment. "+
"You have found a JJ2000 bug.");
}
sbRoot = dwt.getAnSubbandTree(tIdx,compIdx);
int nqcc; // Number of quantization step-size to transmit
// Get the quantization style
if(isReversible) {
qstyle = SQCX_NO_QUANTIZATION;
}
else if (isDerived) {
qstyle = SQCX_SCALAR_DERIVED;
}
else {
qstyle = SQCX_SCALAR_EXPOUNDED;
}
// QCC marker
hbuf.writeShort(QCC);
// Compute the number of steps to send
switch (qstyle) {
case SQCX_SCALAR_DERIVED:
nqcc = 1; // Just the LL value
break;
case SQCX_NO_QUANTIZATION:
case SQCX_SCALAR_EXPOUNDED:
// One value per subband
nqcc = 0;
sb = sbRoot;
mrl = sb.resLvl;
// Get the subband at first resolution level
sb = (SubbandAn)sb.getSubbandByIdx(0,0);
// Find root element for LL subband
while (sb.resLvl != 0) {
sb = sb.subb_LL;
}
// Count total number of subbands
for (int j=0; j<=mrl; j++) {
sb2 = sb;
while (sb2 != null) {
nqcc++;
sb2 = (SubbandAn) sb2.nextSubband();
}
// Go up one resolution level
sb = (SubbandAn) sb.getNextResLevel();
}
break;
default:
throw new Error("Internal JJ2000 error");
}
// Lqcc (marker segment length (in bytes))
// Lqcc(2 bytes)+Cqcc(1 or 2)+Sqcc(1)+ SPqcc (2*Nqcc)
int markSegLen = 3 + ((nComp < 257) ? 1 : 2) +
((isReversible) ? nqcc : 2*nqcc);
hbuf.writeShort(markSegLen);
// Cqcc
if (nComp < 257) {
hbuf.write(compIdx);
}
else {
hbuf.writeShort(compIdx);
}
// Sqcc (quantization style)
hbuf.write(qstyle+(gb<<SQCX_GB_SHIFT));
// SPqcc
switch (qstyle) {
case SQCX_NO_QUANTIZATION:
// Get resolution level 0 subband
sb = sbRoot;
sb = (SubbandAn) sb.getSubbandByIdx(0,0);
// Output one exponent per subband
for (int j=0; j<=mrl; j++) {
sb2 = sb;
while (sb2 != null) {
int tmp = (imgnr+sb2.anGainExp);
hbuf.write(tmp<<SQCX_EXP_SHIFT);
sb2 = (SubbandAn)sb2.nextSubband();
}
// Go up one resolution level
sb = (SubbandAn)sb.getNextResLevel();
}
break;
case SQCX_SCALAR_DERIVED:
// Get resolution level 0 subband
sb = sbRoot;
sb = (SubbandAn) sb.getSubbandByIdx(0,0);
// Calculate subband step (normalized to unit
// dynamic range)
step = baseStep/(1<<sb.level);
// Write exponent-mantissa, 16 bits
hbuf.writeShort(StdQuantizer.
convertToExpMantissa(step));
break;
case SQCX_SCALAR_EXPOUNDED:
// Get resolution level 0 subband
sb = sbRoot;
mrl = sb.resLvl;
sb = (SubbandAn) sb.getSubbandByIdx(0,0);
for (int j=0; j<=mrl; j++) {
sb2 = sb;
while (sb2 != null) {
// Calculate subband step (normalized to unit
// dynamic range)
step = baseStep/(sb2.l2Norm*(1<<sb2.anGainExp));
// Write exponent-mantissa, 16 bits
hbuf.writeShort(StdQuantizer.
convertToExpMantissa(step));
sb2 = (SubbandAn)sb2.nextSubband();
}
// Go up one resolution level
sb = (SubbandAn) sb.getNextResLevel();
}
break;
default:
throw new Error("Internal JJ2000 error");
}
}
/**
* Writes QCD marker segment in tile header. QCD is a functional
* marker segment countaining the quantization default used for
* compressing all the components in an image. The values can be
* overriden for an individual component by a QCC marker in either
* the main or the tile header.
*
* @param tIdx Tile index
* */
protected void writeTileQCD(int tIdx) throws IOException{
int mrl;
int qstyle;
float step;
SubbandAn sb,csb,sbRoot;
String qType = (String)wp.getQuantizationType().getTileDef(tIdx);
float baseStep = ((Float)wp.getQuantizationStep().getTileDef(tIdx)).floatValue();
mrl = ((Integer)wp.getDecompositionLevel().getTileDef(tIdx)).intValue();
int nc = dwt.getNumComps();
int tmpI;
String tmpStr;
boolean notFound = true;
int compIdx = 0;
for(int c=0; c<nc && notFound; c++) {
tmpI = ((Integer)wp.getDecompositionLevel().getTileCompVal(tIdx,c)).intValue();
tmpStr = (String)wp.getQuantizationStep().getTileCompVal(tIdx,c);
if(tmpI==mrl && tmpStr.equals(qType)) {
compIdx = c;
notFound = false;
}
}
if(notFound) {
throw new Error("Default representative for quantization type "+
" and number of decomposition levels not found "+
" in tile QCD (t="+tIdx+") marker segment. "+
"You have found a JJ2000 bug.");
}
sbRoot = dwt.getAnSubbandTree(tIdx,compIdx);
deftilenr = dwt.getNomRangeBits(compIdx);
int gb = ((Integer)wp.getGuardBits().getTileDef(tIdx)).intValue();
boolean isDerived = qType.equals("derived");
boolean isReversible = qType.equals("reversible");
int nqcd; // Number of quantization step-size to transmit
// Get the quantization style
qstyle = (isReversible) ? SQCX_NO_QUANTIZATION :
((isDerived) ? SQCX_SCALAR_DERIVED : SQCX_SCALAR_EXPOUNDED);
// QCD marker
hbuf.writeShort(QCD);
// Compute the number of steps to send
switch (qstyle) {
case SQCX_SCALAR_DERIVED:
nqcd = 1; // Just the LL value
break;
case SQCX_NO_QUANTIZATION:
case SQCX_SCALAR_EXPOUNDED:
// One value per subband
nqcd=0;
sb=sbRoot;
// Get the subband at first resolution level
sb = (SubbandAn) sb.getSubbandByIdx(0,0);
// Count total number of subbands
for (int j=0; j<=mrl; j++) {
csb = sb;
while (csb != null) {
nqcd++;
csb = (SubbandAn) csb.nextSubband();
}
// Go up one resolution level
sb = (SubbandAn) sb.getNextResLevel();
}
break;
default:
throw new Error("Internal JJ2000 error");
}
// Lqcd (marker segment length (in bytes))
// Lqcd(2 bytes)+Sqcd(1)+ SPqcd (2*Nqcd)
int markSegLen = 3 + ((isReversible) ? nqcd : 2*nqcd);
// Rounded to the nearest even value greater or equals
hbuf.writeShort(markSegLen);
// Sqcd
hbuf.write(qstyle+(gb<<SQCX_GB_SHIFT));
// SPqcd
switch (qstyle) {
case SQCX_NO_QUANTIZATION:
sb = sbRoot;
sb = (SubbandAn)sb.getSubbandByIdx(0,0);
// Output one exponent per subband
for (int j=0; j<=mrl; j++) {
csb = sb;
while(csb != null) {
int tmp = (deftilenr+csb.anGainExp);
hbuf.write(tmp<<SQCX_EXP_SHIFT);
csb = (SubbandAn)csb.nextSubband();
// Go up one resolution level
}
sb = (SubbandAn)sb.getNextResLevel();
}
break;
case SQCX_SCALAR_DERIVED:
sb = sbRoot;
sb = (SubbandAn)sb.getSubbandByIdx(0,0);
// Calculate subband step (normalized to unit
// dynamic range)
step = baseStep/(1<<sb.level);
// Write exponent-mantissa, 16 bits
hbuf.writeShort(StdQuantizer.
convertToExpMantissa(step));
break;
case SQCX_SCALAR_EXPOUNDED:
sb = sbRoot;
sb = (SubbandAn)sb.getSubbandByIdx(0,0);
// Output one step per subband
for (int j=0; j<=mrl; j++) {
csb = sb;
while(csb != null) {
// Calculate subband step (normalized to unit
// dynamic range)
step = baseStep/(csb.l2Norm*(1<<csb.anGainExp));
// Write exponent-mantissa, 16 bits
hbuf.writeShort(StdQuantizer.
convertToExpMantissa(step));
csb = (SubbandAn)csb.nextSubband();
}
// Go up one resolution level
sb = (SubbandAn)sb.getNextResLevel();
}
break;
default:
throw new Error("Internal JJ2000 error");
}
}
/**
* Writes QCC marker segment in tile header. It is a functional
* marker segment countaining the quantization used for
* compressing the specified component in an image. The values
* override for the specified component what was defined by a QCC
* marker in either the main or the tile header.
*
* @param t Tile index
*
* @param compIdx Index of the component which needs QCC marker
* segment.
* */
protected void writeTileQCC(int t,int compIdx)
throws IOException{
int mrl;
int qstyle;
float step;
SubbandAn sb,sb2;
int nqcc; // Number of quantization step-size to transmit
SubbandAn sbRoot = dwt.getAnSubbandTree(t,compIdx);
int imgnr = dwt.getNomRangeBits(compIdx);
String qType = (String)wp.getQuantizationType().getTileCompVal(t,compIdx);
float baseStep = ((Float)wp.getQuantizationStep().getTileCompVal(t,compIdx)).
floatValue();
int gb = ((Integer)wp.getGuardBits().getTileCompVal(t,compIdx)).intValue();
boolean isReversible = qType.equals("reversible");
boolean isDerived = qType.equals("derived");
mrl = ((Integer)wp.getDecompositionLevel().getTileCompVal(t,compIdx)).intValue();
// Get the quantization style
if(isReversible) {
qstyle = SQCX_NO_QUANTIZATION;
}
else if (isDerived) {
qstyle = SQCX_SCALAR_DERIVED;
}
else {
qstyle = SQCX_SCALAR_EXPOUNDED;
}
// QCC marker
hbuf.writeShort(QCC);
// Compute the number of steps to send
switch (qstyle) {
case SQCX_SCALAR_DERIVED:
nqcc = 1; // Just the LL value
break;
case SQCX_NO_QUANTIZATION:
case SQCX_SCALAR_EXPOUNDED:
// One value per subband
nqcc = 0;
sb = sbRoot;
mrl = sb.resLvl;
// Get the subband at first resolution level
sb = (SubbandAn)sb.getSubbandByIdx(0,0);
// Find root element for LL subband
while (sb.resLvl != 0) {
sb = sb.subb_LL;
}
// Count total number of subbands
for (int j=0; j<=mrl; j++) {
sb2 = sb;
while (sb2 != null) {
nqcc++;
sb2 = (SubbandAn) sb2.nextSubband();
}
// Go up one resolution level
sb = (SubbandAn) sb.getNextResLevel();
}
break;
default:
throw new Error("Internal JJ2000 error");
}
// Lqcc (marker segment length (in bytes))
// Lqcc(2 bytes)+Cqcc(1 or 2)+Sqcc(1)+ SPqcc (2*Nqcc)
int markSegLen = 3 + ((nComp < 257) ? 1 : 2) +
((isReversible) ? nqcc : 2*nqcc);
hbuf.writeShort(markSegLen);
// Cqcc
if (nComp < 257) {
hbuf.write(compIdx);
}
else {
hbuf.writeShort(compIdx);
}
// Sqcc (quantization style)
hbuf.write(qstyle+(gb<<SQCX_GB_SHIFT));
// SPqcc
switch (qstyle) {
case SQCX_NO_QUANTIZATION:
// Get resolution level 0 subband
sb = sbRoot;
sb = (SubbandAn) sb.getSubbandByIdx(0,0);
// Output one exponent per subband
for (int j=0; j<=mrl; j++) {
sb2 = sb;
while (sb2 != null) {
int tmp = (imgnr+sb2.anGainExp);
hbuf.write(tmp<<SQCX_EXP_SHIFT);
sb2 = (SubbandAn)sb2.nextSubband();
}
// Go up one resolution level
sb = (SubbandAn)sb.getNextResLevel();
}
break;
case SQCX_SCALAR_DERIVED:
// Get resolution level 0 subband
sb = sbRoot;
sb = (SubbandAn) sb.getSubbandByIdx(0,0);
// Calculate subband step (normalized to unit
// dynamic range)
step = baseStep/(1<<sb.level);
// Write exponent-mantissa, 16 bits
hbuf.writeShort(StdQuantizer.
convertToExpMantissa(step));
break;
case SQCX_SCALAR_EXPOUNDED:
// Get resolution level 0 subband
sb = sbRoot;
mrl = sb.resLvl;
sb = (SubbandAn) sb.getSubbandByIdx(0,0);
for (int j=0; j<=mrl; j++) {
sb2 = sb;
while (sb2 != null) {
// Calculate subband step (normalized to unit
// dynamic range)
step = baseStep/(sb2.l2Norm*(1<<sb2.anGainExp));
// Write exponent-mantissa, 16 bits
hbuf.writeShort(StdQuantizer.
convertToExpMantissa(step));
sb2 = (SubbandAn)sb2.nextSubband();
}
// Go up one resolution level
sb = (SubbandAn) sb.getNextResLevel();
}
break;
default:
throw new Error("Internal JJ2000 error");
}
}
/**
* Writes POC marker segment. POC is a functional marker segment
* containing the bounds and progression order for any progression order
* other than default in the codestream.
*
* @param mh Flag indicating whether the main header is to be written
*
* @param tileIdx Tile index
* */
protected void writePOC(boolean mh, int tileIdx) throws IOException {
int markSegLen=0; // Segment marker length
int lenCompField; // Holds the size of any component field as
// this size depends on the number of
//components
Progression[] prog = null; // Holds the progression(s)
int npoc; // Number of progression order changes
// Get the progression order changes, their number and checks
// if it is ok
if(mh){
prog = (Progression[])(wp.getProgressionType().getDefault());
}
else {
prog = (Progression[])(wp.getProgressionType().getTileDef(tileIdx));
}
// Calculate the length of a component field (depends on the number of
// components)
lenCompField = (nComp<257 ? 1 : 2);
// POC marker
hbuf.writeShort(POC);
// Lpoc (marker segment length (in bytes))
// Basic: Lpoc(2 bytes) + npoc * [ RSpoc(1) + CSpoc(1 or 2) + LYEpoc(2)
// + REpoc(1) + CEpoc(1 or 2) + Ppoc(1) ]
npoc = prog.length;
markSegLen = 2 + npoc * (1+lenCompField+2+1+lenCompField+1);
hbuf.writeShort(markSegLen);
// Write each progression order change
for (int i=0 ; i<npoc ; i++){
// RSpoc(i)
hbuf.write(prog[i].rs);
// CSpoc(i)
if ( lenCompField==2 ) {
hbuf.writeShort(prog[i].cs);
}
else {
hbuf.write(prog[i].cs);
}
// LYEpoc(i)
hbuf.writeShort(prog[i].lye);
// REpoc(i)
hbuf.write(prog[i].re);
// CEpoc(i)
if ( lenCompField==2 ) {
hbuf.writeShort(prog[i].ce);
}
else {
hbuf.write(prog[i].ce);
}
// Ppoc(i)
hbuf.write(prog[i].type);
}
}
/**
* Write main header. JJ2000 main header corresponds to the following
* sequence of marker
* segments:<ol><li>SOC</li><li>SIZ</li><li>COD</li><li>COC (if
* needed)</li><li>QCD</li><li>QCC (if needed)</li><li>POC (if
* needed)</li></ol>
* */
public void encodeMainHeader() throws IOException {
int i;
// +---------------------------------+
// | SOC marker segment |
// +---------------------------------+
writeSOC();
// +---------------------------------+
// | Image and tile SIZe (SIZ) |
// +---------------------------------+
writeSIZ();
// +-------------------------------+
// | COding style Default (COD) |
// +-------------------------------+
boolean isEresUsed = ((String)wp.getTerminateOnByte().getDefault()).
equals("predict");
writeCOD(true,0);
// +---------------------------------+
// | COding style Component (COC) |
// +---------------------------------+
for (i= 0; i<nComp; i++) {
boolean isEresUsedinComp = ((String)wp.getTerminateOnByte().getCompDef(i)).
equals("predict");
if(wp.getFilters().isCompSpecified(i) ||
wp.getDecompositionLevel().isCompSpecified(i) ||
wp.getBypass().isCompSpecified(i) ||
wp.getResetMQ().isCompSpecified(i) ||
wp.getMethodForMQTermination().isCompSpecified(i) ||
wp.getCodeSegSymbol().isCompSpecified(i) ||
wp.getCausalCXInfo().isCompSpecified(i) ||
wp.getPrecinctPartition().isCompSpecified(i) ||
wp.getCodeBlockSize().isCompSpecified(i) ||
(isEresUsed != isEresUsedinComp ) )
// Some component non-default stuff => need COC
writeCOC(true,0,i);
}
// +-------------------------------+
// | Quantization Default (QCD) |
// +-------------------------------+
writeMainQCD();
// +-------------------------------+
// | Quantization Component (QCC) |
// +-------------------------------+
// Write needed QCC markers
for(i=0; i<nComp; i++){
if(dwt.getNomRangeBits(i)!= defimgn ||
wp.getQuantizationType().isCompSpecified(i) ||
wp.getQuantizationStep().isCompSpecified(i) ||
wp.getDecompositionLevel().isCompSpecified(i) ||
wp.getGuardBits().isCompSpecified(i)){
writeMainQCC(i);
}
}
// +--------------------------+
// | POC maker segment |
// +--------------------------+
Progression[] prog = (Progression[])(wp.getProgressionType().getDefault());
if(prog.length>1)
writePOC(true, 0);
// +--------------------------+
// | Comment (COM) |
// +--------------------------+
writeCOM();
}
/**
* Write a COM marker segment adding some comments to the codestream.
*
* <p> This marker is currently written in main header and indicates the
* JJ2000 encoder's version that has created the codestream.
* */
private void writeCOM() throws IOException {
// JJ2000 COM marker segment
if(enJJ2KMarkSeg) {
String str = "Created by: JJ2000 version "+JJ2KInfo.version;
int markSegLen; // the marker segment length
// COM marker
hbuf.writeShort(COM);
// Calculate length: Lcom(2) + Rcom (2) + string's length;
markSegLen = 2 + 2 + str.length();
hbuf.writeShort(markSegLen);
// Rcom
hbuf.writeShort(1); // General use (IS 8859-15:1999(Latin) values)
byte[] chars = str.getBytes();
for(int i=0; i<chars.length; i++) {
hbuf.writeByte(chars[i]);
}
}
// other COM marker segments
if(otherCOMMarkSeg!=null) {
StringTokenizer stk = new StringTokenizer(otherCOMMarkSeg,"#");
while(stk.hasMoreTokens()) {
String str = stk.nextToken();
int markSegLen; // the marker segment length
// COM marker
hbuf.writeShort(COM);
// Calculate length: Lcom(2) + Rcom (2) + string's length;
markSegLen = 2 + 2 + str.length();
hbuf.writeShort(markSegLen);
// Rcom
hbuf.writeShort(1); // General use (IS 8859-15:1999(Latin)
// values)
byte[] chars = str.getBytes();
for(int i=0; i<chars.length; i++) {
hbuf.writeByte(chars[i]);
}
}
}
}
/**
* Writes the RGN marker segment in the tile header. It describes the
* scaling value in each tile component
*
* <P>May be used in tile or main header. If used in main header, it
* refers to a ROI of the whole image, regardless of tiling. When used in
* tile header, only the particular tile is affected.
*
* @param tIdx The tile index
*
* @exception IOException If an I/O error occurs while reading from the
* encoder header stream
* */
private void writeRGN(int tIdx) throws IOException {
int i;
int markSegLen; // the marker length
// Write one RGN marker per component
for(i=0;i<nComp;i++){
// RGN marker
hbuf.writeShort(RGN);
// Calculate length (Lrgn)
// Basic: Lrgn (2) + Srgn (1) + SPrgn + one byte
// or two for component number
markSegLen = 4+((nComp<257)? 1:2);
hbuf.writeShort(markSegLen);
// Write component (Crgn)
if(nComp<257)
hbuf.writeByte(i);
else
hbuf.writeShort(i);
// Write type of ROI (Srgn)
hbuf.writeByte(SRGN_IMPLICIT);
// Write ROI info (SPrgn)
hbuf.writeByte(((Integer)(wp.getROIs().
getTileCompVal(tIdx,i))).intValue());
}
}
/**
* Writes tile-part header. JJ2000 tile-part header corresponds to the
* following sequence of marker segments:<ol> <li>SOT</li> <li>COD (if
* needed)</li> <li>COC (if needed)</li> <li>QCD (if needed)</li> <li>QCC
* (if needed)</li> <li>RGN (if needed)</li> <li>POC (if needed)</li>
* <li>SOD</li> </ol>
*
* @param length The length of the current tile-part.
*
* @param tileIdx Index of the tile to write
* */
public void encodeTilePartHeader(int tileLength,int tileIdx)
throws IOException {
int tmp;
Point numTiles = ralloc.getNumTiles(null);
ralloc.setTile(tileIdx%numTiles.x,tileIdx/numTiles.x);
// +--------------------------+
// | SOT maker segment |
// +--------------------------+
// SOT marker
hbuf.writeByte(SOT>>8);
hbuf.writeByte(SOT);
// Lsot (10 bytes)
hbuf.writeByte(0);
hbuf.writeByte(10);
// Isot
if(tileIdx>65534){
throw new IllegalArgumentException("Trying to write a tile-part "+
"header whose tile index is too"+
" high");
}
hbuf.writeByte(tileIdx>>8);
hbuf.writeByte(tileIdx);
// Psot
tmp = tileLength;
hbuf.writeByte(tmp>>24);
hbuf.writeByte(tmp>>16);
hbuf.writeByte(tmp>>8);
hbuf.writeByte(tmp);
// TPsot
hbuf.writeByte(0); // Only one tile-part currently supported !
// TNsot
hbuf.writeByte(1); // Only one tile-part currently supported !
// +--------------------------+
// | COD maker segment |
// +--------------------------+
boolean isEresUsed = ((String)wp.getMethodForMQTermination().getDefault()).
equals("predict");
boolean isEresUsedInTile = ((String)wp.getMethodForMQTermination().getTileDef(tileIdx)).
equals("predict");
boolean tileCODwritten = false;
if(wp.getFilters().isTileSpecified(tileIdx) ||
wp.getComponentTransformation().isTileSpecified(tileIdx) ||
wp.getDecompositionLevel().isTileSpecified(tileIdx) ||
wp.getBypass().isTileSpecified(tileIdx) ||
wp.getResetMQ().isTileSpecified(tileIdx) ||
wp.getTerminateOnByte().isTileSpecified(tileIdx) ||
wp.getCausalCXInfo().isTileSpecified(tileIdx) ||
wp.getPrecinctPartition().isTileSpecified(tileIdx) ||
wp.getSOP().isTileSpecified(tileIdx) ||
wp.getCodeSegSymbol().isTileSpecified(tileIdx) ||
wp.getProgressionType().isTileSpecified(tileIdx) ||
wp.getEPH().isTileSpecified(tileIdx) ||
wp.getCodeBlockSize().isTileSpecified(tileIdx) ||
( isEresUsed != isEresUsedInTile ) ) {
writeCOD(false,tileIdx);
tileCODwritten = true;
}
// +--------------------------+
// | COC maker segment |
// +--------------------------+
for(int c=0; c<nComp; c++){
boolean isEresUsedInTileComp = ((String)wp.getMethodForMQTermination().
getTileCompVal(tileIdx,c)).
equals("predict");
if(wp.getFilters().isTileCompSpecified(tileIdx,c) ||
wp.getDecompositionLevel().isTileCompSpecified(tileIdx,c) ||
wp.getBypass().isTileCompSpecified(tileIdx,c) ||
wp.getResetMQ().isTileCompSpecified(tileIdx,c) ||
wp.getTerminateOnByte().isTileCompSpecified(tileIdx,c) ||
wp.getCausalCXInfo().isTileCompSpecified(tileIdx,c) ||
wp.getPrecinctPartition().isTileCompSpecified(tileIdx,c) ||
wp.getCodeSegSymbol().isTileCompSpecified(tileIdx,c) ||
wp.getCodeBlockSize().isTileCompSpecified(tileIdx,c) ||
( isEresUsedInTileComp != isEresUsed ) ) {
writeCOC(false,tileIdx,c);
}
else if(tileCODwritten){
if(wp.getFilters().isCompSpecified(c) ||
wp.getDecompositionLevel().isCompSpecified(c) ||
wp.getBypass().isCompSpecified(c) ||
wp.getResetMQ().isCompSpecified(c) ||
wp.getTerminateOnByte().isCompSpecified(c) ||
wp.getCodeSegSymbol().isCompSpecified(c) ||
wp.getCausalCXInfo().isCompSpecified(c) ||
wp.getPrecinctPartition().isCompSpecified(c) ||
wp.getCodeBlockSize().isCompSpecified(c) ||
(wp.getMethodForMQTermination().isCompSpecified(c)&&
((String)wp.getMethodForMQTermination().getCompDef(c)).equals("predict"))){
writeCOC(false,tileIdx,c);
}
}
}
// +--------------------------+
// | QCD maker segment |
// +--------------------------+
boolean tileQCDwritten = false;
if(wp.getQuantizationType().isTileSpecified(tileIdx) ||
wp.getQuantizationStep().isTileSpecified(tileIdx) ||
wp.getDecompositionLevel().isTileSpecified(tileIdx) ||
wp.getGuardBits().isTileSpecified(tileIdx)){
writeTileQCD(tileIdx);
tileQCDwritten = true;
} else {
deftilenr = defimgn;
}
// +--------------------------+
// | QCC maker segment |
// +--------------------------+
for(int c=0; c<nComp; c++){
if(dwt.getNomRangeBits(c)!= deftilenr ||
wp.getQuantizationType().isTileCompSpecified(tileIdx,c) ||
wp.getQuantizationStep().isTileCompSpecified(tileIdx,c) ||
wp.getDecompositionLevel().isTileCompSpecified(tileIdx,c) ||
wp.getGuardBits().isTileCompSpecified(tileIdx,c)){
writeTileQCC(tileIdx,c);
}
else if(tileQCDwritten){
if(wp.getQuantizationType().isCompSpecified(c) ||
wp.getQuantizationStep().isCompSpecified(c) ||
wp.getDecompositionLevel().isCompSpecified(c) ||
wp.getGuardBits().isCompSpecified(c)){
writeTileQCC(tileIdx,c);
}
}
}
// +--------------------------+
// | RGN maker segment |
// +--------------------------+
if(roiSc.useRoi() &&(!roiSc.getBlockAligned()))
writeRGN(tileIdx);
// +--------------------------+
// | POC maker segment |
// +--------------------------+
Progression[] prog;
if( wp.getProgressionType().isTileSpecified(tileIdx) ){
prog = (Progression[])(wp.getProgressionType().getTileDef(tileIdx));
if(prog.length>1)
writePOC(false,tileIdx);
}
// +--------------------------+
// | SOD maker |
// +--------------------------+
hbuf.writeByte(SOD>>8);
hbuf.writeByte(SOD);
}
}