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