001/*
002 * $RCSfile: ROIScaler.java,v $
003 * $Revision: 1.1 $
004 * $Date: 2005/02/11 05:02:23 $
005 * $State: Exp $
006 *
007 * Class:                   ROIScaler
008 *
009 * Description:             This class takes care of the scaling of the
010 *                          samples
011 *
012 *
013 *
014 * COPYRIGHT:
015 *
016 * This software module was originally developed by Raphaël Grosbois and
017 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
018 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
019 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
020 * Centre France S.A) in the course of development of the JPEG2000
021 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
022 * software module is an implementation of a part of the JPEG 2000
023 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
024 * Systems AB and Canon Research Centre France S.A (collectively JJ2000
025 * Partners) agree not to assert against ISO/IEC and users of the JPEG
026 * 2000 Standard (Users) any of their rights under the copyright, not
027 * including other intellectual property rights, for this software module
028 * with respect to the usage by ISO/IEC and Users of this software module
029 * or modifications thereof for use in hardware or software products
030 * claiming conformance to the JPEG 2000 Standard. Those intending to use
031 * this software module in hardware or software products are advised that
032 * their use may infringe existing patents. The original developers of
033 * this software module, JJ2000 Partners and ISO/IEC assume no liability
034 * for use of this software module or modifications thereof. No license
035 * or right to this software module is granted for non JPEG 2000 Standard
036 * conforming products. JJ2000 Partners have full right to use this
037 * software module for his/her own purpose, assign or donate this
038 * software module to any third party and to inhibit third parties from
039 * using this software module for non JPEG 2000 Standard conforming
040 * products. This copyright notice must be included in all copies or
041 * derivative works of this software module.
042 *
043 * Copyright (c) 1999/2000 JJ2000 Partners.
044 * */
045package jj2000.j2k.roi.encoder;
046import java.awt.Point;
047
048import jj2000.j2k.quantization.quantizer.*;
049import jj2000.j2k.codestream.writer.*;
050import jj2000.j2k.wavelet.analysis.*;
051import jj2000.j2k.quantization.*;
052import jj2000.j2k.image.input.*;
053import jj2000.j2k.wavelet.*;
054import jj2000.j2k.image.*;
055import jj2000.j2k.util.*;
056import jj2000.j2k.roi.*;
057import jj2000.j2k.*;
058
059import java.util.*;
060import java.io.*;
061
062import com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageWriteParamJava;
063/**
064 * This class deals with the ROI functionality.
065 *
066 * <p>The ROI method is the Maxshift method. The ROIScaler works by scaling
067 * the quantized wavelet coefficients that do not affect the ROI (i.e
068 * background coefficients) so that these samples get a lower significance
069 * than the ROI ones. By scaling the coefficients sufficiently, the ROI
070 * coefficients can be recognized by their amplitude alone and no ROI mask
071 * needs to be generated at the decoder side.
072 *
073 * <p>The source module must be a quantizer and code-block's data is exchange
074 * with thanks to CBlkWTData instances.
075 *
076 * @see Quantizer
077 * @see CBlkWTData
078 * */
079public class ROIScaler extends ImgDataAdapter implements CBlkQuantDataSrcEnc {
080
081    /** The prefix for ROI Scaler options: 'R' */
082    public final static char OPT_PREFIX = 'R';
083
084    /** The list of parameters that are accepted for ROI coding. Options
085     * for ROI Scaler start with 'R'. */
086    private final static String [][] pinfo = {
087        { "Rroi","[<component idx>] R <left> <top> <width> <height>"+
088          " or [<component idx>] C <centre column> <centre row> "+
089          "<radius> or [<component idx>] A <filename>",
090          "Specifies ROIs shape and location. The shape can be either "+
091          "rectangular 'R', or circular 'C' or arbitrary 'A'. "+
092          "Each new occurrence of an 'R', a 'C' or an 'A' is a new ROI. "+
093          "For circular and rectangular ROIs, all values are "+
094          "given as their pixel values relative to the canvas origin. "+
095          "Arbitrary shapes must be included in a PGM file where non 0 "+
096          "values correspond to ROI coefficients. The PGM file must have "+
097          "the size as the image. "+
098          "The component idx specifies which components "+
099          "contain the ROI. The component index is specified as described "+
100          "by points 3 and 4 in the general comment on tile-component idx. "+
101          "If this option is used, the codestream is layer progressive by "+
102          "default unless it is overridden by the 'Aptype' option.",
103          null},
104        { "Ralign","[true|false]",
105          "By specifying this argument, the ROI mask will be "+
106          "limited to covering only entire code-blocks. The ROI coding can "+
107          "then be performed without any actual scaling of the coefficients "+
108          "but by instead scaling the distortion estimates.","false"},
109        { "Rstart_level","<level>",
110          "This argument forces the lowest <level> resolution levels to "+
111          "belong to the ROI. By doing this, it is possible to avoid only "+
112          "getting information for the ROI at an early stage of "+
113          "transmission.<level> = 0 means the lowest resolution level "+
114          "belongs to the ROI, 1 means the two lowest etc. (-1 deactivates"+
115          " the option)","-1"},
116        { "Rno_rect","[true|false]",
117          "This argument makes sure that the ROI mask generation is not done "+
118          "using the fast ROI mask generation for rectangular ROIs "+
119          "regardless of whether the specified ROIs are rectangular or not",
120          "false"},
121    };
122
123    /** The maximum number of magnitude bit-planes in any subband. One value
124     *  for each tile-component */
125    private int maxMagBits[][];
126
127    /** Flag indicating the presence of ROIs */
128    private boolean roi;
129
130    /** Flag indicating if block aligned ROIs are used */
131    private boolean blockAligned;
132
133    /** Number of resolution levels to include in ROI mask */
134    private int useStartLevel;
135
136    /** The class generating the ROI mask */
137    private ROIMaskGenerator mg;
138
139    /** The ROI mask */
140    private DataBlkInt roiMask;
141
142    /** The source of quantized wavelet transform coefficients */
143    private Quantizer src;
144
145    /**
146     * Constructor of the ROI scaler, takes a Quantizer as source of data to
147     * scale.
148     *
149     * @param src The quantizer that is the source of data.
150     *
151     * @param mg The mask generator that will be used for all components
152     *
153     * @param roi Flag indicating whether there are rois specified.
154     *
155     * @param sLev The resolution levels that belong entirely to ROI
156     *
157     * @param uba Flag indicating whether block aligning is used.
158     *
159     * @param encSpec The encoder specifications for addition of roi specs
160     * */
161    public ROIScaler(Quantizer src,
162                     ROIMaskGenerator mg,
163                     boolean roi,
164                     int sLev,
165                     boolean uba,
166                     J2KImageWriteParamJava wp){
167        super(src);
168        this.src = src;
169        this.roi = roi;
170        this.useStartLevel = sLev;
171        if(roi){
172            // If there is no ROI, no need to do this
173            this.mg = mg;
174            roiMask = new DataBlkInt();
175            calcMaxMagBits(wp);
176            blockAligned = uba;
177        }
178    }
179
180    /**
181     * Since ROI scaling is always a reversible operation, it calls
182     * isReversible() method of it source (the quantizer module).
183     *
184     * @param t The tile to test for reversibility
185     *
186     * @param c The component to test for reversibility
187     *
188     * @return True if the quantized data is reversible, false if not.
189     * */
190    public boolean isReversible(int t,int c){
191        return src.isReversible(t,c);
192    }
193
194    /**
195     * Returns a reference to the subband tree structure representing the
196     * subband decomposition for the specified tile-component.
197     *
198     * @param t The index of the tile.
199     *
200     * @param c The index of the component.
201     *
202     * @return The subband tree structure, see SubbandAn.
203     *
204     * @see SubbandAn
205     *
206     * @see Subband
207     * */
208    public SubbandAn getAnSubbandTree(int t,int c) {
209        return src.getAnSubbandTree(t,c);
210    }
211
212    /**
213     * Returns the horizontal offset of the code-block partition. Allowable
214     * values are 0 and 1, nothing else.
215     * */
216    public int getCbULX() {
217        return src.getCbULX();
218    }
219
220    /**
221     * Returns the vertical offset of the code-block partition. Allowable
222     * values are 0 and 1, nothing else.
223     * */
224    public int getCbULY() {
225        return src.getCbULY();
226    }
227
228    /**
229     * Creates a ROIScaler object. The Quantizer is the source of data to
230     * scale.
231     *
232     * <P> The ROI Scaler creates a ROIMaskGenerator depending on what ROI
233     * information is in the J2KImageWriteParamJava. If only rectangular ROI are used,
234     * the fast mask generator for rectangular ROI can be used.
235     *
236     * @param src The source of data to scale
237     *
238     * @param pl The parameter list (or options).
239     *
240     * @param encSpec The encoder specifications for addition of roi specs
241     *
242     * @exception IllegalArgumentException If an error occurs while parsing
243     * the options in 'pl'
244     * */
245    public static ROIScaler createInstance(Quantizer src,
246                                           J2KImageWriteParamJava wp){
247        Vector roiVector = new Vector();
248        ROIMaskGenerator maskGen = null;
249
250        /*   XXX: need investigation
251        // Check parameters
252        pl.checkList(OPT_PREFIX,pl.toNameArray(pinfo));
253        */
254
255        // Get parameters and check if there are and ROIs specified
256        String roiopt = wp.getROIs().getSpecified();
257        if (roiopt == null) {
258            // No ROIs specified! Create ROIScaler with no mask generator
259            return new ROIScaler(src,null,false,-1,false,wp);
260        }
261
262        // Check if the lowest resolution levels should belong to the ROI
263        int sLev = wp.getStartLevelROI();
264
265        // Check if the ROIs are block-aligned
266        boolean useBlockAligned = wp.getAlignROI();
267
268        // Check if generic mask generation is specified
269        boolean onlyRect = false;
270
271        // Parse the ROIs
272        parseROIs(roiopt,src.getNumComps(),roiVector);
273        ROI[] roiArray = new ROI[roiVector.size()];
274        roiVector.copyInto(roiArray);
275
276        // If onlyRect has been forced, check if there are any non-rectangular
277        // ROIs specified.  Currently, only the presence of circular ROIs will
278        // make this false
279        if(onlyRect) {
280            for(int i=roiArray.length-1 ; i>=0  ; i--)
281                if(!roiArray[i].rect){
282                    onlyRect=false;
283                    break;
284                }
285        }
286
287        if(onlyRect){
288            // It's possible to use the fast ROI mask generation when only
289            // rectangular ROIs are specified.
290            maskGen = new RectROIMaskGenerator(roiArray,src.getNumComps());
291        }
292        else{
293            // It's necessary to use the generic mask generation
294            maskGen = new ArbROIMaskGenerator(roiArray,src.getNumComps(),src);
295        }
296
297        return new ROIScaler(src,maskGen,true,sLev,useBlockAligned,wp);
298    }
299
300    /**
301     * This function parses the values given for the ROIs with the argument
302     * -Rroi. Currently only circular and rectangular ROIs are supported.
303     *
304     * <P> A rectangular ROI is indicated by a 'R' followed the coordinates
305     * for the upper left corner of the ROI and then its width and height.
306     *
307     * <P> A circular ROI is indicated by a 'C' followed by the coordinates of
308     * the circle center and then the radius.
309     *
310     * <P> Before the R and C values, the component that are affected by the
311     * ROI are indicated.
312     *
313     * @param roiopt The info on the ROIs
314     *
315     * @param nc number of components
316     *
317     * @param roiVector The vcector containing the ROI parsed from the cmd line
318     *
319     * @return The ROIs specified in roiopt
320     * */
321    protected static Vector parseROIs(String roiopt, int nc,Vector roiVector){
322        ROI[] ROIs;
323        ROI roi;
324        StringTokenizer stok;
325        char tok;
326        int nrOfROIs = 0;
327        char c;
328        int comp,ulx,uly,w,h,x,y,rad;
329        boolean[] roiInComp = null;
330
331        stok = new StringTokenizer(roiopt);
332
333        String word;
334        while(stok.hasMoreTokens()){
335            word = stok.nextToken();
336
337            switch(word.charAt(0)){
338            case 'c': // Components specification
339                roiInComp = ModuleSpec.parseIdx(word,nc);
340                break;
341            case 'R': // Rectangular ROI to be read
342                nrOfROIs++;
343                try{
344                    word = stok.nextToken();
345                    ulx = (new Integer(word)).intValue();
346                    word = stok.nextToken();
347                    uly = (new Integer(word)).intValue();
348                    word = stok.nextToken();
349                    w = (new Integer(word)).intValue();
350                    word = stok.nextToken();
351                    h = (new Integer(word)).intValue();
352                }
353                catch(NumberFormatException e){
354                    throw new IllegalArgumentException("Bad parameter for "+
355                                                       "'-Rroi R' option : "+
356                                                       word);
357                }
358                catch(NoSuchElementException f){
359                    throw new IllegalArgumentException("Wrong number of "+
360                                                       "parameters for  "+
361                                                       "h'-Rroi R' option.");
362                }
363
364                // If the ROI is component-specific, check which comps.
365                if(roiInComp != null)
366                    for(int i=0; i<nc;i++){
367                        if(roiInComp[i]){
368                            roi=new ROI(i,ulx,uly,w,h);
369                            roiVector.addElement(roi);
370                        }
371                    }
372                else{ // Otherwise add ROI for all components
373                    for(int i=0; i<nc;i++){
374                        roi=new ROI(i,ulx,uly,w,h);
375                        roiVector.addElement(roi);
376                    }
377                }
378                break;
379            case 'C': // Circular ROI to be read
380                nrOfROIs++;
381
382                try{
383                    word = stok.nextToken();
384                    x = (new Integer(word)).intValue();
385                    word = stok.nextToken();
386                    y = (new Integer(word)).intValue();
387                    word = stok.nextToken();
388                    rad = (new Integer(word)).intValue();
389                }
390                catch(NumberFormatException e){
391                    throw new IllegalArgumentException("Bad parameter for "+
392                                                       "'-Rroi C' option : "+
393                                                       word);
394                }
395                catch(NoSuchElementException f){
396                    throw new IllegalArgumentException("Wrong number of "+
397                                                       "parameters for "+
398                                                       "'-Rroi C' option.");
399                }
400
401                // If the ROI is component-specific, check which comps.
402                if(roiInComp != null)
403                    for(int i=0; i<nc; i++){
404                        if(roiInComp[i]){
405                            roi = new ROI(i,x,y,rad);
406                            roiVector.addElement(roi);
407                        }
408                    }
409                else{ // Otherwise add ROI for all components
410                    for(int i=0; i<nc; i++){
411                        roi = new ROI(i,x,y,rad);
412                        roiVector.addElement(roi);
413                    }
414                }
415                break;
416            case 'A': // ROI wth arbitrary shape
417                nrOfROIs++;
418
419                String filename;
420                ImgReaderPGM maskPGM = null;
421
422                try{
423                    filename = stok.nextToken();
424                }
425                catch(NoSuchElementException e){
426                    throw new IllegalArgumentException("Wrong number of "+
427                                                       "parameters for "+
428                                                       "'-Rroi A' option.");
429                }
430                try {
431                    maskPGM = new ImgReaderPGM(filename);
432                }
433                catch(IOException e){
434                    throw new Error("Cannot read PGM file with ROI");
435                }
436
437                // If the ROI is component-specific, check which comps.
438                if(roiInComp != null)
439                    for(int i=0; i<nc; i++){
440                        if(roiInComp[i]){
441                            roi = new ROI(i,maskPGM);
442                            roiVector.addElement(roi);
443                        }
444                    }
445                else{ // Otherwise add ROI for all components
446                    for(int i=0; i<nc; i++){
447                        roi = new ROI(i,maskPGM);
448                        roiVector.addElement(roi);
449                    }
450                }
451                break;
452            default:
453                throw new Error("Bad parameters for ROI nr "+roiVector.size());
454            }
455        }
456
457        return roiVector;
458    }
459
460    /**
461     * This function gets a datablk from the entropy coder. The sample sin the
462     * block, which consists of  the quantized coefficients from the quantizer,
463     * are scaled by the values given for any ROIs specified.
464     *
465     * <P>The function calls on a ROIMaskGenerator to get the mask for
466     * scaling the coefficients in the current block.
467     *
468     * <P>The data returned by this method is a copy of the orignal
469     * data. Therfore it can be modified "in place" without any problems after
470     * being returned. The 'offset' of the returned data is 0, and the 'scanw'
471     * is the same as the code-block width. See the 'CBlkWTData' class.
472     *
473     * @param n The component for which to return the next code-block.
474     *
475     * @param cblk If non-null this object will be used to return the new
476     * code-block. If null a new one will be allocated and returned. If the
477     * "data" array of the object is non-null it will be reused, if possible,
478     * to return the data.
479     *
480     * @return The next code-block in the current tile for component 'n', or
481     * null if all code-blocks for the current tile have been returned.
482     *
483     * @see CBlkWTData
484     * */
485    public CBlkWTData getNextCodeBlock(int n, CBlkWTData cblk) {
486        return getNextInternCodeBlock(n,cblk);
487    }
488
489    /**
490     * This function gets a datablk from the entropy coder. The sample sin the
491     * block, which consists of  the quantized coefficients from the quantizer,
492     * are scaled by the values given for any ROIs specified.
493     *
494     * <P>The function calls on a ROIMaskGenerator to get the mask for
495     * scaling the coefficients in the current block.
496     *
497     * @param c The component for which to return the next code-block.
498     *
499     * @param cblk If non-null this object will be used to return the new
500     * code-block. If null a new one will be allocated and returned. If the
501     * "data" array of the object is non-null it will be reused, if possible,
502     * to return the data.
503     *
504     * @return The next code-block in the current tile for component 'n', or
505     * null if all code-blocks for the current tile have been returned.
506     *
507     * @see CBlkWTData
508     * */
509    public CBlkWTData getNextInternCodeBlock(int c, CBlkWTData cblk){
510        int mi,i,j,k,wrap;
511        int ulx, uly, w, h;
512        DataBlkInt mask = roiMask;        // local copy of mask
513        int[] maskData;                   // local copy of mask data
514        int[] data;                       // local copy of quantized data
515        int tmp;
516        int bitMask = 0x7FFFFFFF;
517        SubbandAn root,sb;
518        int maxBits = 0; // local copy
519        boolean roiInTile;
520        boolean sbInMask;
521        int nROIcoeff = 0;
522
523        // Get codeblock's data from quantizer
524        cblk = src.getNextCodeBlock(c,cblk);
525
526        // If there is no ROI in the image, or if we already got all
527        // code-blocks
528        if (!roi || cblk == null) {
529            return cblk;
530        }
531
532        data = (int[])cblk.getData();
533        sb = cblk.sb;
534        ulx = cblk.ulx;
535        uly = cblk.uly;
536        w = cblk.w;
537        h = cblk.h;
538        sbInMask= (sb.resLvl<=useStartLevel);
539
540        // Check that there is an array for the mask and set it to zero
541        maskData = mask.getDataInt(); // local copy of mask data
542        if(maskData==null || w*h>maskData.length){
543            maskData = new int[w*h];
544            mask.setDataInt(maskData);
545        }
546        else{
547            for(i=w*h-1;i>=0;i--)
548                maskData[i]=0;
549        }
550        mask.ulx = ulx;
551        mask.uly = uly;
552        mask.w = w;
553        mask.h = h;
554
555        // Get ROI mask from generator
556        root = src.getAnSubbandTree(tIdx,c);
557        maxBits = maxMagBits[tIdx][c];
558        roiInTile = mg.getROIMask(mask,root,maxBits,c);
559
560        // If there is no ROI in this tile, return the code-block untouched
561        if(!roiInTile && (!sbInMask)) {
562            cblk.nROIbp = 0;
563            return cblk;
564        }
565
566        // Update field containing the number of ROI magnitude bit-planes
567        cblk.nROIbp = cblk.magbits;
568
569        // If the ROI should adhere to the code-block's boundaries or if the
570        // entire subband belongs to the ROI mask, The code-block is set to
571        // belong entirely to the ROI with the highest scaling value
572        if(sbInMask) {
573            // Scale the wmse so that instead of scaling the coefficients, the
574            // wmse is scaled.
575            cblk.wmseScaling *= (float)(1<<(maxBits<<1));
576            cblk.nROIcoeff = w*h;
577            return cblk;
578        }
579
580        // In 'block aligned' mode, the code-block is set to belong entirely
581        // to the ROI with the highest scaling value if one coefficient, at
582        // least, belongs to the ROI
583        if(blockAligned) {
584            wrap=cblk.scanw-w;
585            mi=h*w-1;
586            i=cblk.offset+cblk.scanw*(h-1)+w-1;
587            int nroicoeff = 0;
588            for(j=h;j>0;j--){
589                for(k=w-1;k>=0;k--,i--,mi--){
590                    if (maskData[mi] != 0) {
591                        nroicoeff++;
592                    }
593                }
594                i -= wrap;
595            }
596            if(nroicoeff!=0) { // Include the subband
597                cblk.wmseScaling *= (float)(1<<(maxBits<<1));
598                cblk.nROIcoeff = w*h;
599            }
600            return cblk;
601        }
602
603        // Scale background coefficients
604        bitMask=(((1<<cblk.magbits)-1)<<(31-cblk.magbits));
605        wrap=cblk.scanw-w;
606        mi=h*w-1;
607        i=cblk.offset+cblk.scanw*(h-1)+w-1;
608        for(j=h;j>0;j--){
609            for(k=w;k>0;k--,i--,mi--){
610                tmp=data[i];
611                if (maskData[mi] != 0) {
612                    // ROI coeff. We need to erase fractional bits to ensure
613                    // that they do not conflict with BG coeffs. This is only
614                    // strictly necessary for ROI coeffs. which non-fractional
615                    // magnitude is zero, but much better BG quality can be
616                    // achieved if done if reset to zero since coding zeros is
617                    // much more efficient (the entropy coder knows nothing
618                    // about ROI and cannot avoid coding the ROI fractional
619                    // bits, otherwise this would not be necessary).
620                    data[i] = (0x80000000 & tmp) | (tmp & bitMask);
621                    nROIcoeff++;
622                }
623                else {
624                    // BG coeff. it is not necessary to erase fractional bits
625                    data[i] = (0x80000000 & tmp) |
626                        ((tmp & 0x7FFFFFFF) >> maxBits);
627                }
628            }
629            i-=wrap;
630        }
631
632        // Modify the number of significant bit-planes in the code-block
633        cblk.magbits += maxBits;
634
635        // Store the number of ROI coefficients present in the code-block
636        cblk.nROIcoeff = nROIcoeff;
637
638        return cblk;
639    }
640
641    /**
642     * This function returns the ROI mask generator.
643     *
644     * @return The roi mask generator
645     */
646    public ROIMaskGenerator getROIMaskGenerator(){
647        return mg;
648    }
649
650   /**
651     * This function returns the blockAligned flag
652     *
653     * @return Flag indicating whether the ROIs were block aligned
654     */
655    public boolean getBlockAligned(){
656        return blockAligned;
657    }
658
659   /**
660     * This function returns the flag indicating if any ROI functionality used
661     *
662     * @return Flag indicating whether there are ROIs in the image
663     */
664    public boolean useRoi(){
665        return roi;
666    }
667
668    /**
669     * Returns the parameters that are used in this class and
670     * implementing classes. It returns a 2D String array. Each of the
671     * 1D arrays is for a different option, and they have 3
672     * elements. The first element is the option name, the second one
673     * is the synopsis, the third one is a long description of what
674     * the parameter is and the fourth is its default value. The
675     * synopsis or description may be 'null', in which case it is
676     * assumed that there is no synopsis or description of the option,
677     * respectively. Null may be returned if no options are supported.
678     *
679     * @return the options name, their synopsis and their explanation,
680     * or null if no options are supported.
681     * */
682    public static String[][] getParameterInfo() {
683        return pinfo;
684    }
685
686    /**
687     * Changes the current tile, given the new indexes. An
688     * IllegalArgumentException is thrown if the indexes do not
689     * correspond to a valid tile.
690     *
691     * @param x The horizontal index of the tile.
692     *
693     * @param y The vertical index of the new tile.
694     * */
695    public void setTile(int x, int y) {
696        super.setTile(x,y);
697        if(roi)
698            mg.tileChanged();
699    }
700
701    /**
702     * Advances to the next tile, in standard scan-line order (by rows then
703     * columns). An NoNextElementException is thrown if the current tile is
704     * the last one (i.e. there is no next tile).
705     * */
706    public void nextTile() {
707        super.nextTile();
708        if(roi)
709            mg.tileChanged();
710    }
711
712    /**
713     * Calculates the maximum amount of magnitude bits for each
714     * tile-component, and stores it in the 'maxMagBits' array. This is called
715     * by the constructor
716     *
717     * @param encSpec The encoder specifications for addition of roi specs
718     * */
719    private void calcMaxMagBits(J2KImageWriteParamJava wp) {
720        int tmp;
721        MaxShiftSpec rois = wp.getROIs();
722
723        int nt =  src.getNumTiles();
724        int nc = src.getNumComps();
725
726        maxMagBits = new int[nt][nc];
727
728        src.setTile(0,0);
729        for (int t=0; t<nt; t++) {
730            for (int c=nc-1; c>=0; c--) {
731                tmp = src.getMaxMagBits(c);
732                maxMagBits[t][c] = tmp;
733                rois.setTileCompVal(t,c,new Integer(tmp));
734            }
735            if( t<nt-1 ) src.nextTile();
736        }
737        // Reset to current initial tile position
738        src.setTile(0,0);
739    }
740}
741