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