001/*
002 * $RCSfile: InvWTFull.java,v $
003 * $Revision: 1.1 $
004 * $Date: 2005/02/11 05:02:32 $
005 * $State: Exp $
006 *
007 * Class:                   InvWTFull
008 *
009 * Description:             This class implements a full page inverse DWT for
010 *                          int and float data.
011 *
012 *                          the InvWTFullInt and InvWTFullFloat
013 *                          classes by Bertrand Berthelot, Apr-19-1999
014 *
015 *
016 * COPYRIGHT:
017 *
018 * This software module was originally developed by Raphaël Grosbois and
019 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
020 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
021 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
022 * Centre France S.A) in the course of development of the JPEG2000
023 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
024 * software module is an implementation of a part of the JPEG 2000
025 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
026 * Systems AB and Canon Research Centre France S.A (collectively JJ2000
027 * Partners) agree not to assert against ISO/IEC and users of the JPEG
028 * 2000 Standard (Users) any of their rights under the copyright, not
029 * including other intellectual property rights, for this software module
030 * with respect to the usage by ISO/IEC and Users of this software module
031 * or modifications thereof for use in hardware or software products
032 * claiming conformance to the JPEG 2000 Standard. Those intending to use
033 * this software module in hardware or software products are advised that
034 * their use may infringe existing patents. The original developers of
035 * this software module, JJ2000 Partners and ISO/IEC assume no liability
036 * for use of this software module or modifications thereof. No license
037 * or right to this software module is granted for non JPEG 2000 Standard
038 * conforming products. JJ2000 Partners have full right to use this
039 * software module for his/her own purpose, assign or donate this
040 * software module to any third party and to inhibit third parties from
041 * using this software module for non JPEG 2000 Standard conforming
042 * products. This copyright notice must be included in all copies or
043 * derivative works of this software module.
044 *
045 * Copyright (c) 1999/2000 JJ2000 Partners.
046 * */
047package jj2000.j2k.wavelet.synthesis;
048import java.awt.Point;
049
050import jj2000.j2k.wavelet.*;
051import jj2000.j2k.decoder.*;
052import jj2000.j2k.image.*;
053import jj2000.j2k.util.*;
054
055/**
056 * This class implements the InverseWT with the full-page approach for int and
057 * float data.
058 *
059 * <P>The image can be reconstructed at different (image) resolution levels
060 * indexed from the lowest resolution available for each tile-component. This
061 * is controlled by the setImgResLevel() method.
062 *
063 * <P>Note: Image resolution level indexes may differ from tile-component
064 * resolution index. They are indeed indexed starting from the lowest number
065 * of decomposition levels of each component of each tile.
066 *
067 * <P>Example: For an image (1 tile) with 2 components (component 0 having 2
068 * decomposition levels and component 1 having 3 decomposition levels), the
069 * first (tile-) component has 3 resolution levels and the second one has 4
070 * resolution levels, whereas the image has only 3 resolution levels
071 * available.
072 *
073 * <P>This implementation does not support progressive data, all data is
074 * considered to be non-progressive (i.e. "final" data) and the 'progressive'
075 * attribute of the 'DataBlk' class is always set to false, see the 'DataBlk'
076 * class.
077 *
078 * @see DataBlk
079 * */
080public class InvWTFull extends InverseWT {
081
082    /** Reference to the ProgressWatch instance if any */
083    private ProgressWatch pw = null;
084
085    /** The total number of code-blocks to decode */
086    private int cblkToDecode = 0;
087
088    /** The number of already decoded code-blocks */
089    private int nDecCblk = 0;
090
091    /** the code-block buffer's source i.e. the quantizer */
092    private CBlkWTDataSrcDec src;
093
094    /** Current data type */
095    private int dtype;
096
097    /**
098     * block storing the reconstructed image for each component
099     */
100    private DataBlk reconstructedComps[];
101
102    /** Number of decomposition levels in each component */
103    private int[] ndl;
104
105    /**
106     * The reversible flag for each component in each tile. The first index is
107     * the tile index, the second one is the component index. The
108     * reversibility of the components for each tile are calculated on a as
109     * needed basis.
110     * */
111    private boolean reversible[][];
112
113    /**
114     * Initializes this object with the given source of wavelet
115     * coefficients. It initializes the resolution level for full resolutioin
116     * reconstruction.
117     *
118     * @param src from where the wavelet coefficinets should be
119     * obtained.
120     *
121     * @param decSpec The decoder specifications
122     * */
123    public InvWTFull(CBlkWTDataSrcDec src, DecoderSpecs decSpec){
124        super(src,decSpec);
125        this.src = src;
126
127        int nc = src.getNumComps();
128        reconstructedComps = new DataBlk[nc];
129        ndl = new int[nc];
130        pw = FacilityManager.getProgressWatch();
131    }
132
133   /**
134     * Returns the reversibility of the current subband. It computes
135     * iteratively the reversibility of the child subbands. For each subband
136     * it tests the reversibility of the horizontal and vertical synthesis
137     * filters used to reconstruct this subband.
138     *
139     * @param subband The current subband.
140     *
141     * @return true if all the  filters used to reconstruct the current 
142     * subband are reversible
143     * */
144    private boolean isSubbandReversible(Subband subband) {
145        if(subband.isNode) {
146            // It's reversible if the filters to obtain the 4 subbands are
147            // reversible and the ones for this one are reversible too.
148            return
149                isSubbandReversible(subband.getLL()) &&
150                isSubbandReversible(subband.getHL()) &&
151                isSubbandReversible(subband.getLH()) &&
152                isSubbandReversible(subband.getHH()) &&
153                ((SubbandSyn)subband).hFilter.isReversible() &&
154                ((SubbandSyn)subband).vFilter.isReversible();
155        } else {
156            // Leaf subband. Reversibility of data depends on source, so say
157            // it's true
158            return true;
159        }
160    }
161
162
163    /**
164     * Returns the reversibility of the wavelet transform for the specified
165     * component, in the current tile. A wavelet transform is reversible when
166     * it is suitable for lossless and lossy-to-lossless compression.
167     *
168     * @param t The index of the tile.
169     *
170     * @param c The index of the component.
171     *
172     * @return true is the wavelet transform is reversible, false if not.
173     * */
174    public boolean isReversible(int t,int c) {
175        if (reversible[t] == null) {
176            // Reversibility not yet calculated for this tile
177            reversible[t] = new boolean[getNumComps()];
178            for (int i=reversible.length-1; i>=0 ; i--) {
179                reversible[t][i] =
180                    isSubbandReversible(src.getSynSubbandTree(t,i));
181            }
182        }
183        return reversible[t][c];
184    }
185
186
187    /**
188     * Returns the number of bits, referred to as the "range bits",
189     * corresponding to the nominal range of the data in the specified
190     * component.
191     *
192     * <P>The returned value corresponds to the nominal dynamic range of the
193     * reconstructed image data, as long as the getNomRangeBits() method of
194     * the source returns a value corresponding to the nominal dynamic range
195     * of the image data and not not of the wavelet coefficients.
196     *
197     * <P>If this number is <i>b</b> then for unsigned data the nominal range
198     * is between 0 and 2^b-1, and for signed data it is between -2^(b-1) and
199     * 2^(b-1)-1.
200     *
201     * @param c The index of the component.
202     *
203     * @return The number of bits corresponding to the nominal range of the
204     * data.
205     * */
206    public int getNomRangeBits(int c) {
207        return src.getNomRangeBits(c);
208    }
209
210    /**
211     * Returns the position of the fixed point in the specified
212     * component. This is the position of the least significant integral
213     * (i.e. non-fractional) bit, which is equivalent to the number of
214     * fractional bits. For instance, for fixed-point values with 2 fractional
215     * bits, 2 is returned. For floating-point data this value does not apply
216     * and 0 should be returned. Position 0 is the position of the least
217     * significant bit in the data.
218     *
219     * <P>This default implementation assumes that the wavelet transform does
220     * not modify the fixed point. If that were the case this method should be
221     * overriden.
222     *
223     * @param c The index of the component.
224     *
225     * @return The position of the fixed-point, which is the same as the
226     * number of fractional bits. For floating-point data 0 is returned.
227     * */
228    public int getFixedPoint(int c) {
229        return src.getFixedPoint(c);
230    }
231
232    /**
233     * Returns a block of image data containing the specifed rectangular area,
234     * in the specified component, as a reference to the internal buffer (see
235     * below). The rectangular area is specified by the coordinates and
236     * dimensions of the 'blk' object.
237     *
238     * <p>The area to return is specified by the 'ulx', 'uly', 'w' and 'h'
239     * members of the 'blk' argument. These members are not modified by this
240     * method.</p>
241     *
242     * <p>The data returned by this method can be the data in the internal
243     * buffer of this object, if any, and thus can not be modified by the
244     * caller. The 'offset' and 'scanw' of the returned data can be
245     * arbitrary. See the 'DataBlk' class.</p>
246     *
247     * <p>The returned data has its 'progressive' attribute unset
248     * (i.e. false).</p>
249     *
250     * @param blk Its coordinates and dimensions specify the area to return.
251     *
252     * @param c The index of the component from which to get the data.
253     *
254     * @return The requested DataBlk
255     *
256     * @see #getInternCompData
257     * */
258    public final DataBlk getInternCompData(DataBlk blk, int c) {
259        int tIdx = getTileIdx();
260        if(src.getSynSubbandTree(tIdx,c).getHorWFilter()==null) {
261            dtype = DataBlk.TYPE_INT;
262        } else {
263            dtype =
264                src.getSynSubbandTree(tIdx,c).getHorWFilter().getDataType();
265        }
266
267        //If the source image has not been decomposed 
268        if(reconstructedComps[c]==null) {
269            //Allocate component data buffer
270            switch (dtype) {
271            case DataBlk.TYPE_FLOAT:
272                reconstructedComps[c] =
273                    new DataBlkFloat(0,0,getTileCompWidth(tIdx,c),
274                                     getTileCompHeight(tIdx,c));
275                break;
276            case DataBlk.TYPE_INT:
277                reconstructedComps[c] =
278                    new DataBlkInt(0,0,getTileCompWidth(tIdx,c),
279                                   getTileCompHeight(tIdx,c));
280                break;
281            }
282            //Reconstruct source image
283            waveletTreeReconstruction(reconstructedComps[c],
284                                      src.getSynSubbandTree(tIdx,c),c);
285            if(pw!=null && c==src.getNumComps()-1) {
286                pw.terminateProgressWatch();
287            }
288        }
289
290        if(blk.getDataType()!=dtype) {
291            if(dtype==DataBlk.TYPE_INT) {
292                blk = new DataBlkInt(blk.ulx,blk.uly,blk.w,blk.h);
293            } else {
294                blk = new DataBlkFloat(blk.ulx,blk.uly,blk.w,blk.h);
295            }
296        }
297        // Set the reference to the internal buffer
298        blk.setData(reconstructedComps[c].getData());
299        blk.offset = reconstructedComps[c].w*blk.uly+blk.ulx;
300        blk.scanw = reconstructedComps[c].w;
301        blk.progressive = false;
302        return blk;
303    }
304
305    /**
306     * Returns a block of image data containing the specifed rectangular area,
307     * in the specified component, as a copy (see below). The rectangular area
308     * is specified by the coordinates and dimensions of the 'blk' object.
309     *
310     * <P>The area to return is specified by the 'ulx', 'uly', 'w' and 'h'
311     * members of the 'blk' argument. These members are not modified by this
312     * method.
313     *
314     * <P>The data returned by this method is always a copy of the internal
315     * data of this object, if any, and it can be modified "in place" without
316     * any problems after being returned. The 'offset' of the returned data is
317     * 0, and the 'scanw' is the same as the block's width. See the 'DataBlk'
318     * class.
319     *
320     * <P>If the data array in 'blk' is <tt>null</tt>, then a new one is
321     * created. If the data array is not <tt>null</tt> then it must be big
322     * enough to contain the requested area.
323     *
324     * <P>The returned data always has its 'progressive' attribute unset (i.e
325     * false)
326     *
327     * @param blk Its coordinates and dimensions specify the area to
328     * return. If it contains a non-null data array, then it must be large
329     * enough. If it contains a null data array a new one is created. The
330     * fields in this object are modified to return the data.
331     *
332     * @param c The index of the component from which to get the data.
333     *
334     * @return The requested DataBlk
335     *
336     * @see #getCompData
337     * */
338    public DataBlk getCompData(DataBlk blk, int c) {
339        int j;
340        Object src_data,dst_data;
341        int src_data_int[],dst_data_int[];
342        float src_data_float[],dst_data_float[];
343
344        // To keep compiler happy
345        dst_data = null;
346
347        // Ensure output buffer
348        switch (blk.getDataType()) {
349        case DataBlk.TYPE_INT:
350            dst_data_int = (int[]) blk.getData();
351            if (dst_data_int == null || dst_data_int.length < blk.w*blk.h) {
352                dst_data_int = new int[blk.w*blk.h];
353            }
354            dst_data = dst_data_int;
355            break;
356        case DataBlk.TYPE_FLOAT:
357            dst_data_float = (float[]) blk.getData();
358            if (dst_data_float == null || dst_data_float.length < blk.w*blk.h) {
359                dst_data_float = new float[blk.w*blk.h];
360            }
361            dst_data = dst_data_float;
362            break;
363        }
364
365        // Use getInternCompData() to get the data, since getInternCompData()
366        // returns reference to internal buffer, we must copy it.
367        blk = getInternCompData(blk,c);
368
369        // Copy the data
370        blk.setData(dst_data);
371        blk.offset = 0;
372        blk.scanw = blk.w;
373        return blk;
374    }
375
376    /**
377     * Performs the 2D inverse wavelet transform on a subband of the image, on
378     * the specified component. This method will successively perform 1D
379     * filtering steps on all columns and then all lines of the subband.
380     *
381     * @param db the buffer for the image/wavelet data.
382     *
383     * @param sb The subband to reconstruct.
384     *
385     * @param c The index of the component to reconstruct 
386     * */
387    private void wavelet2DReconstruction(DataBlk db,SubbandSyn sb,int c) {
388        Object data;
389        Object buf;
390        int ulx, uly, w, h;
391        int i,j,k;
392        int offset;
393
394        // If subband is empty (i.e. zero size) nothing to do
395        if (sb.w==0 || sb.h==0) {
396            return;
397        }
398
399        data = db.getData();
400
401        ulx = sb.ulx;
402        uly = sb.uly;
403        w = sb.w;
404        h = sb.h;
405
406        buf = null;  // To keep compiler happy
407
408        switch (sb.getHorWFilter().getDataType()) {
409        case DataBlk.TYPE_INT:
410            buf = new int[(w>=h) ? w : h];
411            break;
412        case DataBlk.TYPE_FLOAT:
413            buf = new float[(w>=h) ? w : h];
414            break;
415        }
416
417        //Perform the horizontal reconstruction
418        offset = (uly-db.uly)*db.w + ulx-db.ulx;
419        if (sb.ulcx%2==0) { // start index is even => use LPF
420            for(i=0; i<h; i++, offset += db.w) {
421                System.arraycopy(data,offset,buf,0,w);
422                sb.hFilter.synthetize_lpf(buf,0,(w+1)/2,1,buf,(w+1)/2,w/2,1,
423                                          data,offset,1);
424            }
425        } else { // start index is odd => use HPF
426            for(i=0; i<h; i++, offset += db.w) {
427                System.arraycopy(data,offset,buf,0,w);
428                sb.hFilter.synthetize_hpf(buf,0,w/2,1,buf,w/2,(w+1)/2,1,
429                                          data,offset,1);
430            }
431        }
432
433        //Perform the vertical reconstruction 
434        offset = (uly-db.uly)*db.w+ulx-db.ulx;
435        switch (sb.getVerWFilter().getDataType()) {
436        case DataBlk.TYPE_INT:
437            int data_int[], buf_int[];
438            data_int = (int[]) data;
439            buf_int = (int[]) buf;
440            if (sb.ulcy%2==0) { // start index is even => use LPF
441                for(j=0; j<w; j++, offset++) {
442                    for(i=h-1, k=offset+i*db.w; i>=0; i--, k-=db.w)
443                        buf_int[i] = data_int[k];
444                    sb.vFilter.synthetize_lpf(buf,0,(h+1)/2,1,buf,(h+1)/2,
445                                              h/2,1,data,offset,db.w);
446                }
447            } else { // start index is odd => use HPF
448                for(j=0; j<w; j++, offset++) {
449                    for(i=h-1, k=offset+i*db.w; i>=0; i--, k-= db.w)
450                        buf_int[i] = data_int[k];
451                    sb.vFilter.synthetize_hpf(buf,0,h/2,1,buf,h/2,(h+1)/2,1,
452                                              data,offset,db.w);
453                }
454            }
455            break;
456        case DataBlk.TYPE_FLOAT:
457            float data_float[], buf_float[];
458            data_float = (float[]) data;
459            buf_float = (float[]) buf;
460            if (sb.ulcy%2==0) { // start index is even => use LPF
461                for(j=0; j<w; j++, offset++) {
462                    for(i=h-1, k=offset+i*db.w; i>=0; i--, k-= db.w)
463                        buf_float[i] = data_float[k];
464                    sb.vFilter.synthetize_lpf(buf,0,(h+1)/2,1,buf,(h+1)/2,
465                                              h/2,1,data,offset,db.w);
466                }
467            } else { // start index is odd => use HPF
468                for(j=0; j<w; j++, offset++) {
469                    for(i=h-1, k=offset+i*db.w; i>=0; i--, k-= db.w)
470                        buf_float[i] = data_float[k];
471                    sb.vFilter.synthetize_hpf(buf,0,h/2,1,buf,h/2,(h+1)/2,1,
472                                              data,offset,db.w);
473                }
474            }
475            break;
476        }
477    }
478
479    /**
480     * Performs the inverse wavelet transform on the whole component. It
481     * iteratively reconstructs the subbands from leaves up to the root
482     * node. This method is recursive, the first call to it the 'sb' must be
483     * the root of the subband tree. The method will then process the entire
484     * subband tree by calling itslef recursively.
485     *
486     * @param img The buffer for the image/wavelet data.
487     *
488     * @param sb The subband to reconstruct.
489     *
490     * @param c The index of the component to reconstruct 
491     * */
492    private void waveletTreeReconstruction(DataBlk img,SubbandSyn sb,int c) {
493
494        DataBlk subbData;
495
496        // If the current subband is a leaf then get the data from the source
497        if(!sb.isNode) {
498            int i,m,n;
499            Object src_data,dst_data;
500            Point ncblks;
501
502            if (sb.w==0 || sb.h==0) {
503                return; // If empty subband do nothing
504            }
505
506            // Get all code-blocks in subband
507            if(dtype==DataBlk.TYPE_INT) {
508                subbData = new DataBlkInt();
509            } else {
510                subbData = new DataBlkFloat();
511            }
512            ncblks = sb.numCb;
513            dst_data = img.getData();
514            for (m=0; m<ncblks.y; m++) {
515                for (n=0; n<ncblks.x; n++) {
516                    subbData = src.getInternCodeBlock(c,m,n,sb,subbData);
517                    src_data = subbData.getData();
518                    if(pw!=null) {
519                        nDecCblk++;
520                        pw.updateProgressWatch(nDecCblk,null);
521                    }
522                    // Copy the data line by line
523                    for (i=subbData.h-1; i>=0; i--) {
524                        System.arraycopy(src_data,
525                                         subbData.offset+i*subbData.scanw,
526                                         dst_data,
527                                         (subbData.uly+i)*img.w+subbData.ulx,
528                                         subbData.w);
529                    }
530                }
531            }
532        } else if(sb.isNode) {
533            // Reconstruct the lower resolution levels if the current subbands
534            // is a node
535
536            //Perform the reconstruction of the LL subband
537            waveletTreeReconstruction(img,(SubbandSyn)sb.getLL(),c);
538
539            if(sb.resLvl<=reslvl-maxImgRes+ndl[c]){
540                //Reconstruct the other subbands
541                waveletTreeReconstruction(img,(SubbandSyn)sb.getHL(),c);
542                waveletTreeReconstruction(img,(SubbandSyn)sb.getLH(),c);
543                waveletTreeReconstruction(img,(SubbandSyn)sb.getHH(),c);
544
545                //Perform the 2D wavelet decomposition of the current subband
546                wavelet2DReconstruction(img,(SubbandSyn)sb,c);
547            }
548        }
549    }
550
551    /**
552     * Returns the implementation type of this wavelet transform, WT_IMPL_FULL
553     * (full-page based transform). All components return the same.
554     *
555     * @param c The index of the component.
556     *
557     * @return WT_IMPL_FULL
558     *
559     * @see WaveletTransform#WT_IMPL_FULL
560     * */
561    public int getImplementationType(int c) {
562        return WaveletTransform.WT_IMPL_FULL;
563    }
564
565    /**
566     * Changes the current tile, given the new indexes. An
567     * IllegalArgumentException is thrown if the indexes do not correspond to
568     * a valid tile.
569     *
570     * @param x The horizontal index of the tile.
571     *
572     * @param y The vertical index of the new tile.
573     * */
574    public void setTile(int x,int y) {
575        int i;
576
577        // Change tile
578        super.setTile(x,y);
579
580        int nc = src.getNumComps();
581        int tIdx = src.getTileIdx();
582        for(int c=0; c<nc; c++) {
583            ndl[c] = src.getSynSubbandTree(tIdx,c).resLvl;
584        }
585
586        // Reset the decomposed component buffers.
587        if (reconstructedComps != null) {
588            for (i=reconstructedComps.length-1; i>=0; i--) {
589                reconstructedComps[i] = null;
590            }
591        }
592
593        cblkToDecode = 0;
594        SubbandSyn root,sb;
595        for(int c=0; c<nc; c++) {
596            root = src.getSynSubbandTree(tIdx,c);
597            for(int r=0; r<=reslvl-maxImgRes+root.resLvl; r++) {
598                if(r==0) {
599                    sb = (SubbandSyn)root.getSubbandByIdx(0,0);
600                    if(sb!=null) cblkToDecode += sb.numCb.x*sb.numCb.y;
601                } else {
602                    sb = (SubbandSyn)root.getSubbandByIdx(r,1);
603                    if(sb!=null) cblkToDecode += sb.numCb.x*sb.numCb.y;
604                    sb = (SubbandSyn)root.getSubbandByIdx(r,2);
605                    if(sb!=null) cblkToDecode += sb.numCb.x*sb.numCb.y;
606                    sb = (SubbandSyn)root.getSubbandByIdx(r,3);
607                    if(sb!=null) cblkToDecode += sb.numCb.x*sb.numCb.y;
608                }
609            } // Loop on resolution levels
610        } // Loop on components
611        nDecCblk = 0;
612
613        if(pw!=null) {
614            pw.initProgressWatch(0,cblkToDecode,"Decoding tile "+tIdx+"...");
615        }
616    }
617
618    /**
619     * Advances to the next tile, in standard scan-line order (by rows then
620     * columns). An 'NoNextElementException' is thrown if the current tile is
621     * the last one (i.e. there is no next tile).
622     * */
623    public void nextTile() {
624        int i;
625
626        // Change tile
627        super.nextTile();
628
629        int nc = src.getNumComps();
630        int tIdx = src.getTileIdx();
631        for(int c=0; c<nc; c++) {
632            ndl[c] = src.getSynSubbandTree(tIdx,c).resLvl;
633        }
634
635        // Reset the decomposed component buffers.
636        if (reconstructedComps != null) {
637            for (i=reconstructedComps.length-1; i>=0; i--) {
638                reconstructedComps[i] = null;
639            }
640        }
641    }
642
643}