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