001/*
002 * $RCSfile: InvCompTransf.java,v $
003 * $Revision: 1.1 $
004 * $Date: 2005/02/11 05:02:14 $
005 * $State: Exp $
006 *
007 * Class:               InvCompTransf
008 *
009 * Description:         Inverse Component transformations applied to tiles
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.image.invcomptransf;
045
046import jj2000.j2k.wavelet.synthesis.*;
047import jj2000.j2k.decoder.*;
048import jj2000.j2k.image.*;
049import jj2000.j2k.util.*;
050import jj2000.j2k.*;
051
052/**
053 * This class apply inverse component transformations to the tiles depending
054 * on specification read from the codestream header. These transformations can
055 * be used to improve compression efficiency but are not related to colour
056 * transforms used to map colour values for display purposes. JPEG 2000 part I
057 * defines 2 component transformations: RCT (Reversible Component
058 * Transformation) and ICT (Irreversible Component Transformation).
059 *
060 * @see ModuleSpec
061 * */
062public class InvCompTransf extends ImgDataAdapter
063    implements BlkImgDataSrc{
064
065    /** Identifier for no component transformation. Value is 0. */
066    public static final int NONE = 0;
067
068    /** The prefix for inverse component transformation options: 'M' */
069    public final static char OPT_PREFIX = 'M';
070
071    /** The list of parameters that is accepted by the inverse
072     * component transformation module. They start with 'M'. */
073    private final static String [][] pinfo = null;
074
075    /** Identifier for the Inverse Reversible Component Transformation
076        (INV_RCT). Value is 1. */
077    public static final int INV_RCT = 1;
078
079    /** Identifier for the Inverse Irreversible Component
080        Transformation (INV_ICT). Value is 2 */
081    public static final int INV_ICT = 2;
082
083    /** The source of image data */
084    private BlkImgDataSrc src;
085
086    /** The component transformations specifications */
087    private CompTransfSpec cts;
088
089    /** The wavelet filter specifications */
090    private SynWTFilterSpec wfs;
091
092    /** The type of the current component transformation JPEG 2000
093     * part I only support NONE, FORW_RCT and FORW_ICT types*/
094    private int transfType = NONE;
095
096    /** Buffer for each component of output data */
097    private int[][] outdata = new int[3][];
098
099    /** Block used to request component 0 */
100    private DataBlk block0;
101
102    /** Block used to request component 1 */
103    private DataBlk block1;
104
105    /** Block used to request component 2 */
106    private DataBlk block2;
107
108    /** Data block used only to store coordinates and progressiveness
109        of the buffered blocks */
110    private DataBlkInt dbi = new DataBlkInt();
111
112    /** The bit-depths of un-transformed components */
113    private int utdepth[];
114
115    /** Flag indicating whether the decoder should skip the component 
116     * transform*/
117    private boolean noCompTransf = false;
118
119    /**
120     * Constructs a new ForwCompTransf object that operates on the
121     * specified source of image data.
122     *
123     * @param imgSrc The source from where to get the data to be
124     * transformed
125     *
126     * @param decSpec The decoder specifications
127     *
128     * @see BlkImgDataSrc
129     * */
130    public InvCompTransf(BlkImgDataSrc imgSrc, DecoderSpecs decSpec,
131                         int[] utdepth) {
132        super(imgSrc);
133        this.cts = decSpec.cts;
134        this.wfs = decSpec.wfs;
135        src = imgSrc;
136        this.utdepth = utdepth;
137    }
138
139    /**
140     * Returns the parameters that are used in this class and implementing
141     * classes. It returns a 2D String array. Each of the 1D arrays is for a
142     * different option, and they have 4 elements. The first element is the
143     * option name, the second one is the synopsis, the third one is a long
144     * description of what the parameter is and the fourth is its default
145     * value. The synopsis or description may be 'null', in which case it is
146     * assumed that there is no synopsis or description of the option,
147     * respectively. Null may be returned if no options are supported.
148     *
149     * @return the options name, their synopsis and their explanation,
150     * or null if no options are supported.
151     * */
152    public static String[][] getParameterInfo() {
153        return pinfo;
154    }
155
156    /**
157     * Returns a string with a descriptive text of which inverse component
158     * transformation is used. This can be either "Inverse RCT" or "Inverse
159     * ICT" or "No component transformation" depending on the current tile.
160     *
161     * @return A descriptive string
162     * */
163    public String toString() {
164        switch(transfType){
165        case INV_RCT:
166            return "Inverse RCT";
167        case INV_ICT:
168            return "Inverse ICT";
169        case NONE:
170            return "No component transformation";
171        default:
172            throw new IllegalArgumentException("Non JPEG 2000 part I"+
173                                               " component transformation");
174        }
175    }
176
177    /**
178     * Returns true if this transform is reversible in current
179     * tile. Reversible component transformations are those which operation
180     * can be completely reversed without any loss of information (not even
181     * due to rounding).
182     *
183     * @return Reversibility of component transformation in current
184     * tile
185     * */
186    public boolean isReversible(){
187        switch(transfType){
188        case NONE:
189        case INV_RCT:
190            return true;
191        case INV_ICT:
192            return false;
193        default:
194            throw new IllegalArgumentException("Non JPEG 2000 part I"+
195                                               " component transformation");
196        }
197    }
198
199    /**
200     * Returns the position of the fixed point in the specified
201     * component. This is the position of the least significant integral
202     * (i.e. non-fractional) bit, which is equivalent to the number of
203     * fractional bits. For instance, for fixed-point values with 2 fractional
204     * bits, 2 is returned. For floating-point data this value does not apply
205     * and 0 should be returned. Position 0 is the position of the least
206     * significant bit in the data.
207     *
208     * <P>This default implementation assumes that the number of fractional
209     * bits is not modified by the component mixer.
210     *
211     * @param c The index of the component.
212     *
213     * @return The value of the fixed point position of the source since the
214     * color transform does not affect it.
215     * */
216     public int getFixedPoint(int c) {
217         return src.getFixedPoint(c);
218     }
219
220    /**
221     * Calculates the bitdepths of the transformed components, given the
222     * bitdepth of the un-transformed components and the component
223     * tranformation type.
224     *
225     * @param utdepth The bitdepth of each un-transformed component
226     *
227     * @param ttype The type ID of the inverse component tranformation
228     *
229     * @param tdepth If not null the results are stored in this
230     * array, otherwise a new array is allocated and returned.
231     *
232     * @return The bitdepth of each transformed component.
233     * */
234    public static
235        int[] calcMixedBitDepths(int utdepth[], int ttype, int tdepth[]) {
236
237        if (utdepth.length < 3 && ttype != NONE) {
238            throw new IllegalArgumentException();
239        }
240
241        if (tdepth == null) {
242            tdepth = new int[utdepth.length];
243        }
244
245        switch (ttype) {
246        case NONE:
247            System.arraycopy(utdepth,0,tdepth,0,utdepth.length);
248            break;
249        case INV_RCT:
250            if (utdepth.length >3) {
251                System.arraycopy(utdepth,3,tdepth,3,utdepth.length-3);
252            }
253            // The formulas are:
254            // tdepth[0] = ceil(log2(2^(utdepth[0])+2^utdepth[1]+
255            //                        2^(utdepth[2])))-2+1
256            // tdepth[1] = ceil(log2(2^(utdepth[0])+2^(utdepth[1])-1))+1
257            // tdepth[2] = ceil(log2(2^(utdepth[1])+2^(utdepth[2])-1))+1
258            // The MathUtil.log2(x) function calculates floor(log2(x)), so we
259            // use 'MathUtil.log2(2*x-1)+1', which calculates ceil(log2(x))
260            // for any x>=1, x integer.
261            tdepth[0] = MathUtil.log2((1<<utdepth[0])+(2<<utdepth[1])+
262                                      (1<<utdepth[2])-1)-2+1;
263            tdepth[1] = MathUtil.log2((1<<utdepth[2])+(1<<utdepth[1])-1)+1;
264            tdepth[2] = MathUtil.log2((1<<utdepth[0])+(1<<utdepth[1])-1)+1;
265            break;
266        case INV_ICT:
267            if (utdepth.length >3) {
268                System.arraycopy(utdepth,3,tdepth,3,utdepth.length-3);
269            }
270            // The MathUtil.log2(x) function calculates floor(log2(x)), so we
271            // use 'MathUtil.log2(2*x-1)+1', which calculates ceil(log2(x))
272            // for any x>=1, x integer.
273            tdepth[0] =
274                MathUtil.log2((int)Math.floor((1<<utdepth[0])*0.299072+
275                                              (1<<utdepth[1])*0.586914+
276                                              (1<<utdepth[2])*0.114014)-1)+1;
277            tdepth[1] =
278                MathUtil.log2((int)Math.floor((1<<utdepth[0])*0.168701+
279                                              (1<<utdepth[1])*0.331299+
280                                              (1<<utdepth[2])*0.5)-1)+1;
281            tdepth[2] =
282                MathUtil.log2((int)Math.floor((1<<utdepth[0])*0.5+
283                                              (1<<utdepth[1])*0.418701+
284                                              (1<<utdepth[2])*0.081299)-1)+1;
285            break;
286        }
287
288        return tdepth;
289    }
290
291    /**
292     * Returns the number of bits, referred to as the "range bits",
293     * corresponding to the nominal range of the data in the specified
294     * component. If this number is <i>b</b> then for unsigned data the
295     * nominal range is between 0 and 2^b-1, and for signed data it is between
296     * -2^(b-1) and 2^(b-1)-1.
297     *
298     * @param c The index of the component.
299     *
300     * @return The bitdepth of un-transformed component 'c'.
301     * */
302    public int getNomRangeBits(int c) {
303        return utdepth[c];
304    }
305
306    /**
307     * Apply inverse component transformation associated with the current
308     * tile. If no component transformation has been requested by the user,
309     * data are not modified.
310     *
311     * <P>This method calls the getInternCompData() method, but respects the
312     * definitions of the getCompData() method defined in the BlkImgDataSrc
313     * interface.
314     *
315     * @param blk Determines the rectangular area to return, and the
316     * data is returned in this object.
317     *
318     * @param c Index of the output component.
319     *
320     * @return The requested DataBlk
321     *
322     * @see BlkImgDataSrc#getCompData
323     * */
324    public DataBlk getCompData(DataBlk blk, int c){
325        // If requesting a component whose index is greater than 3 or there is
326        // no transform return a copy of data (getInternCompData returns the
327        // actual data in those cases)
328        if (c>=3 || transfType == NONE) {
329            return src.getCompData(blk,c);
330        }
331        else { // We can use getInternCompData (since data is a copy anyways)
332            return getInternCompData(blk,c);
333        }
334    }
335
336    /**
337     * Apply the inverse component transformation associated with the current
338     * tile. If no component transformation has been requested by the user,
339     * data are not modified. Else, appropriate method is called (invRCT or
340     * invICT).
341     *
342     * @see #invRCT
343     *
344     * @see #invICT
345     *
346     * @param blk Determines the rectangular area to return.
347     *
348     * @param c Index of the output component.
349     *
350     * @return The requested DataBlk
351     * */
352    public DataBlk getInternCompData(DataBlk blk, int c){
353        // if specified in the command line that no component transform should
354        // be made, return original data
355        if(noCompTransf)
356            return src.getInternCompData(blk,c);
357
358        switch(transfType){
359        case NONE:
360            return src.getInternCompData(blk,c);
361        case INV_RCT:
362            return invRCT(blk,c);
363        case INV_ICT:
364            return invICT(blk,c);
365        default:
366            throw new IllegalArgumentException("Non JPEG 2000 part I"+
367                                               " component transformation");
368        }
369    }
370
371    /**
372     * Apply inverse component transformation to obtain requested component
373     * from specified block of data. Whatever the type of requested DataBlk,
374     * it always returns a DataBlkInt.
375     *
376     * @param blk Determine the rectangular area to return
377     *
378     * @param c The index of the requested component
379     *
380     * @return Data of requested component
381     * */
382    private DataBlk invRCT(DataBlk blk,int c){
383        // If the component number is three or greater, return original data
384        if (c>=3 && c < getNumComps()) {
385            // Requesting a component whose index is greater than 3
386            return src.getInternCompData(blk,c);
387        }
388
389        // If asking a component for the first time for this block,
390        // do transform for the 3 components
391        if ((outdata[c] == null)||
392            (dbi.ulx > blk.ulx) || (dbi.uly > blk.uly) ||
393            (dbi.ulx+dbi.w < blk.ulx+blk.w) ||
394            (dbi.uly+dbi.h < blk.uly+blk.h)) {
395            int k,k0,k1,k2,mink,i;
396            int w = blk.w; //width of output block
397            int h = blk.h; //height of ouput block
398
399            //Reference to output block data array
400            outdata[c] = (int[]) blk.getData();
401
402            //Create data array of blk if necessary
403            if(outdata[c] == null || outdata[c].length!=h*w){
404                outdata[c] = new int[h * w];
405                blk.setData(outdata[c]);
406            }
407
408            outdata[(c+1)%3] = new int[outdata[c].length];
409            outdata[(c+2)%3] = new int[outdata[c].length];
410
411            if(block0==null || block0.getDataType()!=DataBlk.TYPE_INT)
412                block0 = new DataBlkInt();
413            if(block1==null || block1.getDataType()!=DataBlk.TYPE_INT)
414                block1 = new DataBlkInt();
415            if(block2==null || block2.getDataType()!=DataBlk.TYPE_INT)
416                block2 = new DataBlkInt();
417            block0.w = block1.w = block2.w = blk.w;
418            block0.h = block1.h = block2.h = blk.h;
419            block0.ulx = block1.ulx = block2.ulx = blk.ulx;
420            block0.uly = block1.uly = block2.uly = blk.uly;
421
422            int data0[],data1[],data2[]; // input data arrays
423
424            // Fill in buffer blocks (to be read only)
425            // Returned blocks may have different size and position
426            block0 = (DataBlkInt)src.getInternCompData(block0, 0);
427            data0 = (int[]) block0.getData();
428            block1 = (DataBlkInt)src.getInternCompData(block1, 1);
429            data1 = (int[]) block1.getData();
430            block2 = (DataBlkInt)src.getInternCompData(block2, 2);
431            data2 = (int[]) block2.getData();
432
433            // Set the progressiveness of the output data
434            blk.progressive = block0.progressive || block1.progressive ||
435                block2.progressive;
436            blk.offset = 0;
437            blk.scanw = w;
438
439            // set attributes of the DataBlk used for buffering
440            dbi.progressive = blk.progressive;
441            dbi.ulx = blk.ulx;
442            dbi.uly = blk.uly;
443            dbi.w = blk.w;
444            dbi.h = blk.h;
445
446            // Perform conversion
447
448            // Initialize general indexes
449            k = w*h-1;
450            k0 = block0.offset+(h-1)*block0.scanw+w-1;
451            k1 = block1.offset+(h-1)*block1.scanw+w-1;
452            k2 = block2.offset+(h-1)*block2.scanw+w-1;
453
454            for( i = h-1; i >=0; i--){
455                for(mink = k-w; k > mink; k--, k0--, k1--, k2--){
456                    outdata[1][k] = (data0[k0] - ((data1[k1]+data2[k2])>>2) );
457                    outdata[0][k] = data2[k2] + outdata[1][k];
458                    outdata[2][k] = data1[k1] + outdata[1][k];
459                }
460                // Jump to beggining of previous line in input
461                k0 -= block0.scanw - w;
462                k1 -= block1.scanw - w;
463                k2 -= block2.scanw - w;
464            }
465            outdata[c] = null;
466        }
467        else if((c>=0)&&(c<=3)){ //Asking for the 2nd or 3rd block component
468            blk.setData(outdata[c]);
469            blk.progressive = dbi.progressive;
470            blk.offset = (blk.uly-dbi.uly)*dbi.w+blk.ulx-dbi.ulx;
471            blk.scanw = dbi.w;
472            outdata[c] = null;
473        }
474        else {
475            // Requesting a non valid component index
476            throw new IllegalArgumentException();
477        }
478        return blk;
479    }
480
481    /**
482     * Apply inverse irreversible component transformation to obtain requested
483     * component from specified block of data. Whatever the type of requested
484     * DataBlk, it always returns a DataBlkFloat.
485     *
486     * @param blk Determine the rectangular area to return
487     *
488     * @param c The index of the requested component
489     *
490     * @return Data of requested component
491     * */
492    private DataBlk invICT(DataBlk blk,int c){
493        if(c>=3 && c<getNumComps()) {
494            // Requesting a component whose index is greater than 3            
495            int k,k0,k1,k2,mink,i;
496            int w = blk.w; //width of output block
497            int h = blk.h; //height of ouput block
498
499            int outdata[]; // array of output data
500
501            //Reference to output block data array
502            outdata = (int[]) blk.getData();
503
504            //Create data array of blk if necessary
505            if( outdata == null ) {
506                outdata = new int[h * w];
507                blk.setData(outdata);
508            }
509
510            // Variables
511            DataBlkFloat indb = new DataBlkFloat(blk.ulx,blk.uly,w,h);
512            float indata[]; // input data array
513
514            // Get the input data
515            // (returned block may be larger than requested one)
516            src.getInternCompData(indb,c);
517            indata = (float[]) indb.getData();
518
519            // Copy the data converting from int to int
520            k = w*h-1;
521            k0 = indb.offset+(h-1)*indb.scanw+w-1;
522            for (i=h-1; i >=0; i--) {
523                for (mink = k-w; k > mink; k--, k0--) {
524                    outdata[k] = (int) (indata[k0]);
525                }
526                // Jump to beggining of previous line in input
527                k0 -= indb.scanw - w;
528            }
529
530            // Set the progressivity and offset
531            blk.progressive = indb.progressive;
532            blk.offset = 0;
533            blk.scanw = w;
534        }
535
536        // If asking a component for the first time for this block,
537        // do transform for the 3 components
538        else if((outdata[c] == null)||
539            (dbi.ulx > blk.ulx) || (dbi.uly > blk.uly) ||
540            (dbi.ulx+dbi.w < blk.ulx+blk.w) ||
541            (dbi.uly+dbi.h < blk.uly+blk.h)) {
542            int k,k0,k1,k2,mink,i;
543            int w = blk.w; //width of output block
544            int h = blk.h; //height of ouput block
545
546            //Reference to output block data array
547            outdata[c] = (int[]) blk.getData();
548
549            //Create data array of blk if necessary
550            if(outdata[c] == null || outdata[c].length!=w*h){
551                outdata[c] = new int[h * w];
552                blk.setData(outdata[c]);
553            }
554
555            outdata[(c+1)%3] = new int[outdata[c].length];
556            outdata[(c+2)%3] = new int[outdata[c].length];
557
558            if(block0==null || block0.getDataType()!=DataBlk.TYPE_FLOAT)
559                block0 = new DataBlkFloat();
560            if(block2==null || block2.getDataType()!=DataBlk.TYPE_FLOAT)
561                block2 = new DataBlkFloat();
562            if(block1==null || block1.getDataType()!=DataBlk.TYPE_FLOAT)
563                block1 = new DataBlkFloat();
564            block0.w = block2.w = block1.w = blk.w;
565            block0.h = block2.h = block1.h = blk.h;
566            block0.ulx = block2.ulx = block1.ulx = blk.ulx;
567            block0.uly = block2.uly = block1.uly = blk.uly;
568
569            float data0[],data1[],data2[]; // input data arrays
570
571            // Fill in buffer blocks (to be read only)
572            // Returned blocks may have different size and position
573            block0 = (DataBlkFloat)src.getInternCompData(block0, 0);
574            data0  = (float[]) block0.getData();
575            block2 = (DataBlkFloat)src.getInternCompData(block2, 1);
576            data2 = (float[]) block2.getData();
577            block1 = (DataBlkFloat)src.getInternCompData(block1, 2);
578            data1 = (float[]) block1.getData();
579
580            // Set the progressiveness of the output data
581            blk.progressive = block0.progressive || block1.progressive ||
582                block2.progressive;
583            blk.offset = 0;
584            blk.scanw = w;
585
586            // set attributes of the DataBlk used for buffering
587            dbi.progressive = blk.progressive;
588            dbi.ulx = blk.ulx;
589            dbi.uly = blk.uly;
590            dbi.w = blk.w;
591            dbi.h = blk.h;
592
593            //Perform conversion
594
595            // Initialize general indexes
596            k = w*h-1;
597            k0 = block0.offset+(h-1)*block0.scanw+w-1;
598            k2 = block2.offset+(h-1)*block2.scanw+w-1;
599            k1 = block1.offset+(h-1)*block1.scanw+w-1;
600
601            for( i = h-1; i >=0; i--){
602                for(mink = k-w; k > mink; k--, k0--, k2--, k1--){
603                    outdata[0][k] = (int)(data0[k0]+1.402f*data1[k1]+0.5f);
604                    outdata[1][k] =
605                        (int) (data0[k0]-0.34413f*data2[k2]-0.71414f*data1[k1]
606                               + 0.5f);
607                    outdata[2][k] = (int)(data0[k0]+1.772f*data2[k2]+0.5f);
608                }
609                // Jump to beggining of previous line in input
610                k0 -= block0.scanw - w;
611                k2 -= block2.scanw - w;
612                k1 -= block1.scanw - w;
613            }
614            outdata[c] = null;
615        }
616        else if((c>=0)&&(c<=3)){//Asking for the 2nd or 3rd block component
617            blk.setData(outdata[c]);
618            blk.progressive = dbi.progressive;
619            blk.offset = (blk.uly-dbi.uly)*dbi.w+blk.ulx-dbi.ulx;
620            blk.scanw = dbi.w;
621            outdata[c] = null;
622        } else {
623            // Requesting a non valid component index
624            throw new IllegalArgumentException();
625        }
626        return blk;
627    }
628
629        /**
630     * Changes the current tile, given the new indexes. An
631     * IllegalArgumentException is thrown if the indexes do not
632     * correspond to a valid tile.
633     *
634     * <P>This default implementation changes the tile in the source
635     * and re-initializes properly component transformation variables..
636     *
637     * @param x The horizontal index of the tile.
638     *
639     * @param y The vertical index of the new tile.
640     *
641     * */
642    public void setTile(int x, int y) {
643        src.setTile(x,y);
644        tIdx = getTileIdx(); // index of the current tile
645
646        // initializations
647        if( ((Integer)cts.getTileDef(tIdx)).intValue()==NONE )
648            transfType = NONE;
649        else {
650            int nc = src.getNumComps()> 3 ? 3 : src.getNumComps();
651            int rev = 0;
652            for(int c=0; c<nc; c++)
653                rev += (wfs.isReversible(tIdx,c)?1:0);
654            if(rev==3){
655                // All WT are reversible
656                transfType = INV_RCT;
657            }
658            else if(rev==0){
659                // All WT irreversible
660                transfType = INV_ICT;
661            }
662            else{
663                // Error
664                throw new IllegalArgumentException("Wavelet transformation and "+
665                                                   "component transformation"+
666                                                   " not coherent in tile"+tIdx);
667            }
668        }
669    }
670
671    /**
672     * Advances to the next tile, in standard scan-line order (by rows
673     * then columns). An NoNextElementException is thrown if the
674     * current tile is the last one (i.e. there is no next tile).
675     *
676     * <P>This default implementation just advances to the next tile
677     * in the source and re-initializes properly component
678     * transformation variables.
679     *
680     * */
681    public void nextTile() {
682        src.nextTile();
683        tIdx = getTileIdx(); // index of the current tile
684
685        // initializations
686        if( ((Integer)cts.getTileDef(tIdx)).intValue()==NONE )
687            transfType = NONE;
688        else {
689            int nc = src.getNumComps() > 3 ? 3 : src.getNumComps();
690            int rev = 0;
691            for(int c=0; c<nc; c++)
692                rev += (wfs.isReversible(tIdx,c)?1:0);
693            if(rev==3){
694                // All WT are reversible
695                transfType = INV_RCT;
696            }
697            else if(rev==0){
698                // All WT irreversible
699                transfType = INV_ICT;
700            }
701            else{
702                // Error
703                throw new IllegalArgumentException("Wavelet transformation and "+
704                                                   "component transformation"+
705                                                   " not coherent in tile"+tIdx);
706            }
707        }
708    }
709
710}
711