001/*
002 * $RCSfile: ImgDataJoiner.java,v $
003 * $Revision: 1.1 $
004 * $Date: 2005/02/11 05:02:13 $
005 * $State: Exp $
006 *
007 * Class:                   ImgDataJoiner
008 *
009 * Description:             Get ImgData from different sources
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;
045import java.awt.Point;
046
047import jj2000.j2k.*;
048
049/**
050 * This class implements the ImgData interface and allows to obtain data from
051 * different sources. Here, one source is represented by an ImgData and a
052 * component index. The typical use of this class is when the encoder needs
053 * different components (Red, Green, Blue, alpha, ...) from different input
054 * files (i.e. from different ImgReader objects).
055 *
056 * <p>All input ImgData must not be tiled (i.e. must have only 1 tile) and the
057 * image origin must be the canvas origin. The different inputs can have
058 * different dimensions though (this will lead to different subsampling
059 * factors for each component).</p>
060 *
061 * <p>The input ImgData and component index list must be defined when
062 * constructing this class and can not be modified later.</p>
063 *
064 * @see ImgData
065 * @see jj2000.j2k.image.input.ImgReader
066 * */
067public class ImgDataJoiner implements BlkImgDataSrc {
068
069    /** The width of the image */
070    private int w;
071
072    /** The height of the image */
073    private int h;
074
075    /** The number of components in the image */
076    private int nc;
077
078    /** The list of input ImgData */
079    private BlkImgDataSrc[] imageData;
080
081    /** The component index associated with each ImgData */
082    private int[] compIdx;
083
084    /** The subsampling factor along the horizontal direction, for every
085     * component */
086    private int[] subsX;
087
088    /** The subsampling factor along the vertical direction, for every
089     * component */
090    private int[] subsY;
091
092    /**
093     * Class constructor. Each input BlkImgDataSrc and its component index
094     * must appear in the order wanted for the output components.<br>
095     *
096     * <u>Example:</u> Reading R,G,B components from 3 PGM files.<br>
097     * <tt>
098     * BlkImgDataSrc[] idList = <br>
099     *   {<br>
100     *   new ImgReaderPGM(new BEBufferedRandomAccessFile("R.pgm", "r")),<br>
101     *   new ImgReaderPGM(new BEBufferedRandomAccessFile("G.pgm", "r")),<br>
102     *   new ImgReaderPGM(new BEBufferedRandomAccessFile("B.pgm", "r"))<br>
103     *   };<br>
104     * int[] compIdx = {0,0,0};<br>
105     * ImgDataJoiner idj = new ImgDataJoiner(idList, compIdx);
106     * </tt>
107     *
108     * <p>Of course, the 2 arrays must have the same length (This length is
109     * the number of output components). The image width and height are
110     * definded to be the maximum values of all the input ImgData.
111     *
112     * @param imD The list of input BlkImgDataSrc in an array.
113     *
114     * @param cIdx The component index associated with each ImgData.
115     * */
116    public ImgDataJoiner(BlkImgDataSrc[] imD, int[] cIdx){
117        int i;
118        int maxW, maxH;
119
120        // Initializes
121        imageData = imD;
122        compIdx = cIdx;
123        if(imageData.length != compIdx.length)
124            throw new IllegalArgumentException("imD and cIdx must have the"+
125                                               " same length");
126
127        nc = imD.length;
128
129        subsX = new int[nc];
130        subsY = new int[nc];
131
132        // Check that no source is tiled and that the image origin is at the
133        // canvas origin.
134        for(i=0; i<nc; i++) {
135            if (imD[i].getNumTiles() != 1 ||
136                imD[i].getCompULX(cIdx[i])!=0 || 
137                imD[i].getCompULY(cIdx[i])!=0) {
138                throw
139                    new IllegalArgumentException("All input components must, "+
140                                                 "not use tiles and must "+
141                                                 "have "+
142                                                 "the origin at the canvas "+
143                                                 "origin");
144            }
145        }
146
147        // Guess component subsampling factors based on the fact that the
148        // ceil() operation relates the reference grid size to the component's
149        // size, through the subsampling factor.
150
151        // Mhhh, difficult problem. For now just assume that one of the
152        // subsampling factors is always 1 and that the component width is
153        // always larger than its subsampling factor, which covers most of the
154        // cases. We check the correctness of the solution once found to chek
155        // out hypothesis.
156
157        // Look for max width and height.
158        maxW = 0;
159        maxH = 0;
160        for(i=0; i<nc; i++) {
161            if(imD[i].getCompImgWidth(cIdx[i]) > maxW)
162                maxW = imD[i].getCompImgWidth(cIdx[i]);
163            if(imD[i].getCompImgHeight(cIdx[i]) > maxH)
164                maxH = imD[i].getCompImgHeight(cIdx[i]);
165        }
166        // Set the image width and height as the maximum ones
167        w = maxW;
168        h = maxH;
169
170        // Now get the sumsampling factors and check the subsampling factors,
171        // just to see if above hypothesis were correct.
172        for (i=0; i<nc; i++) {
173            // This calculation only holds if the subsampling factor is less
174            // than the component width
175            subsX[i] = (maxW + imD[i].getCompImgWidth(cIdx[i])-1) /
176                imD[i].getCompImgWidth(cIdx[i]);
177            subsY[i] = (maxH + imD[i].getCompImgHeight(cIdx[i])-1) /
178                imD[i].getCompImgHeight(cIdx[i]);
179            if ((maxW+subsX[i]-1)/subsX[i] !=
180                imD[i].getCompImgWidth(cIdx[i]) ||
181                (maxH+subsY[i]-1)/subsY[i] !=
182                imD[i].getCompImgHeight(cIdx[i])) {
183                throw new Error("Can not compute component subsampling "+
184                                "factors: strange subsampling.");
185            }
186        }
187    }
188
189    /**
190     * Returns the overall width of the current tile in pixels. This is the
191     * tile's width without accounting for any component subsampling.
192     *
193     * @return The total current tile's width in pixels.
194     * */
195    public int getTileWidth(){
196        return w;
197    }
198
199    /**
200     * Returns the overall height of the current tile in pixels. This is the
201     * tile's height without accounting for any component subsampling.
202     *
203     * @return The total current tile's height in pixels.
204     * */
205    public int getTileHeight(){
206        return h;
207    }
208
209    /** Returns the nominal tiles width */
210    public int getNomTileWidth() {
211        return w;
212    }
213
214    /** Returns the nominal tiles height */
215    public int getNomTileHeight() {
216        return h;
217    }
218
219    /**
220     * Returns the overall width of the image in pixels. This is the image's
221     * width without accounting for any component subsampling or tiling.
222     *
223     * @return The total image's width in pixels.
224     * */
225    public int getImgWidth(){
226        return w;
227    }
228
229    /**
230     * Returns the overall height of the image in pixels. This is the image's
231     * height without accounting for any component subsampling or tiling.
232     *
233     * @return The total image's height in pixels.
234     * */
235    public int getImgHeight(){
236        return h;
237    }
238
239    /**
240     * Returns the number of components in the image.
241     *
242     * @return The number of components in the image.
243     * */
244    public int getNumComps(){
245        return nc;
246    }
247
248    /**
249     * Returns the component subsampling factor in the horizontal direction,
250     * for the specified component. This is, approximately, the ratio of
251     * dimensions between the reference grid and the component itself, see the
252     * 'ImgData' interface desription for details.
253     *
254     * @param c The index of the component (between 0 and N-1)
255     *
256     * @return The horizontal subsampling factor of component 'c'
257     *
258     * @see ImgData
259     * */
260    public int getCompSubsX(int c) {
261        return subsX[c];
262    }
263
264    /**
265     * Returns the component subsampling factor in the vertical direction, for
266     * the specified component. This is, approximately, the ratio of
267     * dimensions between the reference grid and the component itself, see the
268     * 'ImgData' interface desription for details.
269     *
270     * @param c The index of the component (between 0 and N-1)
271     *
272     * @return The vertical subsampling factor of component 'c'
273     *
274     * @see ImgData
275     * */
276    public int getCompSubsY(int c) {
277        return subsY[c];
278    }
279
280
281    /**
282     * Returns the width in pixels of the specified tile-component
283     *
284     * @param t Tile index
285     *
286     * @param c The index of the component, from 0 to N-1.
287     *
288     * @return The width in pixels of component <tt>c</tt> in tile<tt>t</tt>.
289     * */
290    public int getTileCompWidth(int t,int c){
291        return imageData[c].getTileCompWidth(t,compIdx[c]);
292    }
293
294    /**
295     * Returns the height in pixels of the specified tile-component.
296     *
297     * @param t The tile index.
298     *
299     * @param c The index of the component, from 0 to N-1.
300     *
301     * @return The height in pixels of component <tt>c</tt> in the current
302     * tile.
303     * */
304    public int getTileCompHeight(int t,int c){
305        return imageData[c].getTileCompHeight(t,compIdx[c]);
306    }
307
308    /**
309     * Returns the width in pixels of the specified component in the overall
310     * image.
311     *
312     * @param c The index of the component, from 0 to N-1.
313     *
314     * @return The width in pixels of component <tt>c</tt> in the overall
315     * image.
316     * */
317    public int getCompImgWidth(int c){
318        return imageData[c].getCompImgWidth(compIdx[c]);
319    }
320
321    /**
322     * Returns the height in pixels of the specified component in the
323     * overall image.
324     *
325     * @param n The index of the component, from 0 to N-1.
326     *
327     * @return The height in pixels of component <tt>n</tt> in the overall
328     * image.
329     *
330     *
331     * */
332    public int getCompImgHeight(int n){
333        return imageData[n].getCompImgHeight(compIdx[n]);       
334    }
335
336    /**
337     * Returns the number of bits, referred to as the "range bits",
338     * corresponding to the nominal range of the data in the specified
339     * component. If this number is <i>b</b> then for unsigned data the
340     * nominal range is between 0 and 2^b-1, and for signed data it is between
341     * -2^(b-1) and 2^(b-1)-1. For floating point data this value is not
342     * applicable.
343     *
344     * @param c The index of the component.
345     *
346     * @return The number of bits corresponding to the nominal range of the
347     * data. Fro floating-point data this value is not applicable and the
348     * return value is undefined.
349     * */
350    public int getNomRangeBits(int c){
351        return imageData[c].getNomRangeBits(compIdx[c]);
352    }
353
354    /**
355     * Returns the position of the fixed point in the specified
356     * component. This is the position of the least significant integral
357     * (i.e. non-fractional) bit, which is equivalent to the number of
358     * fractional bits. For instance, for fixed-point values with 2 fractional
359     * bits, 2 is returned. For floating-point data this value does not apply
360     * and 0 should be returned. Position 0 is the position of the least
361     * significant bit in the data.
362     *
363     * @param c The index of the component.
364     *
365     * @return The position of the fixed-point, which is the same as the
366     * number of fractional bits. For floating-point data 0 is returned.
367     * */
368    public int getFixedPoint(int c){
369        return imageData[c].getFixedPoint(compIdx[c]);
370    }
371
372    /**
373     * Returns, in the blk argument, a block of image data containing the
374     * specifed rectangular area, in the specified component. The data is
375     * returned, as a reference to the internal data, if any, instead of as a
376     * copy, therefore the returned data should not be modified.
377     *
378     * <P>The rectangular area to return is specified by the 'ulx', 'uly', 'w'
379     * and 'h' members of the 'blk' argument, relative to the current
380     * tile. These members are not modified by this method. The 'offset' and
381     * 'scanw' of the returned data can be arbitrary. See the 'DataBlk' class.
382     *
383     * <P>This method, in general, is more efficient than the 'getCompData()'
384     * method since it may not copy the data. However if the array of returned
385     * data is to be modified by the caller then the other method is probably
386     * preferable.
387     *
388     * <P>If the data array in <tt>blk</tt> is <tt>null</tt>, then a new one
389     * is created if necessary. The implementation of this interface may
390     * choose to return the same array or a new one, depending on what is more
391     * efficient. Therefore, the data array in <tt>blk</tt> prior to the
392     * method call should not be considered to contain the returned data, a
393     * new array may have been created. Instead, get the array from
394     * <tt>blk</tt> after the method has returned.
395     *
396     * <P>The returned data may have its 'progressive' attribute set. In this
397     * case the returned data is only an approximation of the "final" data.
398     *
399     * @param blk Its coordinates and dimensions specify the area to return,
400     * relative to the current tile. Some fields in this object are modified
401     * to return the data.
402     *
403     * @param c The index of the component from which to get the data.
404     *
405     * @return The requested DataBlk
406     *
407     * @see #getCompData
408     * */
409    public DataBlk getInternCompData(DataBlk blk, int c){
410        return imageData[c].getInternCompData(blk, compIdx[c]);
411    }
412
413    /**
414     * Returns, in the blk argument, a block of image data containing the
415     * specifed rectangular area, in the specified component. The data is
416     * returned, as a copy of the internal data, therefore the returned data
417     * can be modified "in place".
418     *
419     * <P>The rectangular area to return is specified by the 'ulx', 'uly', 'w'
420     * and 'h' members of the 'blk' argument, relative to the current
421     * tile. These members are not modified by this method. The 'offset' of
422     * the returned data is 0, and the 'scanw' is the same as the block's
423     * width. See the 'DataBlk' class.
424     *
425     * <P>This method, in general, is less efficient than the
426     * 'getInternCompData()' method since, in general, it copies the
427     * data. However if the array of returned data is to be modified by the
428     * caller then this method is preferable.
429     *
430     * <P>If the data array in 'blk' is 'null', then a new one is created. If
431     * the data array is not 'null' then it is reused, and it must be large
432     * enough to contain the block's data. Otherwise an 'ArrayStoreException'
433     * or an 'IndexOutOfBoundsException' is thrown by the Java system.
434     *
435     * <P>The returned data may have its 'progressive' attribute set. In this
436     * case the returned data is only an approximation of the "final" data.
437     *
438     * @param blk Its coordinates and dimensions specify the area to return,
439     * relative to the current tile. If it contains a non-null data array,
440     * then it must be large enough. If it contains a null data array a new
441     * one is created. Some fields in this object are modified to return the
442     * data.
443     *
444     * @param c The index of the component from which to get the data.
445     *
446     * @return The requested DataBlk
447     *
448     * @see #getInternCompData
449     * */
450    public DataBlk getCompData(DataBlk blk, int c){
451        return imageData[c].getCompData(blk, compIdx[c]);
452    }
453
454    /**
455     * Changes the current tile, given the new coordinates. An
456     * IllegalArgumentException is thrown if the coordinates do not correspond
457     * to a valid tile.
458     *
459     * @param x The horizontal coordinate of the tile.
460     *
461     * @param y The vertical coordinate of the new tile.
462     * */
463    public void setTile(int x, int y){
464        if (x!=0 || y != 0) {
465            throw new IllegalArgumentException();
466        }
467    }
468
469    /**
470     * Advances to the next tile, in standard scan-line order (by rows then
471     * columns). A NoNextElementException is thrown if the current tile is the
472     * last one (i.e. there is no next tile). This default implementation
473     * assumes no tiling, so NoNextElementException() is always thrown.
474     * */
475    public void nextTile() {
476        throw new NoNextElementException();
477    }
478    
479    /**
480     * Returns the coordinates of the current tile. This default
481     * implementation assumes no-tiling, so (0,0) is returned.
482     *
483     * @param co If not null this object is used to return the information. If
484     * null a new one is created and returned.
485     *
486     * @return The current tile's coordinates.
487     * */
488    public Point getTile(Point co) {
489        if (co != null) {
490            co.x = 0;
491            co.y = 0;
492            return co;
493        }
494        else {
495            return new Point(0,0);
496        }
497    }
498    
499    /**
500     * Returns the index of the current tile, relative to a standard scan-line
501     * order. This default implementations assumes no tiling, so 0 is always
502     * returned.
503     *
504     * @return The current tile's index (starts at 0).
505     * */
506    public int getTileIdx() {
507        return 0;
508    }
509
510    /**
511     * Returns the horizontal coordinate of the upper-left corner of the
512     * specified component in the current tile.
513     *
514     * @param c The component index.
515     * */
516    public int getCompULX(int c) {
517        return 0;
518    }
519
520    /**
521     * Returns the vertical coordinate of the upper-left corner of the
522     * specified component in the current tile.
523     *
524     * @param c The component index.
525     * */
526    public int getCompULY(int c) {
527        return 0;
528    }
529
530    /** Returns the horizontal tile partition offset in the reference grid */
531    public int getTilePartULX() {
532        return 0;
533    }
534
535    /** Returns the vertical tile partition offset in the reference grid */
536    public int getTilePartULY() {
537        return 0;
538    }
539
540    /**
541     * Returns the horizontal coordinate of the image origin, the top-left
542     * corner, in the canvas system, on the reference grid.
543     *
544     * @return The horizontal coordinate of the image origin in the canvas
545     * system, on the reference grid.
546     * */
547    public int getImgULX() {
548        return 0;
549    }
550
551    /**
552     * Returns the vertical coordinate of the image origin, the top-left
553     * corner, in the canvas system, on the reference grid.
554     *
555     * @return The vertical coordinate of the image origin in the canvas
556     * system, on the reference grid.
557     * */
558    public int getImgULY() {
559        return 0;
560    }
561
562    /**
563     * Returns the number of tiles in the horizontal and vertical
564     * directions. This default implementation assumes no tiling, so (1,1) is
565     * always returned.
566     *
567     * @param co If not null this object is used to return the information. If
568     * null a new one is created and returned.
569     *
570     * @return The number of tiles in the horizontal (Point.x) and vertical
571     * (Point.y) directions.
572     * */
573    public Point getNumTiles(Point co) {
574        if (co != null) {
575            co.x = 1;
576            co.y = 1;
577            return co;
578        }
579        else {
580            return new Point(1,1);
581        }
582    }
583
584    /**
585     * Returns the total number of tiles in the image. This default
586     * implementation assumes no tiling, so 1 is always returned.
587     *
588     * @return The total number of tiles in the image.
589     * */
590    public int getNumTiles() {
591        return 1;
592    }
593
594    /**
595     * Returns a string of information about the object, more than 1 line
596     * long. The information string includes information from the several
597     * input ImgData (their toString() method are called one after the other).
598     *
599     * @return A string of information about the object.
600     * */
601    public String toString() {
602        String string = "ImgDataJoiner: WxH = " + w + "x" + h;
603        for(int i=0; i<nc; i++){
604            string += "\n- Component "+i+" "+imageData[i];
605        }
606        return string;
607    }    
608}