001/*
002 * $RCSfile: Tiler.java,v $
003 * $Revision: 1.1 $
004 * $Date: 2005/02/11 05:02:13 $
005 * $State: Exp $
006 *
007 * Class:                   Tiler
008 *
009 * Description:             An object to create TiledImgData from
010 *                          ImgData
011 *
012 *
013 *
014 * COPYRIGHT:
015 *
016 * This software module was originally developed by Raphaël Grosbois and
017 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
018 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
019 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
020 * Centre France S.A) in the course of development of the JPEG2000
021 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
022 * software module is an implementation of a part of the JPEG 2000
023 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
024 * Systems AB and Canon Research Centre France S.A (collectively JJ2000
025 * Partners) agree not to assert against ISO/IEC and users of the JPEG
026 * 2000 Standard (Users) any of their rights under the copyright, not
027 * including other intellectual property rights, for this software module
028 * with respect to the usage by ISO/IEC and Users of this software module
029 * or modifications thereof for use in hardware or software products
030 * claiming conformance to the JPEG 2000 Standard. Those intending to use
031 * this software module in hardware or software products are advised that
032 * their use may infringe existing patents. The original developers of
033 * this software module, JJ2000 Partners and ISO/IEC assume no liability
034 * for use of this software module or modifications thereof. No license
035 * or right to this software module is granted for non JPEG 2000 Standard
036 * conforming products. JJ2000 Partners have full right to use this
037 * software module for his/her own purpose, assign or donate this
038 * software module to any third party and to inhibit third parties from
039 * using this software module for non JPEG 2000 Standard conforming
040 * products. This copyright notice must be included in all copies or
041 * derivative works of this software module.
042 *
043 * Copyright (c) 1999/2000 JJ2000 Partners.
044 * */
045package jj2000.j2k.image;
046import java.awt.Point;
047
048import jj2000.j2k.NoNextElementException;
049import jj2000.j2k.util.FacilityManager;
050import jj2000.j2k.util.MsgLogger;
051
052/**
053 * This class places an image in the canvas coordinate system, tiles it, if so
054 * specified, and performs the coordinate conversions transparently. The
055 * source must be a 'BlkImgDataSrc' which is not tiled and has a the image
056 * origin at the canvas origin (i.e. it is not "canvased"), or an exception is
057 * thrown by the constructor. A tiled and "canvased" output is given through
058 * the 'BlkImgDataSrc' interface. See the 'ImgData' interface for a
059 * description of the canvas and tiling.
060 *
061 * <p>All tiles produced are rectangular, non-overlapping and their union
062 * covers all the image. However, the tiling may not be uniform, depending on
063 * the nominal tile size, tiling origin, component subsampling and other
064 * factors. Therefore it might not be assumed that all tiles are of the same
065 * width and height.</p>
066 *
067 * <p>The nominal dimension of the tiles is the maximal one, in the reference
068 * grid. All the components of the image have the same number of tiles.</p>
069 *
070 * @see ImgData
071 * @see BlkImgDataSrc
072 * */
073public class Tiler extends ImgDataAdapter implements BlkImgDataSrc {
074
075    /** The source of image data */
076    private BlkImgDataSrc src = null;
077
078    /** Horizontal coordinate of the upper left hand reference grid point.*/
079    private int x0siz;
080
081    /** Vertical coordinate of the upper left hand reference grid point.*/
082    private int y0siz;
083
084    /** The horizontal coordinate of the tiling origin in the canvas system,
085     * on the reference grid. */
086    private int xt0siz;
087
088    /** The vertical coordinate of the tiling origin in the canvas system, on
089     * the reference grid. */
090    private int yt0siz;
091
092    /** The nominal width of the tiles, on the reference grid. If 0 then there
093     * is no tiling in that direction. */
094    private int xtsiz;
095
096    /** The nominal height of the tiles, on the reference grid. If 0 then
097     * there is no tiling in that direction. */
098    private int ytsiz;
099
100    /** The number of tiles in the horizontal direction. */
101    private int ntX;
102
103    /** The number of tiles in the vertical direction. */
104    private int ntY;
105
106    /** The component width in the current active tile, for each component */
107    private int compW[] = null;
108
109    /** The component height in the current active tile, for each component */
110    private int compH[] = null;
111
112    /** The horizontal coordinates of the upper-left corner of the components
113     * in the current tile */
114    private int tcx0[] = null;
115
116    /** The vertical coordinates of the upper-left corner of the components in
117     * the current tile. */
118    private int tcy0[] = null;
119
120    /** The horizontal index of the current tile */
121    private int tx;
122
123    /** The vertical index of the current tile */
124    private int ty;
125
126    /** The width of the current tile, on the reference grid. */
127    private int tileW;
128
129    /** The height of the current tile, on the reference grid. */
130    private int tileH;
131
132    /**
133     * Constructs a new tiler with the specified 'BlkImgDataSrc' source,
134     * image origin, tiling origin and nominal tile size.
135     *
136     * @param src The 'BlkImgDataSrc' source from where to get the image
137     * data. It must not be tiled and the image origin must be at '(0,0)' on
138     * its canvas.
139     *
140     * @param ax The horizontal coordinate of the image origin in the canvas
141     * system, on the reference grid (i.e. the image's top-left corner in the
142     * reference grid).
143     *
144     * @param ay The vertical coordinate of the image origin in the canvas
145     * system, on the reference grid (i.e. the image's top-left corner in the
146     * reference grid).
147     *
148     * @param px The horizontal tiling origin, in the canvas system, on the
149     * reference grid. It must satisfy 'px<=ax'.
150     *
151     * @param py The vertical tiling origin, in the canvas system, on the
152     * reference grid. It must satisfy 'py<=ay'.
153     *
154     * @param nw The nominal tile width, on the reference grid. If 0 then
155     * there is no tiling in that direction.
156     *
157     * @param nh The nominal tile height, on the reference grid. If 0 then
158     * there is no tiling in that direction.
159     *
160     * @exception IllegalArgumentException If src is tiled or "canvased", or
161     * if the arguments do not satisfy the specified constraints.
162     * */
163    public Tiler(BlkImgDataSrc src,int ax,int ay,int px,int py,int nw,int nh) {
164        super(src);
165
166        // Initialize
167        this.src = src;
168        this.x0siz = ax;
169        this.y0siz = ay;
170        this.xt0siz = px;
171        this.yt0siz = py;
172        this.xtsiz = nw;
173        this.ytsiz = nh;
174
175        // Verify that input is not tiled
176/*
177        if (src.getNumTiles()!=1) {
178            throw new IllegalArgumentException("Source is tiled");
179        }
180*/
181        // Verify that source is not "canvased"
182/*
183        if (src.getImgULX()!=0 || src.getImgULY()!=0) {
184            throw new IllegalArgumentException("Source is \"canvased\"");
185        }
186*/
187        // Verify that arguments satisfy trivial requirements
188        if (x0siz<0 || y0siz<0 || xt0siz<0 || yt0siz<0 || xtsiz<0 || ytsiz<0
189            || xt0siz>x0siz || yt0siz>y0siz) {
190            throw new IllegalArgumentException("Invalid image origin, "+
191                                               "tiling origin or nominal "+
192                                               "tile size");
193        }
194
195        // If no tiling has been specified, creates a unique tile with maximum
196        // dimension.
197        if (xtsiz==0) xtsiz = x0siz+src.getImgWidth()-xt0siz;
198        if (ytsiz==0) ytsiz = y0siz+src.getImgHeight()-yt0siz;
199
200        // Automatically adjusts xt0siz,yt0siz so that tile (0,0) always
201        // overlaps with the image.
202        if (x0siz-xt0siz>=xtsiz) {
203            xt0siz += ((x0siz-xt0siz)/xtsiz)*xtsiz;
204        }
205        if (y0siz-yt0siz>=ytsiz) {
206            yt0siz += ((y0siz-yt0siz)/ytsiz)*ytsiz;
207        }
208        if (x0siz-xt0siz>=xtsiz || y0siz-yt0siz>=ytsiz) {
209            FacilityManager.getMsgLogger().
210                printmsg(MsgLogger.INFO,"Automatically adjusted tiling "+
211                         "origin to equivalent one ("+xt0siz+","+
212                         yt0siz+") so that "+
213                         "first tile overlaps the image");
214        }
215
216        // Calculate the number of tiles
217        ntX = (int)Math.ceil((x0siz+src.getImgWidth() - xt0siz)/(double)xtsiz);
218        ntY = (int)Math.ceil((y0siz+src.getImgHeight() - yt0siz)/(double)ytsiz);
219    }
220
221    /**
222     * Returns the overall width of the current tile in pixels. This is the
223     * tile's width without accounting for any component subsampling.
224     *
225     * @return The total current tile width in pixels.
226     * */
227    public final int getTileWidth() {
228        return tileW;
229    }
230
231    /**
232     * Returns the overall height of the current tile in pixels. This is the
233     * tile's width without accounting for any component subsampling.
234     *
235     * @return The total current tile height in pixels.
236     * */
237    public final int getTileHeight() {
238        return tileH;
239    }
240
241    /**
242     * Returns the width in pixels of the specified tile-component.
243     *
244     * @param t Tile index
245     *
246     * @param c The index of the component, from 0 to N-1.
247     *
248     * @return The width of specified tile-component.
249     * */
250    public final int getTileCompWidth(int t,int c) {
251        if(t!=getTileIdx()) {
252            throw new Error("Asking the width of a tile-component which is "+
253                            "not in the current tile (call setTile() or "+
254                            "nextTile() methods before).");
255        }
256        return compW[c];
257    }
258
259    /**
260     * Returns the height in pixels of the specified tile-component.
261     *
262     * @param t The tile index.
263     *
264     * @param c The index of the component, from 0 to N-1.
265     *
266     * @return The height of specified tile-component.
267     * */
268    public final int getTileCompHeight(int t,int c) {
269        if(t!=getTileIdx()) {
270            throw new Error("Asking the width of a tile-component which is "+
271                            "not in the current tile (call setTile() or "+
272                            "nextTile() methods before).");
273        }
274        return compH[c];
275    }
276
277    /**
278     * Returns the position of the fixed point in the specified
279     * component. This is the position of the least significant integral
280     * (i.e. non-fractional) bit, which is equivalent to the number of
281     * fractional bits. For instance, for fixed-point values with 2 fractional
282     * bits, 2 is returned. For floating-point data this value does not apply
283     * and 0 should be returned. Position 0 is the position of the least
284     * significant bit in the data.
285     *
286     * @param c The index of the component.
287     *
288     * @return The position of the fixed-point, which is the same as the
289     * number of fractional bits. For floating-point data 0 is returned.
290     * */
291    public int getFixedPoint(int c) {
292        return src.getFixedPoint(c);
293    }
294
295    /**
296     * Returns, in the blk argument, a block of image data containing the
297     * specifed rectangular area, in the specified component. The data is
298     * returned, as a reference to the internal data, if any, instead of as a
299     * copy, therefore the returned data should not be modified.
300     *
301     * <p>The rectangular area to return is specified by the 'ulx', 'uly', 'w'
302     * and 'h' members of the 'blk' argument, relative to the current
303     * tile. These members are not modified by this method. The 'offset' and
304     * 'scanw' of the returned data can be arbitrary. See the 'DataBlk'
305     * class.</p>
306     *
307     * <p>This method, in general, is more efficient than the 'getCompData()'
308     * method since it may not copy the data. However if the array of returned
309     * data is to be modified by the caller then the other method is probably
310     * preferable.</p>
311     *
312     * <p>If the data array in <tt>blk</tt> is <tt>null</tt>, then a new one
313     * is created if necessary. The implementation of this interface may
314     * choose to return the same array or a new one, depending on what is more
315     * efficient. Therefore, the data array in <tt>blk</tt> prior to the
316     * method call should not be considered to contain the returned data, a
317     * new array may have been created. Instead, get the array from
318     * <tt>blk</tt> after the method has returned.</p>
319     *
320     * <p>The returned data may have its 'progressive' attribute set. In this
321     * case the returned data is only an approximation of the "final"
322     * data.</p>
323     *
324     * @param blk Its coordinates and dimensions specify the area to return,
325     * relative to the current tile. Some fields in this object are modified
326     * to return the data.
327     *
328     * @param c The index of the component from which to get the data.
329     *
330     * @return The requested DataBlk
331     *
332     * @see #getCompData
333     * */
334    public final DataBlk getInternCompData(DataBlk blk,int c) {
335        // Check that block is inside tile
336        if (blk.ulx<0 || blk.uly<0 || blk.w>compW[c] || blk.h>compH[c]) {
337            throw new IllegalArgumentException("Block is outside the tile");
338        }
339        // Translate to the sources coordinates
340        int incx = (int)Math.ceil(x0siz/(double)src.getCompSubsX(c));
341        int incy = (int)Math.ceil(y0siz/(double)src.getCompSubsY(c));
342        blk.ulx -= incx;
343        blk.uly -= incy;
344        blk = src.getInternCompData(blk,c);
345        // Translate back to the tiled coordinates
346        blk.ulx += incx;
347        blk.uly += incy;
348        return blk;
349    }
350
351    /**
352     * Returns, in the blk argument, a block of image data containing the
353     * specifed rectangular area, in the specified component. The data is
354     * returned, as a copy of the internal data, therefore the returned data
355     * can be modified "in place".
356     *
357     * <p>The rectangular area to return is specified by the 'ulx', 'uly', 'w'
358     * and 'h' members of the 'blk' argument, relative to the current
359     * tile. These members are not modified by this method. The 'offset' of
360     * the returned data is 0, and the 'scanw' is the same as the block's
361     * width. See the 'DataBlk' class.</p>
362     *
363     * <p>This method, in general, is less efficient than the
364     * 'getInternCompData()' method since, in general, it copies the
365     * data. However if the array of returned data is to be modified by the
366     * caller then this method is preferable.</p>
367     *
368     * <p>If the data array in 'blk' is 'null', then a new one is created. If
369     * the data array is not 'null' then it is reused, and it must be large
370     * enough to contain the block's data. Otherwise an 'ArrayStoreException'
371     * or an 'IndexOutOfBoundsException' is thrown by the Java system.</p>
372     *
373     * <p>The returned data may have its 'progressive' attribute set. In this
374     * case the returned data is only an approximation of the "final"
375     * data.</p>
376     *
377     * @param blk Its coordinates and dimensions specify the area to return,
378     * relative to the current tile. If it contains a non-null data array,
379     * then it must be large enough. If it contains a null data array a new
380     * one is created. Some fields in this object are modified to return the
381     * data.
382     *
383     * @param c The index of the component from which to get the data.
384     *
385     * @return The requested DataBlk
386     *
387     * @see #getInternCompData
388     * */
389    public final DataBlk getCompData(DataBlk blk,int c) {
390        // Check that block is inside tile
391        if (blk.ulx<0 || blk.uly<0 || blk.w>compW[c] || blk.h>compH[c]) {
392            throw new IllegalArgumentException("Block is outside the tile");
393        }
394        // Translate to the source's coordinates
395        int incx = (int)Math.ceil(x0siz/(double)src.getCompSubsX(c));
396        int incy = (int)Math.ceil(y0siz/(double)src.getCompSubsY(c));
397        blk.ulx -= incx;
398        blk.uly -= incy;
399        blk = src.getCompData(blk,c);
400        // Translate back to the tiled coordinates
401        blk.ulx += incx;
402        blk.uly += incy;
403        return blk;
404    }
405
406    /**
407     * Changes the current tile, given the new tile indexes. An
408     * IllegalArgumentException is thrown if the coordinates do not correspond
409     * to a valid tile.
410     *
411     * @param x The horizontal index of the tile.
412     *
413     * @param y The vertical index of the new tile.
414     * */
415    public final void setTile(int x,int y) {
416        src.setTile(x, y);
417
418        // Check tile indexes
419        if (x<0 || y<0 || x>=ntX || y>=ntY) {
420            throw new IllegalArgumentException("Tile's indexes out of bounds");
421        }
422
423        // Set new current tile
424        tx = x;
425        ty = y;
426        // Calculate tile origins
427        int tx0 = (x!=0) ? xt0siz+x*xtsiz : x0siz;
428        int ty0 = (y!=0) ? yt0siz+y*ytsiz : y0siz;
429        int tx1 = (x!=ntX-1) ? (xt0siz+(x+1)*xtsiz) :
430            (x0siz+src.getImgWidth());
431        int ty1 = (y!=ntY-1) ? (yt0siz+(y+1)*ytsiz) :
432            (y0siz+src.getImgHeight());
433        // Set general variables
434        tileW = tx1 - tx0;
435        tileH = ty1 - ty0;
436        // Set component specific variables
437        int nc = src.getNumComps();
438        if(compW==null) compW = new int[nc];
439        if(compH==null) compH = new int[nc];
440        if(tcx0==null) tcx0 = new int[nc];
441        if(tcy0==null) tcy0 = new int[nc];
442        for (int i=0; i<nc ; i++) {
443            tcx0[i] = (int)Math.ceil(tx0/(double)src.getCompSubsX(i));
444            tcy0[i] = (int)Math.ceil(ty0/(double)src.getCompSubsY(i));
445            compW[i] = (int)Math.ceil(tx1/(double)src.getCompSubsX(i)) -
446                tcx0[i];
447            compH[i] = (int)Math.ceil(ty1/(double)src.getCompSubsY(i)) -
448                tcy0[i];
449        }
450    }
451
452    /**
453     * Advances to the next tile, in standard scan-line order (by rows then
454     * columns). An NoNextElementException is thrown if the current tile is
455     * the last one (i.e. there is no next tile).
456     * */
457    public final void nextTile() {
458        if (tx==ntX-1 && ty==ntY-1) { // Already at last tile
459            throw new NoNextElementException();
460        } else if (tx<ntX-1) { // If not at end of current tile line
461            setTile(tx+1,ty);
462        } else { // First tile at next line
463            setTile(0,ty+1);
464        }
465    }
466
467    /**
468     * Returns the horizontal and vertical indexes of the current tile.
469     *
470     * @param co If not null this object is used to return the
471     * information. If null a new one is created and returned.
472     *
473     * @return The current tile's horizontal and vertical indexes..
474     * */
475    public final Point getTile(Point co) {
476        if (co != null) {
477            co.x = tx;
478            co.y = ty;
479            return co;
480        } else {
481            return new Point(tx,ty);
482        }
483    }
484
485    /**
486     * Returns the index of the current tile, relative to a standard scan-line
487     * order.
488     *
489     * @return The current tile's index (starts at 0).
490     * */
491    public final int getTileIdx() {
492        return ty*ntX+tx;
493    }
494
495    /**
496     * Returns the horizontal coordinate of the upper-left corner of the
497     * specified component in the current tile.
498     *
499     * @param c The component index.
500     * */
501    public final int getCompULX(int c) {
502        return tcx0[c];
503    }
504
505    /**
506     * Returns the vertical coordinate of the upper-left corner of the
507     * specified component in the current tile.
508     *
509     * @param c The component index.
510     * */
511    public final int getCompULY(int c) {
512        return tcy0[c];
513    }
514
515    /** Returns the horizontal tile partition offset in the reference grid */
516    public int getTilePartULX() {
517        return xt0siz;
518    }
519
520    /** Returns the vertical tile partition offset in the reference grid */
521    public int getTilePartULY() {
522        return yt0siz;
523    }
524
525    /**
526     * Returns the horizontal coordinate of the image origin, the top-left
527     * corner, in the canvas system, on the reference grid.
528     *
529     * @return The horizontal coordinate of the image origin in the canvas
530     * system, on the reference grid.
531     * */
532    public final int getImgULX() {
533        return x0siz;
534    }
535
536    /**
537     * Returns the vertical coordinate of the image origin, the top-left
538     * corner, in the canvas system, on the reference grid.
539     *
540     * @return The vertical coordinate of the image origin in the canvas
541     * system, on the reference grid.
542     * */
543    public final int getImgULY() {
544        return y0siz;
545    }
546
547    /**
548     * Returns the number of tiles in the horizontal and vertical directions.
549     *
550     * @param co If not null this object is used to return the information. If
551     * null a new one is created and returned.
552     *
553     * @return The number of tiles in the horizontal (Point.x) and vertical
554     * (Point.y) directions.
555     * */
556    public final Point getNumTiles(Point co) {
557        if (co != null) {
558            co.x = ntX;
559            co.y = ntY;
560            return co;
561        } else {
562            return new Point(ntX,ntY);
563        }
564    }
565
566    /**
567     * Returns the total number of tiles in the image.
568     *
569     * @return The total number of tiles in the image.
570     * */
571    public final int getNumTiles() {
572        return ntX*ntY;
573    }
574
575    /**
576     * Returns the nominal width of the tiles in the reference grid.
577     *
578     * @return The nominal tile width, in the reference grid.
579     * */
580    public final int getNomTileWidth() {
581        return xtsiz;
582    }
583
584    /**
585     * Returns the nominal width of the tiles in the reference grid.
586     *
587     * @return The nominal tile width, in the reference grid.
588     * */
589    public final int getNomTileHeight() {
590        return ytsiz;
591    }
592
593    /**
594     * Returns the tiling origin, referred to as '(xt0siz,yt0siz)' in the
595     * codestream header (SIZ marker segment).
596     *
597     * @param co If not null this object is used to return the information. If
598     * null a new one is created and returned.
599     *
600     * @return The coordinate of the tiling origin, in the canvas system, on
601     * the reference grid.
602     *
603     * @see ImgData
604     * */
605    public final Point getTilingOrigin(Point co) {
606        if (co != null) {
607            co.x = xt0siz;
608            co.y = yt0siz;
609            return co;
610        } else {
611            return new Point(xt0siz,yt0siz);
612        }
613    }
614
615    /**
616     * Returns a String object representing Tiler's informations
617     *
618     * @return Tiler's infos in a string
619     * */
620    public String toString() {
621        return "Tiler: source= "+src+
622            "\n"+getNumTiles()+" tile(s), nominal width="+xtsiz+
623            ", nominal height="+ytsiz;
624    }
625}