001/*
002 * $RCSfile: HeaderEncoder.java,v $
003 * $Revision: 1.1 $
004 * $Date: 2005/02/11 05:02:02 $
005 * $State: Exp $
006 *
007 * Class:                   HeaderEncoder
008 *
009 * Description:             Write codestream headers.
010 *
011 *
012 *
013 * COPYRIGHT:
014 *
015 * This software module was originally developed by Raphaël Grosbois and
016 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
017 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
018 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
019 * Centre France S.A) in the course of development of the JPEG2000
020 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
021 * software module is an implementation of a part of the JPEG 2000
022 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
023 * Systems AB and Canon Research Centre France S.A (collectively JJ2000
024 * Partners) agree not to assert against ISO/IEC and users of the JPEG
025 * 2000 Standard (Users) any of their rights under the copyright, not
026 * including other intellectual property rights, for this software module
027 * with respect to the usage by ISO/IEC and Users of this software module
028 * or modifications thereof for use in hardware or software products
029 * claiming conformance to the JPEG 2000 Standard. Those intending to use
030 * this software module in hardware or software products are advised that
031 * their use may infringe existing patents. The original developers of
032 * this software module, JJ2000 Partners and ISO/IEC assume no liability
033 * for use of this software module or modifications thereof. No license
034 * or right to this software module is granted for non JPEG 2000 Standard
035 * conforming products. JJ2000 Partners have full right to use this
036 * software module for his/her own purpose, assign or donate this
037 * software module to any third party and to inhibit third parties from
038 * using this software module for non JPEG 2000 Standard conforming
039 * products. This copyright notice must be included in all copies or
040 * derivative works of this software module.
041 *
042 * Copyright (c) 1999/2000 JJ2000 Partners.
043 * */
044package jj2000.j2k.codestream.writer;
045import java.awt.Point;
046import java.beans.Encoder;
047import java.io.ByteArrayOutputStream;
048import java.io.DataOutputStream;
049import java.io.IOException;
050import java.io.OutputStream;
051import java.util.StringTokenizer;
052import java.util.Vector;
053
054import jj2000.j2k.JJ2KInfo;
055import jj2000.j2k.ModuleSpec;
056import jj2000.j2k.codestream.Markers;
057import jj2000.j2k.entropy.Progression;
058import jj2000.j2k.entropy.StdEntropyCoderOptions;
059import jj2000.j2k.entropy.encoder.EBCOTRateAllocator;
060import jj2000.j2k.entropy.encoder.PostCompRateAllocator;
061import jj2000.j2k.image.ImgData;
062import jj2000.j2k.image.Tiler;
063import jj2000.j2k.io.BinaryDataOutput;
064import jj2000.j2k.quantization.quantizer.StdQuantizer;
065import jj2000.j2k.roi.encoder.ROIScaler;
066import jj2000.j2k.util.MathUtil;
067import jj2000.j2k.wavelet.analysis.AnWTFilter;
068import jj2000.j2k.wavelet.analysis.ForwardWT;
069import jj2000.j2k.wavelet.analysis.SubbandAn;
070
071import com.github.jaiimageio.jpeg2000.impl.J2KImageWriteParamJava;
072/**
073 * This class writes almost of the markers and marker segments in main header
074 * and in tile-part headers. It is created by the run() method of the Encoder
075 * instance.
076 *
077 * <p>A marker segment includes a marker and eventually marker segment
078 * parameters. It is designed by the three letter code of the marker
079 * associated with the marker segment. JPEG 2000 part I defines 6 types of
080 * markers: <ul> <li> Delimiting : SOC,SOT,SOD,EOC (written in
081 * FileCodestreamWriter).</li> <li> Fixed information: SIZ.</li> <li>
082 * Functional: COD,COC,RGN,QCD,QCC,POC.</li> <li> In bit-stream: SOP,EPH.</li>
083 * <li> Pointer: TLM,PLM,PLT,PPM,PPT.</li> <li> Informational:
084 * CRG,COM.</li></ul>
085 *
086 * <p>Main Header is written when Encoder instance calls encodeMainHeader
087 * whereas tile-part headers are written when the EBCOTRateAllocator instance
088 * calls encodeTilePartHeader.
089 *
090 * @see Encoder
091 * @see Markers
092 * @see EBCOTRateAllocator
093 * */
094public class HeaderEncoder implements Markers, StdEntropyCoderOptions {
095
096    /** Nominal range bit of the component defining default values in QCD for
097     * main header */
098    private int defimgn;
099
100    /** Nominal range bit of the component defining default values in QCD for
101     * tile headers */
102    private int deftilenr;
103
104    /** The number of components in the image */
105    protected int nComp;
106
107    /** Whether or not to write the JJ2000 COM marker segment */
108    private boolean enJJ2KMarkSeg = true;
109
110    /** Other COM marker segments specified in the command line */
111    private String otherCOMMarkSeg = null;
112
113    /** The ByteArrayOutputStream to store header data. This handler
114     * is kept in order to use methods not accessible from a general
115     * DataOutputStream. For the other methods, it's better to use
116     * variable hbuf.
117     *
118     * @see #hbuf */
119    protected ByteArrayOutputStream baos;
120
121    /** The DataOutputStream to store header data. This kind of object
122     * is useful to write short, int, .... It's constructor takes
123     * baos as parameter.
124     *
125     * @see #baos
126     **/
127    protected DataOutputStream hbuf;
128
129    /** The image data reader. Source of original data info */
130    protected ImgData origSrc;
131
132    /** An array specifying, for each component,if the data was signed
133        or not */
134    protected boolean isOrigSig[];
135
136    /** Reference to the rate allocator */
137    protected PostCompRateAllocator ralloc;
138
139    /** Reference to the DWT module */
140    protected ForwardWT dwt;
141
142    /** Reference to the tiler module */
143    protected Tiler tiler;
144
145    /** Reference to the ROI module */
146    protected ROIScaler roiSc;
147
148    /** The encoder specifications */
149    protected J2KImageWriteParamJava wp;
150
151    /**
152     * Initializes the header writer with the references to the coding chain.
153     *
154     * @param origsrc The original image data (before any component mixing,
155     * tiling, etc.)
156     *
157     * @param isorigsig An array specifying for each component if it was
158     * originally signed or not.
159     *
160     * @param dwt The discrete wavelet transform module.
161     *
162     * @param tiler The tiler module.
163     *
164     * @param encSpec The encoder specifications
165     *
166     * @param roiSc The ROI scaler module.
167     *
168     * @param ralloc The post compression rate allocator.
169     * */
170    public HeaderEncoder(ImgData origsrc, boolean isorigsig[],
171                         ForwardWT dwt, Tiler tiler,J2KImageWriteParamJava wp,
172                         ROIScaler roiSc, PostCompRateAllocator ralloc) {
173        if (origsrc.getNumComps() != isorigsig.length) {
174            throw new IllegalArgumentException();
175        }
176        this.origSrc   = origsrc;
177        this.isOrigSig = isorigsig;
178        this.dwt       = dwt;
179        this.tiler     = tiler;
180        this.wp   = wp;
181        this.roiSc     = roiSc;
182        this.ralloc    = ralloc;
183
184        baos = new ByteArrayOutputStream();
185        hbuf = new DataOutputStream(baos);
186        nComp = origsrc.getNumComps();
187//        enJJ2KMarkSeg = wp.getHjj2000_COM();
188//        otherCOMMarkSeg = wp.getHCOM();
189    }
190
191    /**
192     * Resets the contents of this HeaderEncoder to its initial state. It
193     * erases all the data in the header buffer and reactualizes the
194     * headerLength field of the bit stream writer.
195     * */
196    public void reset() {
197        baos.reset();
198        hbuf = new DataOutputStream(baos);
199    }
200
201    /**
202     * Returns the byte-buffer used to store the codestream header.
203     *
204     * @return A byte array countaining codestream header
205     * */
206    protected byte[] getBuffer(){
207        return baos.toByteArray();
208    }
209
210    /**
211     * Returns the length of the header.
212     *
213     * @return The length of the header in bytes
214     * */
215    public int getLength() {
216        return hbuf.size();
217    }
218
219    /**
220     * Writes the header to the specified BinaryDataOutput.
221     *
222     * @param out Where to write the header.
223     * */
224    public void writeTo(BinaryDataOutput out) throws IOException {
225        int i,len;
226        byte buf[];
227
228        buf = getBuffer();
229        len = getLength();
230
231        for (i=0; i<len; i++) {
232            out.writeByte(buf[i]);
233        }
234    }
235
236    /**
237     * Returns the number of bytes used in the codestream header's
238     * buffer.
239     *
240     * @return Header length in buffer (without any header
241     * overhead)
242     * */
243    protected int getBufferLength(){
244        return baos.size();
245    }
246
247    /**
248     * Writes the header to the specified OutputStream.
249     *
250     * @param out Where to write the header.
251     * */
252    public void writeTo(OutputStream out) throws IOException {
253        out.write(getBuffer(),0,getBufferLength());
254    }
255
256    /**
257     * Start Of Codestream marker (SOC) signalling the beginning of a
258     * codestream.
259     * */
260    private void writeSOC() throws IOException {
261        hbuf.writeShort(SOC);
262    }
263
264    /**
265     * Writes SIZ marker segment of the codestream header. It is a fixed
266     * information marker segment containing informations about image and tile
267     * sizes. It is required in the main header immediately after SOC marker
268     * segment.
269     * */
270    private void writeSIZ() throws IOException {
271        int tmp;
272
273        // SIZ marker
274        hbuf.writeShort(SIZ);
275
276        // Lsiz (Marker length) corresponding to
277        // Lsiz(2 bytes)+Rsiz(2)+Xsiz(4)+Ysiz(4)+XOsiz(4)+YOsiz(4)+
278        // XTsiz(4)+YTsiz(4)+XTOsiz(4)+YTOsiz(4)+Csiz(2)+
279        // (Ssiz(1)+XRsiz(1)+YRsiz(1))*nComp
280        // markSegLen = 38 + 3*nComp;
281        int markSegLen = 38 + 3*nComp;
282        hbuf.writeShort(markSegLen);
283
284        // Rsiz (codestream capabilities)
285        hbuf.writeShort(0); // JPEG 2000 - Part I
286
287        // Xsiz (original image width)
288        hbuf.writeInt(tiler.getImgWidth()+tiler.getImgULX());
289
290        // Ysiz (original image height)
291        hbuf.writeInt(tiler.getImgHeight()+tiler.getImgULY());
292
293        // XOsiz (horizontal offset from the origin of the reference
294        // grid to the left side of the image area)
295        hbuf.writeInt(tiler.getImgULX());
296
297        // YOsiz (vertical offset from the origin of the reference
298        // grid to the top side of the image area)
299        hbuf.writeInt(tiler.getImgULY());
300
301        // XTsiz (nominal tile width)
302        hbuf.writeInt(tiler.getNomTileWidth());
303
304        // YTsiz (nominal tile height)
305        hbuf.writeInt(tiler.getNomTileHeight());
306
307        Point torig = tiler.getTilingOrigin(null);
308        // XTOsiz (Horizontal offset from the origin of the reference
309        // grid to the left side of the first tile)
310        hbuf.writeInt(torig.x);
311
312        // YTOsiz (Vertical offset from the origin of the reference
313        // grid to the top side of the first tile)
314        hbuf.writeInt(torig.y);
315
316        // Csiz (number of components)
317        hbuf.writeShort(nComp);
318
319        // Bit-depth and downsampling factors.
320        for(int c=0; c<nComp; c++){ // Loop on each component
321
322            // Ssiz bit-depth before mixing
323            tmp = origSrc.getNomRangeBits(c)-1;
324
325            tmp |= ( (isOrigSig[c]?1:0)<<SSIZ_DEPTH_BITS );
326            hbuf.write(tmp);
327
328            // XRsiz (component sub-sampling value x-wise)
329            hbuf.write(tiler.getCompSubsX(c));
330
331            // YRsiz (component sub-sampling value y-wise)
332            hbuf.write(tiler.getCompSubsY(c));
333
334        } // End loop on each component
335
336    }
337
338    /**
339     * Writes COD marker segment. COD is a functional marker segment
340     * containing the code style default (coding style, decomposition,
341     * layering) used for compressing all the components in an image.
342     *
343     * <p>The values can be overriden for an individual component by a COC
344     * marker in either the main or the tile header.
345     *
346     * @param mh Flag indicating whether this marker belongs to the main
347     * header
348     *
349     * @param tileIdx Tile index if the marker belongs to a tile-part header
350     *
351     * @see #writeCOC
352     * */
353    protected void writeCOD(boolean mh, int tileIdx)
354        throws IOException {
355        AnWTFilter[][] filt;
356        boolean precinctPartitionUsed;
357        int tmp;
358        int mrl=0,a=0;
359        int ppx=0, ppy=0;
360        Progression[] prog;
361
362        if (mh) {
363            mrl = ((Integer)wp.getDecompositionLevel().getDefault()).intValue();
364            // get default precinct size
365            ppx = wp.getPrecinctPartition().getPPX(-1,-1,mrl);
366            ppy = wp.getPrecinctPartition().getPPY(-1,-1,mrl);
367            prog = (Progression[])(wp.getProgressionType().getDefault());
368        }
369        else {
370            mrl = ((Integer)wp.getDecompositionLevel().getTileDef(tileIdx)).intValue();
371            // get precinct size for specified tile
372            ppx = wp.getPrecinctPartition().getPPX(tileIdx,-1,mrl);
373            ppy = wp.getPrecinctPartition().getPPY(tileIdx,-1,mrl);
374            prog = (Progression[])(wp.getProgressionType().getTileDef(tileIdx));
375        }
376
377        if ( ppx != PRECINCT_PARTITION_DEF_SIZE ||
378             ppy != PRECINCT_PARTITION_DEF_SIZE ) {
379            precinctPartitionUsed = true;
380        }
381        else {
382            precinctPartitionUsed = false;
383        }
384
385        if ( precinctPartitionUsed ) {
386            // If precinct partition is used we add one byte per resolution
387            // level i.e. mrl+1 (+1 for resolution 0).
388            a = mrl+1;
389        }
390
391        // Write COD marker
392        hbuf.writeShort(COD);
393
394        // Lcod (marker segment length (in bytes)) Basic : Lcod(2
395        // bytes)+Scod(1)+SGcod(4)+SPcod(5+a)  where:
396        // a=0 if no precinct partition is used
397        // a=mrl+1 if precinct partition used
398        int markSegLen = 12+a;
399        hbuf.writeShort(markSegLen);
400
401        // Scod (coding style parameter)
402        tmp=0;
403        if ( precinctPartitionUsed )
404            tmp=SCOX_PRECINCT_PARTITION;
405
406        // Are SOP markers used ?
407        if (mh) {
408            if( ((String)wp.getSOP().getDefault().toString())
409                 .equalsIgnoreCase("true") ) {
410                tmp |= SCOX_USE_SOP;
411            }
412        }
413        else {
414            if ( ((String)wp.getSOP().getTileDef(tileIdx).toString())
415                 .equalsIgnoreCase("true") ) {
416                tmp |= SCOX_USE_SOP;
417            }
418        }
419
420        // Are EPH markers used ?
421        if(mh){
422            if ( ((String)wp.getEPH().getDefault().toString())
423                 .equalsIgnoreCase("true") ) {
424                tmp |= SCOX_USE_EPH;
425            }
426        }
427        else{
428            if ( ((String)wp.getEPH().getTileDef(tileIdx).toString())
429                 .equalsIgnoreCase("true") ) {
430                tmp |= SCOX_USE_EPH;
431            }
432        }
433        if (dwt.getCbULX()!=0) tmp |= SCOX_HOR_CB_PART;
434        if (dwt.getCbULY()!=0) tmp |= SCOX_VER_CB_PART;
435        hbuf.write(tmp);
436
437        // SGcod
438        // Progression order
439        hbuf.write(prog[0].type);
440
441        // Number of layers
442        hbuf.writeShort(ralloc.getNumLayers());
443
444        // Multiple component transform
445        // CSsiz (Color transform)
446        String str = null;
447        if(mh)
448            str = (String)wp.getComponentTransformation().getDefault();
449        else
450            str = (String)wp.getComponentTransformation().getTileDef(tileIdx);
451
452        if(str.equals("none"))
453            hbuf.write(0);
454        else
455            hbuf.write(1);
456
457        // SPcod
458        // Number of decomposition levels
459        hbuf.write(mrl);
460
461        // Code-block width and height
462        if ( mh ) {
463            // main header, get default values
464            tmp = wp.getCodeBlockSize().
465                getCBlkWidth(ModuleSpec.SPEC_DEF,-1,-1);
466            hbuf.write(MathUtil.log2(tmp)-2);
467            tmp = wp.getCodeBlockSize().
468                getCBlkHeight(ModuleSpec.SPEC_DEF,-1,-1);
469            hbuf.write(MathUtil.log2(tmp)-2);
470        }
471        else {
472            // tile header, get tile default values
473            tmp = wp.getCodeBlockSize().
474                getCBlkWidth(ModuleSpec.SPEC_TILE_DEF,tileIdx,-1);
475            hbuf.write(MathUtil.log2(tmp)-2);
476            tmp = wp.getCodeBlockSize().
477                getCBlkHeight(ModuleSpec.SPEC_TILE_DEF,tileIdx,-1);
478            hbuf.write(MathUtil.log2(tmp)-2);
479        }
480
481        // Style of the code-block coding passes
482        tmp = 0;
483        if(mh){ // Main header
484            // Selective arithmetic coding bypass ?
485            if( ((String)wp.getBypass().getDefault()).equals("true")) {
486                tmp |= OPT_BYPASS;
487            }
488            // MQ reset after each coding pass ?
489            if( ((String)wp.getResetMQ().getDefault()).equals("true")) {
490                tmp |= OPT_RESET_MQ;
491            }
492            // MQ termination after each arithmetically coded coding pass ?
493            if(  ((String)wp.getTerminateOnByte().getDefault()).equals("true") ) {
494                tmp |= OPT_TERM_PASS;
495            }
496            // Vertically stripe-causal context mode ?
497            if(  ((String)wp.getCausalCXInfo().getDefault()).equals("true") ) {
498                tmp |= OPT_VERT_STR_CAUSAL;
499            }
500            // Predictable termination ?
501            if( ((String)wp.getMethodForMQTermination().getDefault()).equals("predict")){
502                tmp |= OPT_PRED_TERM;
503            }
504            // Error resilience segmentation symbol insertion ?
505            if(  ((String)wp.getCodeSegSymbol().getDefault()).equals("true")) {
506                tmp |= OPT_SEG_SYMBOLS;
507            }
508        }
509        else{ // Tile header
510
511            // Selective arithmetic coding bypass ?
512            if( ((String)wp.getBypass().getTileDef(tileIdx)).equals("true")) {
513                tmp |= OPT_BYPASS;
514            }
515            // MQ reset after each coding pass ?
516            if( ((String)wp.getResetMQ().getTileDef(tileIdx)).equals("true")) {
517                tmp |= OPT_RESET_MQ;
518            }
519            // MQ termination after each arithmetically coded coding pass ?
520            if(  ((String)wp.getTerminateOnByte().getTileDef(tileIdx)).equals("true") ) {
521                tmp |= OPT_TERM_PASS;
522            }
523            // Vertically stripe-causal context mode ?
524            if(  ((String)wp.getCausalCXInfo().getTileDef(tileIdx)).equals("true") ) {
525                tmp |= OPT_VERT_STR_CAUSAL;
526            }
527            // Predictable termination ?
528            if( ((String)wp.getMethodForMQTermination().getTileDef(tileIdx)).equals("predict")){
529                tmp |= OPT_PRED_TERM;
530            }
531            // Error resilience segmentation symbol insertion ?
532            if(  ((String)wp.getCodeSegSymbol().getTileDef(tileIdx)).equals("true")) {
533                tmp |= OPT_SEG_SYMBOLS;
534            }
535        }
536        hbuf.write(tmp);
537
538        // Wavelet transform
539        // Wavelet Filter
540        if(mh){
541            filt=((AnWTFilter[][])wp.getFilters().getDefault());
542            hbuf.write(filt[0][0].getFilterType());
543        }else{
544            filt=((AnWTFilter[][])wp.getFilters().getTileDef(tileIdx));
545            hbuf.write(filt[0][0].getFilterType());
546        }
547
548        // Precinct partition
549        if ( precinctPartitionUsed ) {
550            // Write the precinct size for each resolution level + 1
551            // (resolution 0) if precinct partition is used.
552            Vector v[] = null;
553            if ( mh ) {
554                v = (Vector[])wp.getPrecinctPartition().getDefault();
555            }
556            else {
557                v = (Vector[])wp.getPrecinctPartition().getTileDef(tileIdx);
558            }
559            for (int r=mrl ; r>=0 ; r--) {
560                if ( r>=v[1].size() ) {
561                    tmp = ((Integer)v[1].elementAt(v[1].size()-1)).
562                        intValue();
563                }
564                else {
565                    tmp = ((Integer)v[1].elementAt(r)).intValue();
566                }
567                int yExp = (MathUtil.log2(tmp)<< 4) & 0x00F0;
568
569                if ( r>=v[0].size() ) {
570                    tmp = ((Integer)v[0].elementAt(v[0].size()-1)).
571                        intValue();
572                }
573                else {
574                    tmp = ((Integer)v[0].elementAt(r)).intValue();
575                }
576                int xExp = MathUtil.log2(tmp) & 0x000F;
577                hbuf.write(yExp|xExp);
578            }
579        }
580    }
581
582    /**
583     * Writes COC marker segment . It is a functional marker containing the
584     * coding style for one component (coding style, decomposition, layering).
585     *
586     * <P>Its values overrides any value previously set in COD in the main
587     * header or in the tile header.
588     *
589     * @param mh Flag indicating whether the main header is to be written
590     *
591     * @param tileIdx Tile index
592     *
593     * @param compIdx index of the component which need use of the COC marker
594     * segment.
595     *
596     * @see #writeCOD
597     * */
598    protected void writeCOC(boolean mh, int tileIdx, int compIdx)
599        throws IOException {
600        AnWTFilter[][] filt;
601        boolean precinctPartitionUsed;
602        int tmp;
603        int mrl=0,a=0;
604        int ppx=0, ppy=0;
605        Progression[] prog;
606
607        if (mh) {
608            mrl = ((Integer)wp.getDecompositionLevel().getCompDef(compIdx)).intValue();
609            // Get precinct size for specified component
610            ppx = wp.getPrecinctPartition().getPPX(-1, compIdx, mrl);
611            ppy = wp.getPrecinctPartition().getPPY(-1, compIdx, mrl);
612            prog = (Progression[])(wp.getProgressionType().getCompDef(compIdx));
613        }
614        else {
615            mrl = ((Integer)wp.getDecompositionLevel().getTileCompVal(tileIdx,compIdx)).
616                intValue();
617            // Get precinct size for specified component/tile
618            ppx = wp.getPrecinctPartition().getPPX(tileIdx, compIdx, mrl);
619            ppy = wp.getPrecinctPartition().getPPY(tileIdx, compIdx, mrl);
620            prog = (Progression[])(wp.getProgressionType().getTileCompVal(tileIdx,compIdx));
621        }
622
623        if ( ppx != Markers.PRECINCT_PARTITION_DEF_SIZE ||
624             ppy != Markers.PRECINCT_PARTITION_DEF_SIZE ) {
625            precinctPartitionUsed = true;
626        }
627        else {
628            precinctPartitionUsed = false;
629        }
630        if ( precinctPartitionUsed ) {
631            // If precinct partition is used we add one byte per resolution
632            // level  i.e. mrl+1 (+1 for resolution 0).
633            a = mrl+1;
634        }
635
636        // COC marker
637        hbuf.writeShort(COC);
638
639        // Lcoc (marker segment length (in bytes))
640        // Basic: Lcoc(2 bytes)+Scoc(1)+ Ccoc(1 or 2)+SPcod(5+a)
641        int markSegLen = 8 + ((nComp < 257) ? 1 : 2)+a;
642
643        // Rounded to the nearest even value greater or equals
644        hbuf.writeShort(markSegLen);
645
646        // Ccoc
647        if(nComp < 257) {
648            hbuf.write(compIdx);
649        }
650        else {
651            hbuf.writeShort(compIdx);
652        }
653
654        // Scod (coding style parameter)
655        tmp=0;
656        if ( precinctPartitionUsed ) {
657            tmp=SCOX_PRECINCT_PARTITION;
658        }
659        hbuf.write(tmp);
660
661
662        // SPcoc
663
664        // Number of decomposition levels
665        hbuf.write(mrl);
666
667        // Code-block width and height
668        if ( mh ) {
669            // main header, get component default values
670            tmp = wp.getCodeBlockSize().
671                getCBlkWidth(ModuleSpec.SPEC_COMP_DEF, -1, compIdx);
672            hbuf.write(MathUtil.log2(tmp)-2);
673            tmp = wp.getCodeBlockSize().
674                getCBlkHeight(ModuleSpec.SPEC_COMP_DEF, -1, compIdx);
675            hbuf.write(MathUtil.log2(tmp)-2);
676        }
677        else {
678            // tile header, get tile component values
679            tmp = wp.getCodeBlockSize().
680                getCBlkWidth(ModuleSpec.SPEC_TILE_COMP, tileIdx, compIdx);
681            hbuf.write(MathUtil.log2(tmp)-2);
682            tmp = wp.getCodeBlockSize().
683                getCBlkHeight(ModuleSpec.SPEC_TILE_COMP, tileIdx, compIdx);
684            hbuf.write(MathUtil.log2(tmp)-2);
685        }
686
687        // Entropy coding mode options
688        tmp = 0;
689        if(mh){ // Main header
690            // Lazy coding mode ?
691            if( ((String)wp.getBypass().getCompDef(compIdx)).equals("true")) {
692                tmp |= OPT_BYPASS;
693            }
694            // MQ reset after each coding pass ?
695            if( ((String)wp.getResetMQ().getCompDef(compIdx)).
696                equalsIgnoreCase("true")) {
697                tmp |= OPT_RESET_MQ;
698            }
699            // MQ termination after each arithmetically coded coding pass ?
700            if(  ((String)wp.getTerminateOnByte().getCompDef(compIdx)).equals("true") ) {
701                tmp |= OPT_TERM_PASS;
702            }
703            // Vertically stripe-causal context mode ?
704            if(  ((String)wp.getCausalCXInfo().getCompDef(compIdx)).equals("true") ) {
705                tmp |= OPT_VERT_STR_CAUSAL;
706            }
707            // Predictable termination ?
708            if( ((String)wp.getMethodForMQTermination().getCompDef(compIdx)).equals("predict")){
709                tmp |= OPT_PRED_TERM;
710            }
711            // Error resilience segmentation symbol insertion ?
712            if(  ((String)wp.getCodeSegSymbol().getCompDef(compIdx)).equals("true")) {
713                tmp |= OPT_SEG_SYMBOLS;
714            }
715        }
716        else{ // Tile Header
717            if( ((String)wp.getBypass().getTileCompVal(tileIdx,compIdx)).
718                equals("true")) {
719                tmp |= OPT_BYPASS;
720            }
721            // MQ reset after each coding pass ?
722            if( ((String)wp.getResetMQ().getTileCompVal(tileIdx,compIdx)).
723                equals("true")) {
724                tmp |= OPT_RESET_MQ;
725            }
726            // MQ termination after each arithmetically coded coding pass ?
727            if(  ((String)wp.getTerminateOnByte().getTileCompVal(tileIdx,compIdx)).
728                 equals("true") ) {
729                tmp |= OPT_TERM_PASS;
730            }
731            // Vertically stripe-causal context mode ?
732            if(  ((String)wp.getCausalCXInfo().getTileCompVal(tileIdx,compIdx)).
733                 equals("true") ) {
734                tmp |= OPT_VERT_STR_CAUSAL;
735            }
736            // Predictable termination ?
737            if( ((String)wp.getMethodForMQTermination().getTileCompVal(tileIdx,compIdx)).
738                equals("predict")){
739                tmp |= OPT_PRED_TERM;
740            }
741            // Error resilience segmentation symbol insertion ?
742            if(  ((String)wp.getCodeSegSymbol().getTileCompVal(tileIdx,compIdx)).
743                 equals("true")) {
744                tmp |= OPT_SEG_SYMBOLS;
745            }
746        }
747        hbuf.write(tmp);
748
749        // Wavelet transform
750        // Wavelet Filter
751        if(mh){
752            filt=((AnWTFilter[][])wp.getFilters().getCompDef(compIdx));
753            hbuf.write(filt[0][0].getFilterType());
754        }else{
755            filt=((AnWTFilter[][])wp.getFilters().getTileCompVal(tileIdx,compIdx));
756            hbuf.write(filt[0][0].getFilterType());
757        }
758
759        // Precinct partition
760        if ( precinctPartitionUsed ) {
761            // Write the precinct size for each resolution level + 1
762            // (resolution 0) if precinct partition is used.
763            Vector v[] = null;
764            if ( mh ) {
765                v = (Vector[])wp.getPrecinctPartition().getCompDef(compIdx);
766            }
767            else {
768                v = (Vector[])wp.getPrecinctPartition().getTileCompVal(tileIdx, compIdx);
769            }
770            for (int r=mrl ; r>=0 ; r--) {
771                if ( r>=v[1].size() ) {
772                    tmp = ((Integer)v[1].elementAt(v[1].size()-1)).
773                        intValue();
774                }
775                else {
776                    tmp = ((Integer)v[1].elementAt(r)).intValue();
777                }
778                int yExp = (MathUtil.log2(tmp)<< 4) & 0x00F0;
779
780                if ( r>=v[0].size() ) {
781                    tmp = ((Integer)v[0].elementAt(v[0].size()-1)).
782                        intValue();
783                }
784                else {
785                    tmp = ((Integer)v[0].elementAt(r)).intValue();
786                }
787                int xExp = MathUtil.log2(tmp) & 0x000F;
788                hbuf.write(yExp|xExp);
789            }
790        }
791
792    }
793
794    /**
795     * Writes QCD marker segment in main header. QCD is a functional marker
796     * segment countaining the quantization default used for compressing all
797     * the components in an image. The values can be overriden for an
798     * individual component by a QCC marker in either the main or the tile
799     * header.
800     * */
801    protected void writeMainQCD() throws IOException{
802        float step;
803
804        String qType = (String)wp.getQuantizationType().getDefault();
805        float baseStep = ((Float)wp.getQuantizationStep().getDefault()).floatValue();
806        int gb = ((Integer)wp.getGuardBits().getDefault()).intValue();
807
808        boolean isDerived   = qType.equals("derived");
809        boolean isReversible = qType.equals("reversible");
810        int mrl = ((Integer)wp.getDecompositionLevel().getDefault()).intValue();
811
812        int nt = dwt.getNumTiles();
813        int nc = dwt.getNumComps();
814        int tmpI;
815        int[] tcIdx = new int[2];
816        String tmpStr;
817        boolean notFound = true;
818        for(int t=0; t<nt && notFound; t++) {
819            for(int c=0; c<nc && notFound; c++) {
820                tmpI = ((Integer)wp.getDecompositionLevel().getTileCompVal(t,c)).intValue();
821                tmpStr = (String)wp.getQuantizationType().getTileCompVal(t,c);
822                if(tmpI==mrl && tmpStr.equals(qType)) {
823                    tcIdx[0] = t; tcIdx[1] = c;
824                    notFound = false;
825                }
826            }
827        }
828        if(notFound) {
829            throw new Error("Default representative for quantization type "+
830                            " and number of decomposition levels not found "+
831                            " in main QCD marker segment. "+
832                            "You have found a JJ2000 bug.");
833        }
834        SubbandAn sb,csb,
835            sbRoot = dwt.getAnSubbandTree(tcIdx[0],tcIdx[1]);
836        defimgn = dwt.getNomRangeBits(tcIdx[1]);
837
838        int nqcd; // Number of quantization step-size to transmit
839
840        // Get the quantization style
841        int qstyle = (isReversible) ? SQCX_NO_QUANTIZATION :
842            ((isDerived) ? SQCX_SCALAR_DERIVED : SQCX_SCALAR_EXPOUNDED);
843
844        // QCD marker
845        hbuf.writeShort(QCD);
846
847
848        // Compute the number of steps to send
849        switch (qstyle) {
850        case SQCX_SCALAR_DERIVED:
851            nqcd = 1; // Just the LL value
852            break;
853        case SQCX_NO_QUANTIZATION:
854        case SQCX_SCALAR_EXPOUNDED:
855            // One value per subband
856            nqcd=0;
857
858            sb=sbRoot;
859
860            // Get the subband at first resolution level
861            sb = (SubbandAn) sb.getSubbandByIdx(0,0);
862
863            // Count total number of subbands
864            for (int j=0; j<=mrl; j++) {
865                csb = sb;
866                while (csb != null) {
867                    nqcd++;
868                    csb = (SubbandAn) csb.nextSubband();
869                }
870                // Go up one resolution level
871                sb = (SubbandAn) sb.getNextResLevel();
872            }
873            break;
874        default:
875            throw new Error("Internal JJ2000 error");
876        }
877
878        // Lqcd (marker segment length (in bytes))
879        // Lqcd(2 bytes)+Sqcd(1)+ SPqcd (2*Nqcd)
880        int markSegLen = 3 + ((isReversible) ? nqcd : 2*nqcd);
881
882        // Rounded to the nearest even value greater or equals
883        hbuf.writeShort(markSegLen);
884
885        // Sqcd
886        hbuf.write(qstyle+(gb<<SQCX_GB_SHIFT));
887
888        // SPqcd
889        switch (qstyle) {
890        case SQCX_NO_QUANTIZATION:
891            sb = sbRoot;
892            sb = (SubbandAn)sb.getSubbandByIdx(0,0);
893
894            // Output one exponent per subband
895            for (int j=0; j<=mrl; j++) {
896                csb = sb;
897                while(csb != null) {
898                    int tmp = (defimgn + csb.anGainExp);
899                    hbuf.write(tmp<<SQCX_EXP_SHIFT);
900
901                    csb = (SubbandAn)csb.nextSubband();
902                    // Go up one resolution level
903                }
904                sb = (SubbandAn)sb.getNextResLevel();
905            }
906            break;
907        case SQCX_SCALAR_DERIVED:
908            sb = sbRoot;
909            sb = (SubbandAn)sb.getSubbandByIdx(0,0);
910
911            // Calculate subband step (normalized to unit
912            // dynamic range)
913            step = baseStep/(1<<sb.level);
914
915            // Write exponent-mantissa, 16 bits
916            hbuf.writeShort(StdQuantizer.
917                            convertToExpMantissa(step));
918            break;
919        case SQCX_SCALAR_EXPOUNDED:
920            sb = sbRoot;
921            sb = (SubbandAn)sb.getSubbandByIdx(0,0);
922
923            // Output one step per subband
924            for (int j=0; j<=mrl; j++) {
925                csb = sb;
926                while(csb != null) {
927                    // Calculate subband step (normalized to unit
928                    // dynamic range)
929                    step = baseStep/(csb.l2Norm*(1<<csb.anGainExp));
930
931                    // Write exponent-mantissa, 16 bits
932                    hbuf.writeShort(StdQuantizer.
933                                    convertToExpMantissa(step));
934
935                    csb = (SubbandAn)csb.nextSubband();
936                }
937                // Go up one resolution level
938                sb = (SubbandAn)sb.getNextResLevel();
939            }
940            break;
941        default:
942            throw new Error("Internal JJ2000 error");
943        }
944    }
945
946    /**
947     * Writes QCC marker segment in main header. It is a functional
948     * marker segment countaining the quantization used for
949     * compressing the specified component in an image. The values
950     * override for the specified component what was defined by a QCC
951     * marker in either the main or the tile header.
952     *
953     * @param compIdx Index of the component which needs QCC marker
954     * segment.
955     * */
956    protected void writeMainQCC(int compIdx)
957        throws IOException{
958
959        int mrl;
960        int qstyle;
961        int tIdx = 0;
962        float step;
963
964        SubbandAn sb,sb2;
965        SubbandAn sbRoot;
966
967        int imgnr = dwt.getNomRangeBits(compIdx);
968        String qType = (String)wp.getQuantizationType().getCompDef(compIdx);
969        float baseStep = ((Float)wp.getQuantizationStep().getCompDef(compIdx)).floatValue();
970        int gb = ((Integer)wp.getGuardBits().getCompDef(compIdx)).intValue();
971
972        boolean isReversible = qType.equals("reversible");
973        boolean isDerived   = qType.equals("derived");
974
975        mrl = ((Integer)wp.getDecompositionLevel().getCompDef(compIdx)).intValue();
976
977        int nt = dwt.getNumTiles();
978        int nc = dwt.getNumComps();
979        int tmpI;
980        String tmpStr;
981        boolean notFound = true;
982        for(int t=0; t<nt && notFound; t++) {
983            for(int c=0; c<nc && notFound; c++) {
984                tmpI = ((Integer)wp.getDecompositionLevel().getTileCompVal(t,c)).intValue();
985                tmpStr = (String)wp.getQuantizationType().getTileCompVal(t,c);
986                if(tmpI==mrl && tmpStr.equals(qType)) {
987                    tIdx = t;
988                    notFound = false;
989                }
990            }
991        }
992        if(notFound) {
993            throw new Error("Default representative for quantization type "+
994                            " and number of decomposition levels not found "+
995                            " in main QCC (c="+compIdx+") marker segment. "+
996                            "You have found a JJ2000 bug.");
997        }
998        sbRoot = dwt.getAnSubbandTree(tIdx,compIdx);
999
1000        int nqcc; // Number of quantization step-size to transmit
1001
1002        // Get the quantization style
1003        if(isReversible) {
1004            qstyle = SQCX_NO_QUANTIZATION;
1005        }
1006        else if (isDerived) {
1007            qstyle = SQCX_SCALAR_DERIVED;
1008        }
1009        else {
1010            qstyle = SQCX_SCALAR_EXPOUNDED;
1011        }
1012
1013        // QCC marker
1014        hbuf.writeShort(QCC);
1015
1016        // Compute the number of steps to send
1017        switch (qstyle) {
1018        case SQCX_SCALAR_DERIVED:
1019            nqcc = 1; // Just the LL value
1020            break;
1021        case SQCX_NO_QUANTIZATION:
1022        case SQCX_SCALAR_EXPOUNDED:
1023            // One value per subband
1024            nqcc = 0;
1025
1026            sb = sbRoot;
1027            mrl = sb.resLvl;
1028
1029            // Get the subband at first resolution level
1030            sb = (SubbandAn)sb.getSubbandByIdx(0,0);
1031
1032            // Find root element for LL subband
1033            while (sb.resLvl != 0) {
1034                sb = sb.subb_LL;
1035            }
1036
1037            // Count total number of subbands
1038            for (int j=0; j<=mrl; j++) {
1039                sb2 = sb;
1040                while (sb2 != null) {
1041                    nqcc++;
1042                    sb2 = (SubbandAn) sb2.nextSubband();
1043                }
1044                // Go up one resolution level
1045                sb = (SubbandAn) sb.getNextResLevel();
1046            }
1047            break;
1048        default:
1049            throw new Error("Internal JJ2000 error");
1050        }
1051
1052        // Lqcc (marker segment length (in bytes))
1053        // Lqcc(2 bytes)+Cqcc(1 or 2)+Sqcc(1)+ SPqcc (2*Nqcc)
1054        int markSegLen = 3 + ((nComp < 257) ? 1 : 2) +
1055            ((isReversible) ? nqcc : 2*nqcc);
1056        hbuf.writeShort(markSegLen);
1057
1058        // Cqcc
1059        if (nComp < 257) {
1060            hbuf.write(compIdx);
1061        }
1062        else {
1063            hbuf.writeShort(compIdx);
1064        }
1065
1066        // Sqcc (quantization style)
1067        hbuf.write(qstyle+(gb<<SQCX_GB_SHIFT));
1068
1069        // SPqcc
1070        switch (qstyle) {
1071        case SQCX_NO_QUANTIZATION:
1072            // Get resolution level 0 subband
1073            sb = sbRoot;
1074            sb = (SubbandAn) sb.getSubbandByIdx(0,0);
1075
1076            // Output one exponent per subband
1077            for (int j=0; j<=mrl; j++) {
1078                sb2 = sb;
1079                while (sb2 != null) {
1080                    int tmp = (imgnr+sb2.anGainExp);
1081                    hbuf.write(tmp<<SQCX_EXP_SHIFT);
1082
1083                    sb2 = (SubbandAn)sb2.nextSubband();
1084                }
1085                // Go up one resolution level
1086                sb = (SubbandAn)sb.getNextResLevel();
1087            }
1088            break;
1089        case SQCX_SCALAR_DERIVED:
1090            // Get resolution level 0 subband
1091            sb = sbRoot;
1092            sb = (SubbandAn) sb.getSubbandByIdx(0,0);
1093
1094            // Calculate subband step (normalized to unit
1095            // dynamic range)
1096            step = baseStep/(1<<sb.level);
1097
1098            // Write exponent-mantissa, 16 bits
1099            hbuf.writeShort(StdQuantizer.
1100                            convertToExpMantissa(step));
1101            break;
1102        case SQCX_SCALAR_EXPOUNDED:
1103            // Get resolution level 0 subband
1104            sb = sbRoot;
1105            mrl = sb.resLvl;
1106
1107            sb = (SubbandAn) sb.getSubbandByIdx(0,0);
1108
1109            for (int j=0; j<=mrl; j++) {
1110                sb2 = sb;
1111                while (sb2 != null) {
1112                    // Calculate subband step (normalized to unit
1113                    // dynamic range)
1114                    step = baseStep/(sb2.l2Norm*(1<<sb2.anGainExp));
1115
1116                    // Write exponent-mantissa, 16 bits
1117                    hbuf.writeShort(StdQuantizer.
1118                                    convertToExpMantissa(step));
1119                    sb2 = (SubbandAn)sb2.nextSubband();
1120                }
1121                // Go up one resolution level
1122                sb = (SubbandAn) sb.getNextResLevel();
1123            }
1124            break;
1125        default:
1126            throw new Error("Internal JJ2000 error");
1127        }
1128    }
1129
1130    /**
1131     * Writes QCD marker segment in tile header. QCD is a functional
1132     * marker segment countaining the quantization default used for
1133     * compressing all the components in an image. The values can be
1134     * overriden for an individual component by a QCC marker in either
1135     * the main or the tile header.
1136     *
1137     * @param tIdx Tile index
1138     * */
1139    protected void writeTileQCD(int tIdx) throws IOException{
1140        int mrl;
1141        int qstyle;
1142
1143        float step;
1144        SubbandAn sb,csb,sbRoot;
1145
1146        String qType = (String)wp.getQuantizationType().getTileDef(tIdx);
1147        float baseStep = ((Float)wp.getQuantizationStep().getTileDef(tIdx)).floatValue();
1148        mrl = ((Integer)wp.getDecompositionLevel().getTileDef(tIdx)).intValue();
1149
1150        int nc = dwt.getNumComps();
1151        int tmpI;
1152        String tmpStr;
1153        boolean notFound = true;
1154        int compIdx = 0;
1155        for(int c=0; c<nc && notFound; c++) {
1156            tmpI = ((Integer)wp.getDecompositionLevel().getTileCompVal(tIdx,c)).intValue();
1157            tmpStr = (String)wp.getQuantizationStep().getTileCompVal(tIdx,c);
1158            if(tmpI==mrl && tmpStr.equals(qType)) {
1159                compIdx = c;
1160                notFound = false;
1161            }
1162        }
1163        if(notFound) {
1164            throw new Error("Default representative for quantization type "+
1165                            " and number of decomposition levels not found "+
1166                            " in tile QCD (t="+tIdx+") marker segment. "+
1167                            "You have found a JJ2000 bug.");
1168        }
1169
1170        sbRoot = dwt.getAnSubbandTree(tIdx,compIdx);
1171        deftilenr = dwt.getNomRangeBits(compIdx);
1172        int gb = ((Integer)wp.getGuardBits().getTileDef(tIdx)).intValue();
1173
1174        boolean isDerived   = qType.equals("derived");
1175        boolean isReversible = qType.equals("reversible");
1176
1177        int nqcd; // Number of quantization step-size to transmit
1178
1179        // Get the quantization style
1180        qstyle = (isReversible) ? SQCX_NO_QUANTIZATION :
1181            ((isDerived) ? SQCX_SCALAR_DERIVED : SQCX_SCALAR_EXPOUNDED);
1182
1183        // QCD marker
1184        hbuf.writeShort(QCD);
1185
1186        // Compute the number of steps to send
1187        switch (qstyle) {
1188        case SQCX_SCALAR_DERIVED:
1189            nqcd = 1; // Just the LL value
1190            break;
1191        case SQCX_NO_QUANTIZATION:
1192        case SQCX_SCALAR_EXPOUNDED:
1193            // One value per subband
1194            nqcd=0;
1195
1196            sb=sbRoot;
1197
1198            // Get the subband at first resolution level
1199            sb = (SubbandAn) sb.getSubbandByIdx(0,0);
1200
1201            // Count total number of subbands
1202            for (int j=0; j<=mrl; j++) {
1203                csb = sb;
1204                while (csb != null) {
1205                    nqcd++;
1206                    csb = (SubbandAn) csb.nextSubband();
1207                }
1208                // Go up one resolution level
1209                sb = (SubbandAn) sb.getNextResLevel();
1210            }
1211            break;
1212        default:
1213            throw new Error("Internal JJ2000 error");
1214        }
1215
1216        // Lqcd (marker segment length (in bytes))
1217        // Lqcd(2 bytes)+Sqcd(1)+ SPqcd (2*Nqcd)
1218        int markSegLen = 3 + ((isReversible) ? nqcd : 2*nqcd);
1219
1220        // Rounded to the nearest even value greater or equals
1221        hbuf.writeShort(markSegLen);
1222
1223        // Sqcd
1224        hbuf.write(qstyle+(gb<<SQCX_GB_SHIFT));
1225
1226        // SPqcd
1227        switch (qstyle) {
1228        case SQCX_NO_QUANTIZATION:
1229            sb = sbRoot;
1230            sb = (SubbandAn)sb.getSubbandByIdx(0,0);
1231
1232            // Output one exponent per subband
1233            for (int j=0; j<=mrl; j++) {
1234                csb = sb;
1235                while(csb != null) {
1236                    int tmp = (deftilenr+csb.anGainExp);
1237                    hbuf.write(tmp<<SQCX_EXP_SHIFT);
1238
1239                    csb = (SubbandAn)csb.nextSubband();
1240                    // Go up one resolution level
1241                }
1242                sb = (SubbandAn)sb.getNextResLevel();
1243            }
1244            break;
1245        case SQCX_SCALAR_DERIVED:
1246            sb = sbRoot;
1247            sb = (SubbandAn)sb.getSubbandByIdx(0,0);
1248
1249            // Calculate subband step (normalized to unit
1250            // dynamic range)
1251            step = baseStep/(1<<sb.level);
1252
1253            // Write exponent-mantissa, 16 bits
1254            hbuf.writeShort(StdQuantizer.
1255                            convertToExpMantissa(step));
1256            break;
1257        case SQCX_SCALAR_EXPOUNDED:
1258            sb = sbRoot;
1259            sb = (SubbandAn)sb.getSubbandByIdx(0,0);
1260
1261            // Output one step per subband
1262            for (int j=0; j<=mrl; j++) {
1263                csb = sb;
1264                while(csb != null) {
1265                    // Calculate subband step (normalized to unit
1266                    // dynamic range)
1267                    step = baseStep/(csb.l2Norm*(1<<csb.anGainExp));
1268
1269                    // Write exponent-mantissa, 16 bits
1270                    hbuf.writeShort(StdQuantizer.
1271                                    convertToExpMantissa(step));
1272
1273                    csb = (SubbandAn)csb.nextSubband();
1274                }
1275                // Go up one resolution level
1276                sb = (SubbandAn)sb.getNextResLevel();
1277            }
1278            break;
1279        default:
1280            throw new Error("Internal JJ2000 error");
1281        }
1282    }
1283
1284    /**
1285     * Writes QCC marker segment in tile header. It is a functional
1286     * marker segment countaining the quantization used for
1287     * compressing the specified component in an image. The values
1288     * override for the specified component what was defined by a QCC
1289     * marker in either the main or the tile header.
1290     *
1291     * @param t Tile index
1292     *
1293     * @param compIdx Index of the component which needs QCC marker
1294     * segment.
1295     * */
1296    protected void writeTileQCC(int t,int compIdx)
1297        throws IOException{
1298
1299        int mrl;
1300        int qstyle;
1301        float step;
1302
1303        SubbandAn sb,sb2;
1304        int nqcc; // Number of quantization step-size to transmit
1305
1306        SubbandAn sbRoot = dwt.getAnSubbandTree(t,compIdx);
1307        int imgnr = dwt.getNomRangeBits(compIdx);
1308        String qType = (String)wp.getQuantizationType().getTileCompVal(t,compIdx);
1309        float baseStep = ((Float)wp.getQuantizationStep().getTileCompVal(t,compIdx)).
1310            floatValue();
1311        int gb = ((Integer)wp.getGuardBits().getTileCompVal(t,compIdx)).intValue();
1312
1313        boolean isReversible = qType.equals("reversible");
1314        boolean isDerived   = qType.equals("derived");
1315
1316        mrl = ((Integer)wp.getDecompositionLevel().getTileCompVal(t,compIdx)).intValue();
1317
1318        // Get the quantization style
1319        if(isReversible) {
1320            qstyle = SQCX_NO_QUANTIZATION;
1321        }
1322        else if (isDerived) {
1323            qstyle = SQCX_SCALAR_DERIVED;
1324        }
1325        else {
1326            qstyle = SQCX_SCALAR_EXPOUNDED;
1327        }
1328
1329        // QCC marker
1330        hbuf.writeShort(QCC);
1331
1332        // Compute the number of steps to send
1333        switch (qstyle) {
1334        case SQCX_SCALAR_DERIVED:
1335            nqcc = 1; // Just the LL value
1336            break;
1337        case SQCX_NO_QUANTIZATION:
1338        case SQCX_SCALAR_EXPOUNDED:
1339            // One value per subband
1340            nqcc = 0;
1341
1342            sb = sbRoot;
1343            mrl = sb.resLvl;
1344
1345            // Get the subband at first resolution level
1346            sb = (SubbandAn)sb.getSubbandByIdx(0,0);
1347
1348            // Find root element for LL subband
1349            while (sb.resLvl != 0) {
1350                sb = sb.subb_LL;
1351            }
1352
1353            // Count total number of subbands
1354            for (int j=0; j<=mrl; j++) {
1355                sb2 = sb;
1356                while (sb2 != null) {
1357                    nqcc++;
1358                    sb2 = (SubbandAn) sb2.nextSubband();
1359                }
1360                // Go up one resolution level
1361                sb = (SubbandAn) sb.getNextResLevel();
1362            }
1363            break;
1364        default:
1365            throw new Error("Internal JJ2000 error");
1366        }
1367
1368        // Lqcc (marker segment length (in bytes))
1369        // Lqcc(2 bytes)+Cqcc(1 or 2)+Sqcc(1)+ SPqcc (2*Nqcc)
1370        int markSegLen = 3 + ((nComp < 257) ? 1 : 2) +
1371            ((isReversible) ? nqcc : 2*nqcc);
1372        hbuf.writeShort(markSegLen);
1373
1374        // Cqcc
1375        if (nComp < 257) {
1376            hbuf.write(compIdx);
1377        }
1378        else {
1379            hbuf.writeShort(compIdx);
1380        }
1381
1382        // Sqcc (quantization style)
1383        hbuf.write(qstyle+(gb<<SQCX_GB_SHIFT));
1384
1385        // SPqcc
1386        switch (qstyle) {
1387        case SQCX_NO_QUANTIZATION:
1388            // Get resolution level 0 subband
1389            sb = sbRoot;
1390            sb = (SubbandAn) sb.getSubbandByIdx(0,0);
1391
1392            // Output one exponent per subband
1393            for (int j=0; j<=mrl; j++) {
1394                sb2 = sb;
1395                while (sb2 != null) {
1396                    int tmp = (imgnr+sb2.anGainExp);
1397                    hbuf.write(tmp<<SQCX_EXP_SHIFT);
1398
1399                    sb2 = (SubbandAn)sb2.nextSubband();
1400                }
1401                // Go up one resolution level
1402                sb = (SubbandAn)sb.getNextResLevel();
1403            }
1404            break;
1405        case SQCX_SCALAR_DERIVED:
1406            // Get resolution level 0 subband
1407            sb = sbRoot;
1408            sb = (SubbandAn) sb.getSubbandByIdx(0,0);
1409
1410            // Calculate subband step (normalized to unit
1411            // dynamic range)
1412            step = baseStep/(1<<sb.level);
1413
1414            // Write exponent-mantissa, 16 bits
1415            hbuf.writeShort(StdQuantizer.
1416                            convertToExpMantissa(step));
1417            break;
1418        case SQCX_SCALAR_EXPOUNDED:
1419            // Get resolution level 0 subband
1420            sb = sbRoot;
1421            mrl = sb.resLvl;
1422
1423            sb = (SubbandAn) sb.getSubbandByIdx(0,0);
1424
1425            for (int j=0; j<=mrl; j++) {
1426                sb2 = sb;
1427                while (sb2 != null) {
1428                    // Calculate subband step (normalized to unit
1429                    // dynamic range)
1430                    step = baseStep/(sb2.l2Norm*(1<<sb2.anGainExp));
1431
1432                    // Write exponent-mantissa, 16 bits
1433                    hbuf.writeShort(StdQuantizer.
1434                                    convertToExpMantissa(step));
1435                    sb2 = (SubbandAn)sb2.nextSubband();
1436                }
1437                // Go up one resolution level
1438                sb = (SubbandAn) sb.getNextResLevel();
1439            }
1440            break;
1441        default:
1442            throw new Error("Internal JJ2000 error");
1443        }
1444    }
1445
1446    /**
1447     * Writes POC marker segment. POC is a functional marker segment
1448     * containing the bounds and progression order for any progression order
1449     * other than default in the codestream.
1450     *
1451     * @param mh Flag indicating whether the main header is to be written
1452     *
1453     * @param tileIdx Tile index
1454     * */
1455    protected void writePOC(boolean mh, int tileIdx) throws IOException {
1456        int markSegLen=0;        // Segment marker length
1457        int lenCompField;        // Holds the size of any component field as
1458                                 // this size depends on the number of
1459                                 //components
1460        Progression[] prog = null; // Holds the progression(s)
1461        int npoc;                // Number of progression order changes
1462
1463        // Get the progression order changes, their number and checks
1464        // if it is ok
1465        if(mh){
1466            prog = (Progression[])(wp.getProgressionType().getDefault());
1467        }
1468        else {
1469            prog = (Progression[])(wp.getProgressionType().getTileDef(tileIdx));
1470        }
1471
1472        // Calculate the length of a component field (depends on the number of
1473        // components)
1474        lenCompField = (nComp<257 ? 1 : 2);
1475
1476        // POC marker
1477        hbuf.writeShort(POC);
1478
1479        // Lpoc (marker segment length (in bytes))
1480        // Basic: Lpoc(2 bytes) + npoc * [ RSpoc(1) + CSpoc(1 or 2) + LYEpoc(2)
1481        // + REpoc(1) + CEpoc(1 or 2) + Ppoc(1) ]
1482        npoc = prog.length;
1483        markSegLen = 2 + npoc * (1+lenCompField+2+1+lenCompField+1);
1484        hbuf.writeShort(markSegLen);
1485
1486        // Write each progression order change
1487        for (int i=0 ; i<npoc ; i++){
1488            // RSpoc(i)
1489            hbuf.write(prog[i].rs);
1490            // CSpoc(i)
1491            if ( lenCompField==2 ) {
1492                hbuf.writeShort(prog[i].cs);
1493            }
1494            else {
1495                hbuf.write(prog[i].cs);
1496            }
1497            // LYEpoc(i)
1498            hbuf.writeShort(prog[i].lye);
1499            // REpoc(i)
1500            hbuf.write(prog[i].re);
1501            // CEpoc(i)
1502            if ( lenCompField==2 ) {
1503                hbuf.writeShort(prog[i].ce);
1504            }
1505            else {
1506                hbuf.write(prog[i].ce);
1507            }
1508            // Ppoc(i)
1509            hbuf.write(prog[i].type);
1510        }
1511    }
1512
1513
1514    /**
1515     * Write main header. JJ2000 main header corresponds to the following
1516     * sequence of marker
1517     * segments:<ol><li>SOC</li><li>SIZ</li><li>COD</li><li>COC (if
1518     * needed)</li><li>QCD</li><li>QCC (if needed)</li><li>POC (if
1519     * needed)</li></ol>
1520     * */
1521    public void encodeMainHeader() throws IOException {
1522        int i;
1523
1524
1525        // +---------------------------------+
1526        // |    SOC marker segment           |
1527        // +---------------------------------+
1528        writeSOC();
1529
1530        // +---------------------------------+
1531        // |    Image and tile SIZe (SIZ)    |
1532        // +---------------------------------+
1533        writeSIZ();
1534
1535        // +-------------------------------+
1536        // |   COding style Default (COD)  |
1537        // +-------------------------------+
1538        boolean isEresUsed = ((String)wp.getTerminateOnByte().getDefault()).
1539            equals("predict");
1540        writeCOD(true,0);
1541
1542        // +---------------------------------+
1543        // |   COding style Component (COC)  |
1544        // +---------------------------------+
1545        for (i= 0; i<nComp; i++) {
1546            boolean isEresUsedinComp = ((String)wp.getTerminateOnByte().getCompDef(i)).
1547                equals("predict");
1548            if(wp.getFilters().isCompSpecified(i) ||
1549               wp.getDecompositionLevel().isCompSpecified(i) ||
1550               wp.getBypass().isCompSpecified(i) ||
1551               wp.getResetMQ().isCompSpecified(i) ||
1552               wp.getMethodForMQTermination().isCompSpecified(i) ||
1553               wp.getCodeSegSymbol().isCompSpecified(i) ||
1554               wp.getCausalCXInfo().isCompSpecified(i) ||
1555               wp.getPrecinctPartition().isCompSpecified(i) ||
1556               wp.getCodeBlockSize().isCompSpecified(i) ||
1557               (isEresUsed != isEresUsedinComp ) )
1558                // Some component non-default stuff => need COC
1559                writeCOC(true,0,i);
1560        }
1561
1562        // +-------------------------------+
1563        // |   Quantization Default (QCD)  |
1564        // +-------------------------------+
1565        writeMainQCD();
1566
1567        // +-------------------------------+
1568        // | Quantization Component (QCC)  |
1569        // +-------------------------------+
1570        // Write needed QCC markers
1571        for(i=0; i<nComp; i++){
1572            if(dwt.getNomRangeBits(i)!= defimgn ||
1573               wp.getQuantizationType().isCompSpecified(i) ||
1574               wp.getQuantizationStep().isCompSpecified(i) ||
1575               wp.getDecompositionLevel().isCompSpecified(i) ||
1576               wp.getGuardBits().isCompSpecified(i)){
1577                writeMainQCC(i);
1578            }
1579        }
1580
1581        // +--------------------------+
1582        // |    POC maker segment     |
1583        // +--------------------------+
1584        Progression[] prog = (Progression[])(wp.getProgressionType().getDefault());
1585        if(prog.length>1)
1586            writePOC(true, 0);
1587
1588        // +--------------------------+
1589        // |      Comment (COM)       |
1590        // +--------------------------+
1591        writeCOM();
1592    }
1593
1594    /**
1595     * Write a COM marker segment adding some comments to the codestream.
1596     *
1597     * <p> This marker is currently written in main header and indicates the
1598     * JJ2000 encoder's version that has created the codestream.
1599     * */
1600    private void writeCOM() throws IOException {
1601        // JJ2000 COM marker segment
1602        if(enJJ2KMarkSeg) {
1603            String str = "Created by: JJ2000 version "+JJ2KInfo.version;
1604            int markSegLen; // the marker segment length
1605
1606            // COM marker
1607            hbuf.writeShort(COM);
1608
1609            // Calculate length: Lcom(2) + Rcom (2) + string's length;
1610            markSegLen = 2 + 2 + str.length();
1611            hbuf.writeShort(markSegLen);
1612
1613            // Rcom 
1614            hbuf.writeShort(1); // General use (IS 8859-15:1999(Latin) values)
1615
1616            byte[] chars = str.getBytes();
1617            for(int i=0; i<chars.length; i++) {
1618                hbuf.writeByte(chars[i]);
1619            }
1620        }
1621        // other COM marker segments
1622        if(otherCOMMarkSeg!=null) {
1623            StringTokenizer stk = new StringTokenizer(otherCOMMarkSeg,"#");
1624            while(stk.hasMoreTokens()) {
1625                String str = stk.nextToken();
1626                int markSegLen; // the marker segment length
1627
1628                // COM marker
1629                hbuf.writeShort(COM);
1630
1631                // Calculate length: Lcom(2) + Rcom (2) + string's length;
1632                markSegLen = 2 + 2 + str.length();
1633                hbuf.writeShort(markSegLen);
1634
1635                // Rcom 
1636                hbuf.writeShort(1); // General use (IS 8859-15:1999(Latin)
1637                // values)
1638
1639                byte[] chars = str.getBytes();
1640                for(int i=0; i<chars.length; i++) {
1641                    hbuf.writeByte(chars[i]);
1642                }
1643            }
1644        }
1645    }
1646
1647    /**
1648     * Writes the RGN marker segment in the tile header. It describes the
1649     * scaling value in each tile component
1650     *
1651     * <P>May be used in tile or main header. If used in main header, it
1652     * refers to a ROI of the whole image, regardless of tiling. When used in
1653     * tile header, only the particular tile is affected.
1654     *
1655     * @param tIdx The tile index
1656     *
1657     * @exception IOException If an I/O error occurs while reading from the
1658     * encoder header stream
1659     * */
1660    private void writeRGN(int tIdx) throws IOException {
1661        int i;
1662        int markSegLen;    // the marker length
1663
1664        // Write one RGN marker per component
1665        for(i=0;i<nComp;i++){
1666            // RGN marker
1667            hbuf.writeShort(RGN);
1668
1669            // Calculate length (Lrgn)
1670            // Basic: Lrgn (2) + Srgn (1) + SPrgn + one byte
1671            // or two for component number
1672            markSegLen = 4+((nComp<257)? 1:2);
1673            hbuf.writeShort(markSegLen);
1674
1675            // Write component (Crgn)
1676            if(nComp<257)
1677                hbuf.writeByte(i);
1678            else
1679                hbuf.writeShort(i);
1680
1681            // Write type of ROI (Srgn)
1682            hbuf.writeByte(SRGN_IMPLICIT);
1683
1684            // Write ROI info (SPrgn)
1685            hbuf.writeByte(((Integer)(wp.getROIs().
1686                              getTileCompVal(tIdx,i))).intValue());
1687        }
1688    }
1689    /**
1690     * Writes tile-part header. JJ2000 tile-part header corresponds to the
1691     * following sequence of marker segments:<ol> <li>SOT</li> <li>COD (if
1692     * needed)</li> <li>COC (if needed)</li> <li>QCD (if needed)</li> <li>QCC
1693     * (if needed)</li> <li>RGN (if needed)</li> <li>POC (if needed)</li>
1694     * <li>SOD</li> </ol>
1695     *
1696     * @param length The length of the current tile-part.
1697     *
1698     * @param tileIdx Index of the tile to write
1699     * */
1700    public void encodeTilePartHeader(int tileLength,int tileIdx)
1701        throws IOException {
1702
1703        int tmp;
1704        Point numTiles = ralloc.getNumTiles(null);
1705        ralloc.setTile(tileIdx%numTiles.x,tileIdx/numTiles.x);
1706
1707        // +--------------------------+
1708        // |    SOT maker segment     |
1709        // +--------------------------+
1710        // SOT marker
1711        hbuf.writeByte(SOT>>8);
1712        hbuf.writeByte(SOT);
1713
1714        // Lsot (10 bytes)
1715        hbuf.writeByte(0);
1716        hbuf.writeByte(10);
1717
1718        // Isot
1719        if(tileIdx>65534){
1720            throw new IllegalArgumentException("Trying to write a tile-part "+
1721                                               "header whose tile index is too"+
1722                                               " high");
1723        }
1724        hbuf.writeByte(tileIdx>>8);
1725        hbuf.writeByte(tileIdx);
1726
1727        // Psot
1728        tmp = tileLength;
1729        hbuf.writeByte(tmp>>24);
1730        hbuf.writeByte(tmp>>16);
1731        hbuf.writeByte(tmp>>8);
1732        hbuf.writeByte(tmp);
1733
1734        // TPsot
1735        hbuf.writeByte(0); // Only one tile-part currently supported !
1736
1737        // TNsot
1738        hbuf.writeByte(1); // Only one tile-part currently supported !
1739
1740        // +--------------------------+
1741        // |    COD maker segment     |
1742        // +--------------------------+
1743        boolean isEresUsed = ((String)wp.getMethodForMQTermination().getDefault()).
1744            equals("predict");
1745        boolean isEresUsedInTile = ((String)wp.getMethodForMQTermination().getTileDef(tileIdx)).
1746            equals("predict");
1747        boolean tileCODwritten = false;
1748        if(wp.getFilters().isTileSpecified(tileIdx) ||
1749           wp.getComponentTransformation().isTileSpecified(tileIdx) ||
1750           wp.getDecompositionLevel().isTileSpecified(tileIdx) ||
1751           wp.getBypass().isTileSpecified(tileIdx) ||
1752           wp.getResetMQ().isTileSpecified(tileIdx) ||
1753           wp.getTerminateOnByte().isTileSpecified(tileIdx) ||
1754           wp.getCausalCXInfo().isTileSpecified(tileIdx) ||
1755           wp.getPrecinctPartition().isTileSpecified(tileIdx) ||
1756           wp.getSOP().isTileSpecified(tileIdx) ||
1757           wp.getCodeSegSymbol().isTileSpecified(tileIdx) ||
1758           wp.getProgressionType().isTileSpecified(tileIdx) ||
1759           wp.getEPH().isTileSpecified(tileIdx) ||
1760           wp.getCodeBlockSize().isTileSpecified(tileIdx) ||
1761           ( isEresUsed != isEresUsedInTile ) ) {
1762            writeCOD(false,tileIdx);
1763            tileCODwritten = true;
1764        }
1765
1766        // +--------------------------+
1767        // |    COC maker segment     |
1768        // +--------------------------+
1769        for(int c=0; c<nComp; c++){
1770            boolean isEresUsedInTileComp = ((String)wp.getMethodForMQTermination().
1771                                            getTileCompVal(tileIdx,c)).
1772                equals("predict");
1773
1774            if(wp.getFilters().isTileCompSpecified(tileIdx,c) ||
1775               wp.getDecompositionLevel().isTileCompSpecified(tileIdx,c) ||
1776               wp.getBypass().isTileCompSpecified(tileIdx,c) ||
1777               wp.getResetMQ().isTileCompSpecified(tileIdx,c) ||
1778               wp.getTerminateOnByte().isTileCompSpecified(tileIdx,c) ||
1779               wp.getCausalCXInfo().isTileCompSpecified(tileIdx,c) ||
1780               wp.getPrecinctPartition().isTileCompSpecified(tileIdx,c) ||
1781               wp.getCodeSegSymbol().isTileCompSpecified(tileIdx,c) ||
1782               wp.getCodeBlockSize().isTileCompSpecified(tileIdx,c) ||
1783               ( isEresUsedInTileComp != isEresUsed ) ) {
1784                writeCOC(false,tileIdx,c);
1785            }
1786            else if(tileCODwritten){
1787                if(wp.getFilters().isCompSpecified(c) ||
1788                   wp.getDecompositionLevel().isCompSpecified(c) ||
1789                   wp.getBypass().isCompSpecified(c) ||
1790                   wp.getResetMQ().isCompSpecified(c) ||
1791                   wp.getTerminateOnByte().isCompSpecified(c) ||
1792                   wp.getCodeSegSymbol().isCompSpecified(c) ||
1793                   wp.getCausalCXInfo().isCompSpecified(c) ||
1794                   wp.getPrecinctPartition().isCompSpecified(c) ||
1795                   wp.getCodeBlockSize().isCompSpecified(c) ||
1796                   (wp.getMethodForMQTermination().isCompSpecified(c)&&
1797                    ((String)wp.getMethodForMQTermination().getCompDef(c)).equals("predict"))){
1798                    writeCOC(false,tileIdx,c);
1799                }
1800            }
1801        }
1802
1803        // +--------------------------+
1804        // |    QCD maker segment     |
1805        // +--------------------------+
1806        boolean tileQCDwritten = false;
1807        if(wp.getQuantizationType().isTileSpecified(tileIdx) ||
1808           wp.getQuantizationStep().isTileSpecified(tileIdx) ||
1809           wp.getDecompositionLevel().isTileSpecified(tileIdx) ||
1810           wp.getGuardBits().isTileSpecified(tileIdx)){
1811            writeTileQCD(tileIdx);
1812            tileQCDwritten = true;
1813        } else {
1814            deftilenr = defimgn;
1815        }
1816
1817        // +--------------------------+
1818        // |    QCC maker segment     |
1819        // +--------------------------+
1820        for(int c=0; c<nComp; c++){
1821            if(dwt.getNomRangeBits(c)!= deftilenr ||
1822               wp.getQuantizationType().isTileCompSpecified(tileIdx,c) ||
1823               wp.getQuantizationStep().isTileCompSpecified(tileIdx,c) ||
1824               wp.getDecompositionLevel().isTileCompSpecified(tileIdx,c) ||
1825               wp.getGuardBits().isTileCompSpecified(tileIdx,c)){
1826                writeTileQCC(tileIdx,c);
1827            }
1828            else if(tileQCDwritten){
1829                if(wp.getQuantizationType().isCompSpecified(c) ||
1830                   wp.getQuantizationStep().isCompSpecified(c) ||
1831                   wp.getDecompositionLevel().isCompSpecified(c) ||
1832                   wp.getGuardBits().isCompSpecified(c)){
1833                    writeTileQCC(tileIdx,c);
1834                }
1835            }
1836        }
1837
1838        // +--------------------------+
1839        // |    RGN maker segment     |
1840        // +--------------------------+
1841        if(roiSc.useRoi() &&(!roiSc.getBlockAligned()))
1842            writeRGN(tileIdx);
1843
1844        // +--------------------------+
1845        // |    POC maker segment     |
1846        // +--------------------------+
1847        Progression[] prog;
1848        if( wp.getProgressionType().isTileSpecified(tileIdx) ){
1849            prog = (Progression[])(wp.getProgressionType().getTileDef(tileIdx));
1850            if(prog.length>1)
1851                writePOC(false,tileIdx);
1852        }
1853
1854        // +--------------------------+
1855        // |         SOD maker        |
1856        // +--------------------------+
1857        hbuf.writeByte(SOD>>8);
1858        hbuf.writeByte(SOD);
1859    }
1860}
1861