001/*
002 * $RCSfile: RenderedImageSrc.java,v $
003 *
004 * 
005 * Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.
006 * 
007 * Redistribution and use in source and binary forms, with or without
008 * modification, are permitted provided that the following conditions
009 * are met: 
010 * 
011 * - Redistribution of source code must retain the above copyright 
012 *   notice, this  list of conditions and the following disclaimer.
013 * 
014 * - Redistribution in binary form must reproduce the above copyright
015 *   notice, this list of conditions and the following disclaimer in 
016 *   the documentation and/or other materials provided with the
017 *   distribution.
018 * 
019 * Neither the name of Sun Microsystems, Inc. or the names of 
020 * contributors may be used to endorse or promote products derived 
021 * from this software without specific prior written permission.
022 * 
023 * This software is provided "AS IS," without a warranty of any 
024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
035 * POSSIBILITY OF SUCH DAMAGES. 
036 * 
037 * You acknowledge that this software is not designed or intended for 
038 * use in the design, construction, operation or maintenance of any 
039 * nuclear facility. 
040 *
041 * $Revision: 1.2 $
042 * $Date: 2006/09/22 23:07:25 $
043 * $State: Exp $
044 */
045package com.github.jaiimageio.jpeg2000.impl;
046
047import java.awt.Point;
048import java.awt.Rectangle;
049import java.awt.image.ColorModel;
050import java.awt.image.DataBuffer;
051import java.awt.image.Raster;
052import java.awt.image.RenderedImage;
053import java.awt.image.SampleModel;
054import java.awt.image.WritableRaster;
055
056import jj2000.j2k.JJ2KExceptionHandler;
057import jj2000.j2k.image.BlkImgDataSrc;
058import jj2000.j2k.image.DataBlk;
059import jj2000.j2k.image.DataBlkInt;
060import jj2000.j2k.image.ImgData;
061
062import com.github.jaiimageio.impl.common.ImageUtil;
063
064public class RenderedImageSrc implements BlkImgDataSrc {
065    /** The width of the image */
066    private int w;
067
068    /** The height of the image */
069    private int h;
070
071    /** The tile width for encoding */
072    int tileWidth;
073
074    /** The tile height for encoding */
075    int tileHeight;
076
077    /** The tile grid offset for encoding */
078    int tileXOffset, tileYOffset;
079
080    /** The source -> destination transformation */
081    int scaleX, scaleY, xOffset, yOffset;
082
083    /** The source bands to be encoded. */
084    int[] sourceBands = null;
085
086    /** The destination upper-left corner */
087    int minX, minY;
088
089    /** The number of components in the image */
090    private int nc;
091
092    /** The number of bits that determine the nominal dynamic range */
093    // XXX: Should be an int[] of length 'nc'.
094    private int rb;
095
096    /** Buffer for the 3 components of each pixel(in the current block) */
097    private int[][] barr = null;
098
099    /** Data block used only to store coordinates of the buffered blocks */
100    private DataBlkInt dbi = new DataBlkInt();
101
102    /** The line buffer. */
103    private byte buf[];
104
105    /** Temporary DataBlkInt object (needed when encoder uses floating-point
106        filters). This avoid allocating new DataBlk at each time */
107    private DataBlkInt intBlk;
108
109    private RenderedImage src;
110    private J2KImageWriteParamJava param;
111
112    /** The input source raster. */
113    private Raster raster;
114
115    /** The raster for a destination tile */
116    private Raster aTile;
117
118    private Point co = new Point();
119
120    private int dcOffset = 0;
121
122    private boolean isBinary = false;
123
124    private Rectangle destinationRegion;
125    private Rectangle sourceRegion;
126
127    private ColorModel cm;
128    private SampleModel sm;
129
130    private boolean noTransform = true;
131    private boolean noSubband = true;
132
133    /** Used to process abortion. */
134    private J2KImageWriter writer;
135
136    /** Indicates a <code>raster</code> rather than a <code>RenderedImage</code>
137     *  to be encoded.
138     */
139    private boolean inputIsRaster = false;
140
141    /**
142     * Creates <code>RenderedImageSrc</code> for encoding a <code>Raster</code>.
143     *
144     * @param raster The <code>Raster</code> to be encoded.
145     * @param param The <code>J2KImageWriteParamJava</code> used in encoding.
146     * @param writer The <code>J2KImageWriter</code> performs the encoding.
147     *
148     * @param IOException If an error occurs while opening the file.
149     */
150    public RenderedImageSrc(Raster raster,
151                            J2KImageWriteParamJava param,
152                            J2KImageWriter writer) {
153        this.raster = raster;
154        this.param = param;
155        this.writer = writer;
156        this.inputIsRaster = true;
157
158        sourceRegion = param.getSourceRegion();
159
160        if (sourceRegion == null)
161            sourceRegion = new Rectangle(raster.getMinX(), raster.getMinY(),
162                                         raster.getWidth(), raster.getHeight());
163        else
164            sourceRegion = sourceRegion.intersection(raster.getBounds());
165
166        if (sourceRegion.isEmpty())
167            throw new RuntimeException(I18N.getString("J2KImageWriterCodecLib0"));
168
169        sm = raster.getSampleModel();
170        getFromParam();
171        setSampleModelAndMore();
172        setTile(0, 0);
173    }
174
175    /**
176     * Creates <code>RenderedImageSrc</code> for encoding a
177     * <code>RenderedImage</code>.
178     *
179     * @param src The <code>RenderedImage</code> to be encoded.
180     * @param param The <code>J2KImageWriteParamJava</code> used in encoding.
181     * @param writer The <code>J2KImageWriter</code> performs the encoding.
182     *
183     * @param IOException If an error occurs while opening the file.
184     * */
185    public RenderedImageSrc(RenderedImage src,
186                            J2KImageWriteParamJava param,
187                            J2KImageWriter writer) {
188        this.src = src;
189        this.param = param;
190        this.writer = writer;
191
192        sourceRegion = param.getSourceRegion();
193
194        if (sourceRegion == null)
195            sourceRegion = new Rectangle(src.getMinX(), src.getMinY(),
196                                         src.getWidth(), src.getHeight());
197        else
198            sourceRegion = sourceRegion.intersection(new Rectangle(src.getMinX(),
199                                                                   src.getMinY(),
200                                                                   src.getWidth(),
201                                                                   src.getHeight()));
202        if (sourceRegion.isEmpty())
203            throw new RuntimeException(I18N.getString("J2KImageWriterCodecLib0"));
204
205        sm = src.getSampleModel();
206        cm = src.getColorModel();
207        getFromParam();
208        setSampleModelAndMore();
209    }
210
211    private void getFromParam() {
212        try {
213            tileWidth = param.getTileWidth();
214            tileHeight = param.getTileHeight();
215            tileXOffset = param.getTileGridXOffset();
216            tileYOffset = param.getTileGridYOffset();
217        } catch(IllegalStateException e) {
218            param.setTilingMode(param.MODE_EXPLICIT);
219            if (inputIsRaster) {
220                param.setTiling(raster.getWidth(), raster.getHeight(),
221                                raster.getMinX(), raster.getMinY());
222            } else {
223                param.setTiling(src.getWidth(), src.getHeight(),
224                                src.getMinX(), src.getMinY());
225            }
226            tileWidth = param.getTileWidth();
227            tileHeight = param.getTileHeight();
228            tileXOffset = param.getTileGridXOffset();
229            tileYOffset = param.getTileGridYOffset();
230        }
231
232        scaleX = param.getSourceXSubsampling();
233        scaleY = param.getSourceYSubsampling();
234        xOffset = param.getSubsamplingXOffset();
235        yOffset = param.getSubsamplingYOffset();
236
237        sourceRegion.translate(xOffset, yOffset);
238        sourceRegion.width -= xOffset;
239        sourceRegion.height -= yOffset;
240
241        xOffset = sourceRegion.x % scaleX;
242        yOffset = sourceRegion.y % scaleY;
243
244        minX = sourceRegion.x / scaleX;
245        minY = sourceRegion.y / scaleY;
246
247        w = (sourceRegion.width + scaleX - 1) / scaleX;
248        h = (sourceRegion.height + scaleY - 1) / scaleY;
249
250        tileXOffset += (minX - tileXOffset)/tileWidth * tileWidth;
251        tileYOffset += (minY - tileYOffset)/tileHeight * tileHeight;
252
253        destinationRegion = new Rectangle(minX, minY, w, h);
254
255        if (!destinationRegion.equals(sourceRegion) ||
256            tileWidth != sm.getWidth() ||
257            tileHeight != sm.getHeight() ||
258            (!inputIsRaster &&
259             (tileXOffset != src.getTileGridXOffset() ||
260             tileYOffset != src.getTileGridYOffset())) ||
261            (inputIsRaster &&
262             (tileXOffset != raster.getMinX() ||
263             tileYOffset != raster.getMinY())))
264            noTransform = false;
265
266    }
267
268    private void setSampleModelAndMore() {
269        nc = sm.getNumBands();
270        sourceBands = param.getSourceBands();
271        if (sourceBands != null) {
272            sm = sm.createSubsetSampleModel(sourceBands);
273            noSubband = false;
274        } else {
275            sourceBands = new int[nc];
276            for (int i = 0; i < nc; i++)
277                sourceBands[i] = i;
278        }
279
280        sm = sm.createCompatibleSampleModel(tileWidth, tileHeight);
281        nc = sm.getNumBands();
282        isBinary = ImageUtil.isBinary(sm);
283
284        if(cm != null) {
285            // XXX: rb should be set to getComponentSize();
286            rb = cm.getComponentSize(0);
287            for (int i = 1; i < cm.getNumComponents(); i++)
288                if (rb < cm.getComponentSize(i))
289                    rb = cm.getComponentSize(i);
290        } else {
291            // XXX: rb should be set to getSampleSize();
292            rb = sm.getSampleSize(0);
293            for (int i = 1; i < sm.getNumBands(); i++)
294                if (rb < sm.getSampleSize(i))
295                    rb = sm.getSampleSize(i);
296        }
297
298        if (!isOrigSigned(0) && rb > 1)
299            // XXX: if rb is an int[] this will have to change.
300            dcOffset = 1 << rb - 1;
301    }
302
303
304    public int getTilePartULX() {
305        return tileXOffset;
306    }
307
308    public int getTilePartULY() {
309        return tileYOffset;
310    }
311
312    /**
313     * Returns the width of the current tile in pixels.
314     *
315     * @return The total image width in pixels.
316     * */
317    public int getTileWidth() {
318        int width = tileWidth;
319        int maxX = getImgULX() + getImgWidth();
320        int x = co.x * tileWidth + tileXOffset;
321        if (x + tileWidth >= maxX)
322            width = maxX - x;
323        return width;
324    }
325
326    /**
327     * Returns the overall height of the current tile in pixels.
328     *
329     * @return The total image height in pixels.  */
330    public int getTileHeight() {
331        int height = tileHeight;
332        int maxY = getImgULY() + getImgHeight();
333        int y = co.y * tileHeight + tileYOffset;
334        if (y + tileHeight >= maxY)
335            height = maxY - y;
336
337        return height;
338    }
339
340    public int getNomTileWidth() {
341        return tileWidth;
342    }
343
344    public int getNomTileHeight() {
345        return tileHeight;
346    }
347
348    /**
349     * Returns the overall width of the image in pixels. This is the image's
350     * width without accounting for any component subsampling or tiling. The
351     * value of <tt>w</tt> is returned.
352     *
353     * @return The total image's width in pixels.
354     * */
355    public int getImgWidth() {
356        return w;
357    }
358
359    /**
360     * Returns the overall height of the image in pixels. This is the image's
361     * height without accounting for any component subsampling or tiling. The
362     * value of <tt>h</tt> is returned.
363     *
364     * @return The total image's height in pixels.
365     * */
366    public int getImgHeight() {
367        return h;
368    }
369
370    /**
371     * Returns the number of components in the image. The value of <tt>nc</tt>
372     * is returned.
373     *
374     * @return The number of components in the image.
375     * */
376    public int getNumComps() {
377        return nc;
378    }
379
380    public int getTileGridXOffset() {
381        return param.getTileGridXOffset();
382    }
383
384    public int getTileGridYOffset() {
385        return param.getTileGridYOffset();
386    }
387
388    public int getTileCompHeight(int t, int c) {
389        return tileHeight;
390    }
391
392    public int getTileCompWidth(int t, int c) {
393        return tileWidth;
394    }
395
396    /**
397     * Returns the component subsampling factor in the horizontal direction,
398     * for the specified component. This is, approximately, the ratio of
399     * dimensions between the reference grid and the component itself, see the
400     * 'ImgData' interface desription for details.
401     *
402     * @param c The index of the component (between 0 and C-1)
403     *
404     * @return The horizontal subsampling factor of component 'c'
405     *
406     * @see ImgData
407     * */
408    public int getCompSubsX(int c) {
409        return 1;
410    }
411
412    /**
413     * Returns the component subsampling factor in the vertical direction, for
414     * the specified component. This is, approximately, the ratio of
415     * dimensions between the reference grid and the component itself, see the
416     * 'ImgData' interface desription for details.
417     *
418     * @param c The index of the component (between 0 and C-1)
419     *
420     * @return The vertical subsampling factor of component 'c'
421     *
422     * @see ImgData
423     * */
424    public int getCompSubsY(int c) {
425        return 1;
426    }
427
428    /**
429     * Returns the width in pixels of the specified component in the current
430     * tile. This default implementation assumes no tiling and no component
431     * subsampling (i.e., all components, or components, have the same
432     * dimensions in pixels).
433     *
434     * @param c The index of the component, from 0 to C-1.
435     *
436     * @return The width in pixels of component <tt>n</tt> in the current
437     * tile.
438     * */
439    public int getCompWidth(int n) {
440        return w;
441    }
442
443    /**
444     * Returns the height in pixels of the specified component in the current
445     * tile. This default implementation assumes no tiling and no component
446     * subsampling (i.e., all components, or components, have the same
447     * dimensions in pixels).
448     *
449     * @param c The index of the component, from 0 to C-1.
450     *
451     * @return The height in pixels of component <tt>c</tt> in the current
452     * tile.
453     * */
454    public int getCompHeight(int c) {
455        return h;
456    }
457
458    /**
459     * Returns the width in pixels of the specified component in the overall
460     * image. This default implementation assumes no component, or component,
461     * subsampling (i.e. all components have the same dimensions in pixels).
462     *
463     * @param c The index of the component, from 0 to C-1.
464     *
465     * @return The width in pixels of component <tt>c</tt> in the overall
466     * image.
467     * */
468    public int getCompImgWidth(int c) {
469        return w;
470    }
471
472    /**
473     * Returns the height in pixels of the specified component in the overall
474     * image. This default implementation assumes no component, or component,
475     * subsampling (i.e. all components have the same dimensions in pixels).
476     *
477     * @param c The index of the component, from 0 to C-1.
478     *
479     * @return The height in pixels of component <tt>c</tt> in the overall
480     * image.
481     * */
482    public int getCompImgHeight(int c) {
483        return h;
484    }
485
486    /**
487     * Changes the current tile, given the new coordinates.
488     *
489     * @param x The horizontal coordinate of the tile.
490     *
491     * @param y The vertical coordinate of the new tile.
492     * */
493    public void setTile(int x, int y) {
494        if (x >= getNumXTiles()) {
495            y += x/ getNumXTiles();
496            x = x % getNumXTiles();
497        }
498        co.x = x;
499        co.y = y;
500        aTile = null;
501    }
502
503    /**
504     * Advances to the next tile, in standard scan-line order (by rows then
505     * columns).
506     * */
507    public void nextTile() {
508        co.x++;
509        if (co.x >= getNumXTiles()) {
510            co.x = 0;
511            co.y++;
512        }
513        setTile(co.x, co.y);
514    }
515
516    /**
517     * Returns the coordinates of the current tile. This default
518     * implementation assumes no-tiling, so (0,0) is returned.
519     *
520     * @param co If not null this object is used to return the information. If
521     * null a new one is created and returned.
522     *
523     * @return The current tile's coordinates.
524     * */
525    public Point getTile(Point co) {
526        if (co != null)
527            return co;
528        else
529            return new Point(0, 0);
530    }
531
532    /**
533     * Returns the index of the current tile, relative to a standard scan-line
534     * order.
535     *
536     * @return The current tile's index (starts at 0).
537     * */
538    public int getTileIdx() {
539        return getNumXTiles() * co.y + co.x;
540    }
541
542    /**
543     * Returns the horizontal and vertical offset of the upper-left corner of
544     * the current tile, in the specified component, relative to the canvas
545     * origin, in the component coordinates (not in the reference grid
546     * coordinates). These are the coordinates of the current tile's (not
547     * active tile) upper-left corner relative to the canvas.
548     *
549     * @param co If not null the object is used to return the values, if null
550     * a new one is created and returned.
551     *
552     * @param c The index of the component (between 0 and C-1)
553     *
554     * @return The horizontal and vertical offsets of the upper-left corner of
555     * the current tile, for the specified component, relative to the canvas
556     * origin, in the component coordinates.
557     * */
558    public Point getTileOff(Point p, int c) {
559        if (p != null) {
560            p.x = co.x * tileWidth + tileXOffset;
561            p.y = co.y * tileHeight + tileYOffset;
562            return co;
563        } else
564            return new Point(co.x * tileWidth + tileXOffset,
565                             co.y * tileHeight + tileYOffset);
566    }
567
568    /**
569     * Returns the horizontal coordinate of the upper-left corner of the
570     * active tile, with respect to the canvas origin, in the component
571     * coordinates, for the specified component.
572     *
573     * @param c The index of the component (between 0 and C-1)
574     *
575     * @return The horizontal coordinate of the upper-left corner of the
576     * active tile, with respect to the canvas origin, for component 'c', in
577     * the component coordinates.
578     * */
579    public int getCompULX(int c) {
580        return raster.getMinX();
581    }
582
583    /**
584     * Returns the vertical coordinate of the upper-left corner of the active
585     * tile, with respect to the canvas origin, in the component coordinates,
586     * for the specified component.
587     *
588     * @param c The index of the component (between 0 and C-1)
589     *
590     * @return The vertical coordinate of the upper-left corner of the active
591     * tile, with respect to the canvas origin, for component 'c', in the
592     * component coordinates.
593     * */
594    public int getCompULY(int c) {
595        return raster.getMinY();
596    }
597
598    /**
599     * Returns the horizontal coordinate of the image origin, the top-left
600     * corner, in the canvas system, on the reference grid.
601     *
602     * @return The horizontal coordinate of the image origin in the canvas
603     * system, on the reference grid.
604     * */
605    public int getImgULX() {
606        return destinationRegion.x;
607    }
608
609    /**
610     * Returns the vertical coordinate of the image origin, the top-left
611     * corner, in the canvas system, on the reference grid.
612     *
613     * @return The vertical coordinate of the image origin in the canvas
614     * system, on the reference grid.
615     * */
616    public int getImgULY() {
617        return destinationRegion.y;
618    }
619
620    /**
621     * Returns the number of tiles in the horizontal and vertical
622     * directions.
623     *
624     * @param co If not null this object is used to return the information. If
625     * null a new one is created and returned.
626     *
627     * @return The number of tiles in the horizontal (Point.x) and vertical
628     * (Point.y) directions.
629     * */
630    public Point getNumTiles(Point co) {
631        if (co != null) {
632            co.x = getNumXTiles();
633            co.y = getNumYTiles();
634            return co;
635        }
636        else {
637            return new Point(getNumXTiles(), getNumYTiles());
638        }
639    }
640
641    /**
642     * Returns the total number of tiles in the image. This default
643     * implementation assumes no tiling, so 1 is always returned.
644     *
645     * @return The total number of tiles in the image.
646     * */
647    public int getNumTiles() {
648        return getNumXTiles() * getNumYTiles();
649    }
650
651    /**
652     * Returns the number of bits corresponding to the nominal range of the
653     * data in the specified component. This is the value rb (range bits) that
654     * was specified in the constructor, which normally is 8 for non bilevel
655     * data, and 1 for bilevel data.
656     *
657     * <P>If this number is <i>b</b> then the nominal range is between
658     * -2^(b-1) and 2^(b-1)-1, since unsigned data is level shifted to have a
659     * nominal avergae of 0.
660     *
661     * @param c The index of the component.
662     *
663     * @return The number of bits corresponding to the nominal range of the
664     * data. For floating-point data this value is not applicable and the
665     * return value is undefined.
666     * */
667    public int getNomRangeBits(int c) {
668        // Check component index
669        // XXX: Should be component-dependent.
670        return rb;
671    }
672
673    /**
674     * Returns the position of the fixed point in the specified component
675     * (i.e. the number of fractional bits), which is always 0 for this
676     * ImgReader.
677     *
678     * @param c The index of the component.
679     *
680     * @return The position of the fixed-point (i.e. the number of fractional
681     * bits). Always 0 for this ImgReader.
682     * */
683    public int getFixedPoint(int c) {
684        // Check component index
685        return 0;
686    }
687
688
689    /**
690     * Returns, in the blk argument, the block of image data containing the
691     * specifed rectangular area, in the specified component. The data is
692     * returned, as a reference to the internal data, if any, instead of as a
693     * copy, therefore the returned data should not be modified.
694     *
695     * <P> After being read the coefficients are level shifted by subtracting
696     * 2^(nominal bit range - 1)
697     *
698     * <P>The rectangular area to return is specified by the 'ulx', 'uly', 'w'
699     * and 'h' members of the 'blk' argument, relative to the current
700     * tile. These members are not modified by this method. The 'offset' and
701     * 'scanw' of the returned data can be arbitrary. See the 'DataBlk' class.
702     *
703     * <P>If the data array in <tt>blk</tt> is <tt>null</tt>, then a new one
704     * is created if necessary. The implementation of this interface may
705     * choose to return the same array or a new one, depending on what is more
706     * efficient. Therefore, the data array in <tt>blk</tt> prior to the
707     * method call should not be considered to contain the returned data, a
708     * new array may have been created. Instead, get the array from
709     * <tt>blk</tt> after the method has returned.
710     *
711     * <P>The returned data always has its 'progressive' attribute unset
712     * (i.e. false).
713     *
714     * <P>When an I/O exception is encountered the JJ2KExceptionHandler is
715     * used. The exception is passed to its handleException method. The action
716     * that is taken depends on the action that has been registered in
717     * JJ2KExceptionHandler. See JJ2KExceptionHandler for details.
718     *
719     * <P>This method implements buffering for the 3 components: When the
720     * first one is asked, all the 3 components are read and stored until they
721     * are needed.
722     *
723     * @param blk Its coordinates and dimensions specify the area to
724     * return. Some fields in this object are modified to return the data.
725     *
726     * @param c The index of the component from which to get the data. Only 0,
727     * 1 and 3 are valid.
728     *
729     * @return The requested DataBlk
730     *
731     * @see #getCompData
732     *
733     * @see JJ2KExceptionHandler
734     */
735    public final DataBlk getInternCompData(DataBlk blk, int c) {
736        if (writer != null && writer.getAbortRequest())
737            throw new RuntimeException(J2KImageWriter.WRITE_ABORTED);
738
739        if (barr == null)
740            barr = new int[nc][];
741
742        // Check type of block provided as an argument
743        if(blk.getDataType()!=DataBlk.TYPE_INT){
744            if(intBlk==null)
745                intBlk = new DataBlkInt(blk.ulx,blk.uly,blk.w,blk.h);
746            else{
747                intBlk.ulx = blk.ulx;
748                intBlk.uly = blk.uly;
749                intBlk.w = blk.w;
750                intBlk.h = blk.h;
751            }
752            blk = intBlk;
753        }
754
755        float percentage =
756            (getTileIdx() + (blk.uly + 1.0F) / blk.h) / getNumTiles();
757        writer.processImageProgressWrapper(percentage * 100.0F);
758
759        // If asking a component for the first time for this block, read the 3
760        // components
761        if ((barr[c] == null) ||
762            (dbi.ulx > blk.ulx) || (dbi.uly > blk.uly) ||
763            (dbi.ulx+dbi.w < blk.ulx+blk.w) ||
764            (dbi.uly+dbi.h < blk.uly+blk.h)) {
765            int k,j,i,mi;
766
767            // Reset data arrays if needed
768            if (barr[c] == null || barr[c].length < blk.w*blk.h) {
769                barr[c] = new int[blk.w*blk.h];
770            }
771            blk.setData(barr[c]);
772
773            for (i = (c + 1) % nc; i != c; i = (i + 1) % nc)
774                if (barr[i] == null || barr[i].length < blk.w*blk.h) {
775                    barr[i] = new int[blk.w*blk.h];
776                }
777
778            // set attributes of the DataBlk used for buffering
779            dbi.ulx = blk.ulx;
780            dbi.uly = blk.uly;
781            dbi.w = blk.w;
782            dbi.h = blk.h;
783
784            // get data from the image
785            if (aTile == null) {
786                aTile = getTile(co.x, co.y);
787                Rectangle temp = aTile.getBounds();
788                aTile = aTile.createTranslatedChild(temp.x-minX,
789                                                    temp.y-minY);
790            }
791
792            for (i = 0; i < nc ; i++) {
793                aTile.getSamples(blk.ulx, blk.uly, blk.w, blk.h, i, barr[i]);
794                for (k = 0; k < barr[i].length; k++)
795                    barr[i][k] -= dcOffset;
796            }
797            //getByteData(raster, new Rectangle(blk.ulx, blk.uly, blk.w, blk.h), barr);
798
799            // Set buffer attributes
800            blk.setData(barr[c]);
801            blk.offset = 0;
802            blk.scanw = blk.w;
803        } else { //Asking for the 2nd or 3rd block component
804            blk.setData(barr[c]);
805            blk.offset = (blk.ulx-dbi.ulx)*dbi.w+blk.ulx-dbi.ulx;
806            blk.scanw = dbi.scanw;
807        }
808
809        // Turn off the progressive attribute
810        blk.progressive = false;
811        return blk;
812    }
813
814    /**
815     * Returns, in the blk argument, a block of image data containing the
816     * specifed rectangular area, in the specified component. The data is
817     * returned, as a copy of the internal data, therefore the returned data
818     * can be modified "in place".
819     *
820     * <P> After being read the coefficients are level shifted by subtracting
821     * 2^(nominal bit range - 1)
822     *
823     * <P>The rectangular area to return is specified by the 'ulx', 'uly', 'w'
824     * and 'h' members of the 'blk' argument, relative to the current
825     * tile. These members are not modified by this method. The 'offset' of
826     * the returned data is 0, and the 'scanw' is the same as the block's
827     * width. See the 'DataBlk' class.
828     *
829     * <P>If the data array in 'blk' is 'null', then a new one is created. If
830     * the data array is not 'null' then it is reused, and it must be large
831     * enough to contain the block's data. Otherwise an 'ArrayStoreException'
832     * or an 'IndexOutOfBoundsException' is thrown by the Java system.
833     *
834     * <P>The returned data has its 'progressive' attribute unset
835     * (i.e. false).
836     *
837     * <P>When an I/O exception is encountered the JJ2KExceptionHandler is
838     * used. The exception is passed to its handleException method. The action
839     * that is taken depends on the action that has been registered in
840     * JJ2KExceptionHandler. See JJ2KExceptionHandler for details.
841     *
842     * @param blk Its coordinates and dimensions specify the area to
843     * return. If it contains a non-null data array, then it must have the
844     * correct dimensions. If it contains a null data array a new one is
845     * created. The fields in this object are modified to return the data.
846     *
847     * @param c The index of the component from which to get the data. Only
848     * 0,1 and 2 are valid.
849     *
850     * @return The requested DataBlk
851     *
852     * @see #getInternCompData
853     *
854     * @see JJ2KExceptionHandler
855     * */
856    public final DataBlk getCompData(DataBlk blk, int c) {
857        // NOTE: can not directly call getInterCompData since that returns
858        // internally buffered data.
859        int ulx,uly,w,h;
860
861        // Check type of block provided as an argument
862        if(blk.getDataType()!=DataBlk.TYPE_INT){
863            DataBlkInt tmp = new DataBlkInt(blk.ulx,blk.uly,blk.w,blk.h);
864            blk = tmp;
865        }
866
867        int bakarr[] = (int[])blk.getData();
868        // Save requested block size
869        ulx = blk.ulx;
870        uly = blk.uly;
871        w = blk.w;
872        h = blk.h;
873        // Force internal data buffer to be different from external
874        blk.setData(null);
875        getInternCompData(blk,c);
876        // Copy the data
877        if (bakarr == null) {
878            bakarr = new int[w*h];
879        }
880        if (blk.offset == 0 && blk.scanw == w) {
881            // Requested and returned block buffer are the same size
882            System.arraycopy(blk.getData(),0,bakarr,0,w*h);
883        }
884        else { // Requested and returned block are different
885            for (int i=h-1; i>=0; i--) { // copy line by line
886                System.arraycopy(blk.getData(),blk.offset+i*blk.scanw,
887                                 bakarr,i*w,w);
888            }
889        }
890        blk.setData(bakarr);
891        blk.offset = 0;
892        blk.scanw = blk.w;
893        return blk;
894    }
895
896    /**
897     * Returns true if the data read was originally signed in the specified
898     * component, false if not. This method always returns false since PPM
899     * data is always unsigned.
900     *
901     * @param c The index of the component, from 0 to N-1.
902     *
903     * @return always false, since PPM data is always unsigned.
904     * */
905    public boolean isOrigSigned(int c) {
906        if (isBinary) return true;
907
908        // Check component index
909        SampleModel sm = null;
910        if (inputIsRaster)
911            sm = raster.getSampleModel();
912        else
913            sm = src.getSampleModel();
914
915        if (sm.getDataType() == DataBuffer.TYPE_USHORT ||
916            sm.getDataType() == DataBuffer.TYPE_BYTE)
917            return false;
918        return true;
919    }
920
921    private int getNumXTiles() {
922        int x = destinationRegion.x;
923        int tx = tileXOffset;
924        int tw = tileWidth;
925        return ToTile(x + destinationRegion.width - 1, tx, tw) - ToTile(x, tx, tw) + 1;
926    }
927
928    private int getNumYTiles() {
929        int y = destinationRegion.y;
930        int ty = tileYOffset;
931        int th = tileHeight;
932        return ToTile(y + destinationRegion.height - 1, ty, th) - ToTile(y, ty, th) + 1;
933    }
934
935    private static int ToTile(int pos, int tileOffset, int tileSize) {
936        pos -= tileOffset;
937        if (pos < 0) {
938            pos += 1 - tileSize;         // force round to -infinity (ceiling)
939        }
940        return pos/tileSize;
941    }
942
943    private Raster getTile(int tileX, int tileY) {
944        int sx = tileXOffset + tileX * tileWidth;
945        int sy = tileYOffset + tileY * tileHeight;
946        tileX += tileXOffset / tileWidth;
947        tileY += tileYOffset / tileHeight;
948
949        if (inputIsRaster) {
950            if (noTransform) {
951                return raster.createChild(sx, sy, getTileWidth(), getTileHeight(),
952                                          sx, sy, sourceBands);
953            }
954
955            WritableRaster ras =
956                Raster.createWritableRaster(sm, new Point(sx, sy));
957
958            int x = mapToSourceX(sx);
959            int y = mapToSourceY(sy);
960
961            int minY = raster.getMinY();
962            int maxY = raster.getMinY() + raster.getHeight();
963
964            int cTileWidth = getTileWidth();
965            for (int j = 0; j < getTileHeight(); j++, sy++, y += scaleY) {
966                if (y < minY || y >= maxY)
967                    continue;
968                Raster source = raster.createChild(x, y, (cTileWidth - 1) * scaleX + 1, 1,
969                                                   x, y, null);
970                int tempX = sx;
971                for (int i = 0, offset = x; i < cTileWidth; i++, tempX++, offset += scaleX) {
972                    for (int k = 0; k < nc; k++) {
973                        int p = source.getSample(offset, y, sourceBands[k]);
974                        ras.setSample(tempX, sy, k, p);
975                    }
976                }
977            }
978
979            return ras;
980
981        } else {
982            if (noTransform) {
983                Raster ras = src.getTile(tileX, tileY);
984                if (noSubband)
985                    return ras;
986                else {
987                    return ras.createChild(sx, sy, tileWidth, tileHeight,
988                                           sx, sy, sourceBands);
989                }
990            }
991
992            WritableRaster ras = Raster.createWritableRaster(sm, new Point(sx, sy));
993
994            int x = mapToSourceX(sx);
995            int y = mapToSourceY(sy);
996
997            int minY = src.getMinY();
998            int maxY = src.getMinY() + src.getHeight();
999            int length = tileWidth * scaleX;
1000
1001            if (x + length >= src.getWidth())
1002                length = src.getWidth() - x;
1003            int dLength = (length + scaleX -1 ) / scaleX;
1004
1005            for (int j = 0; j < tileHeight; j++, sy++, y += scaleY) {
1006                if (y < minY || y >= maxY)
1007                    continue;
1008
1009                Raster source = src.getData(new Rectangle(x, y, length, 1));
1010
1011                int tempX = sx;
1012                for (int i = 0, offset = x; i < dLength; i++, tempX++, offset += scaleX) {
1013
1014                    for (int k = 0; k < nc; k++) {
1015                        int p = source.getSample(offset, y, sourceBands[k]);
1016
1017                        ras.setSample(tempX, sy, k, p);
1018                    }
1019                }
1020            }
1021            return ras;
1022        }
1023    }
1024
1025    private int mapToSourceX(int x) {
1026        return x * scaleX + xOffset;
1027    }
1028
1029    private int mapToSourceY(int y) {
1030        return y * scaleY + yOffset;
1031    }
1032}