001/*
002 * $RCSfile: RawImageWriter.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.1 $
042 * $Date: 2005/02/11 05:01:42 $
043 * $State: Exp $
044 */
045package com.github.jaiimageio.impl.plugins.raw;
046
047import java.awt.Point;
048import java.awt.Rectangle;
049import java.awt.image.BandedSampleModel;
050import java.awt.image.ColorModel;
051import java.awt.image.ComponentSampleModel;
052import java.awt.image.DataBuffer;
053import java.awt.image.DataBufferByte;
054import java.awt.image.DataBufferShort;
055import java.awt.image.DataBufferUShort;
056import java.awt.image.DataBufferInt;
057import java.awt.image.DataBufferFloat;
058import java.awt.image.DataBufferDouble;
059import java.awt.image.IndexColorModel;
060import java.awt.image.MultiPixelPackedSampleModel;
061import java.awt.image.Raster;
062import java.awt.image.RenderedImage;
063import java.awt.image.SampleModel;
064import java.awt.image.SinglePixelPackedSampleModel;
065import java.awt.image.WritableRaster;
066import java.io.IOException;
067
068import javax.imageio.IIOImage;
069import javax.imageio.IIOException;
070import javax.imageio.ImageTypeSpecifier;
071import javax.imageio.ImageWriteParam;
072import javax.imageio.ImageWriter;
073import javax.imageio.metadata.IIOMetadata;
074import javax.imageio.metadata.IIOMetadataNode;
075import javax.imageio.metadata.IIOMetadataFormatImpl;
076import javax.imageio.metadata.IIOInvalidTreeException;
077import javax.imageio.spi.ImageWriterSpi;
078import javax.imageio.stream.ImageOutputStream;
079
080import com.github.jaiimageio.impl.common.ImageUtil;
081
082/**
083 * The Java Image IO plugin writer for encoding a binary RenderedImage into
084 * a Raw format.
085 *
086 * <p> The encoding process may clip, subsample or select bands using the
087 * parameters specified in the <code>ImageWriteParam</code>.
088 *
089 * Thus, when read this raw image the proper image data type
090 * should be provided.
091 *
092 * @see com.github.jaiimageio.plugins.RawImageWriteParam
093 */
094
095 // <p> If the destination data is packed packed, the data is written in the
096 // order defined by the sample model. That the data is packed is defined as
097 // (1) if the sample model is <code>SingleSamplePackedSampleModel</code> or
098 // <code>BandedSampleModel</code>; or (2) the pixel stride or sanline stride
099 // equals to the band number; or (3) the pixel stride equals to the band
100 // number multiply the tile height; or (4) the scanline stride equals to the
101 // band number multiply the tile width; or (5) the data for a band is stored
102 // in a separate data bank.
103 //
104 // <p> Otherwise, the data is reordered in a packed pixel interleaved format,
105 // and then written into the stream.  In this case the data order may be change.
106 // For example, the original image data is in the order of
107 // <pre>
108 //        RRRRRRRRRRRRRRRRRRRR
109 //        GGGGGGGGGGGGGGGGGGGG
110 //        BBBBBBBBBBBBBBBBBBBB
111 //        RRRRRRRRRRRRRRRRRRRR
112 //        GGGGGGGGGGGGGGGGGGGG
113 //        BBBBBBBBBBBBBBBBBBBB
114 // </pre>
115 //
116 // , and only the G and B bands are written in the stream.  So the data in the
117 // stream will be in the order of
118 // <pre>
119 //       GBGBGBGBGBGBGB
120 // </pre>.
121public class RawImageWriter extends ImageWriter {
122    /** The output stream to write into */
123    private ImageOutputStream stream = null;
124
125    /** The image index in this stream. */
126    private int imageIndex;
127
128    /** The tile width for encoding */
129    private int tileWidth;
130
131    /** The tile height for encoding */
132    private int tileHeight;
133
134    /** The tile grid offset for encoding */
135    private int tileXOffset, tileYOffset;
136
137    /** The source -> destination transformation */
138    private int scaleX, scaleY, xOffset, yOffset;
139
140    /** The source bands to be encoded. */
141    private int[] sourceBands = null;
142
143    /** The number of components in the image */
144    private int numBands;
145
146    /** The source raster if write raster. */
147    private RenderedImage input;
148
149    /** The input source raster. */
150    private Raster inputRaster;
151
152    private Rectangle destinationRegion = null;
153
154    private SampleModel sampleModel;
155
156    /** Coordinate transform or sub selection is needed before encoding. */
157    private boolean noTransform = true;
158    private boolean noSubband = true;
159
160    /** Indicates a <code>raster</code> rather than a <code>RenderedImage</code>
161     *  to be encoded.
162     */
163    private boolean writeRaster = false;
164
165    /** Whether can write optimally. */
166    private boolean optimal = false;
167
168    /** The strides for pixel, band, and scanline. */
169    private int pxlStride, lineStride, bandStride;
170
171    /** Constructs <code>RawImageWriter</code> based on the provided
172     *  <code>ImageWriterSpi</code>.
173     */
174    public RawImageWriter(ImageWriterSpi originator) {
175        super(originator);
176    }
177
178    public void setOutput(Object output) {
179        super.setOutput(output); // validates output
180        if (output != null) {
181            if (!(output instanceof ImageOutputStream))
182                throw new IllegalArgumentException(I18N.getString("RawImageWriter0"));
183            this.stream = (ImageOutputStream)output;
184        } else
185            this.stream = null;
186    }
187
188    public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
189        return null;
190    }
191
192    public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType,
193                                               ImageWriteParam param) {
194        return null;
195    }
196
197    public IIOMetadata convertStreamMetadata(IIOMetadata inData,
198                                             ImageWriteParam param) {
199        return null;
200    }
201
202    public IIOMetadata convertImageMetadata(IIOMetadata metadata,
203                                            ImageTypeSpecifier type,
204                                            ImageWriteParam param) {
205        return null;
206    }
207
208    public boolean canWriteRasters() {
209        return true;
210    }
211
212    public ImageWriteParam getDefaultWriteParam() {
213        return new RawImageWriteParam(getLocale());
214    }
215
216    public void write(IIOMetadata streamMetadata,
217                      IIOImage image,
218                      ImageWriteParam param) throws IOException {
219        clearAbortRequest();
220        processImageStarted(imageIndex++);
221
222        if (param == null)
223            param = getDefaultWriteParam();
224
225        writeRaster = image.hasRaster();
226        Rectangle sourceRegion = param.getSourceRegion();
227        ColorModel colorModel = null;
228        Rectangle originalRegion = null;
229
230        if (writeRaster) {
231            inputRaster = image.getRaster();
232            sampleModel = inputRaster.getSampleModel();
233            originalRegion = inputRaster.getBounds();
234        } else {
235            input = image.getRenderedImage();
236            sampleModel = input.getSampleModel();
237
238            originalRegion = new Rectangle(input.getMinX(), input.getMinY(),
239                                           input.getWidth(), input.getHeight());
240
241            colorModel = input.getColorModel();
242        }
243
244        if (sourceRegion == null)
245            sourceRegion = (Rectangle)originalRegion.clone();
246        else
247            sourceRegion = sourceRegion.intersection(originalRegion);
248
249        if (sourceRegion.isEmpty())
250            throw new RuntimeException(I18N.getString("RawImageWriter1"));
251
252        scaleX = param.getSourceXSubsampling();
253        scaleY = param.getSourceYSubsampling();
254        xOffset = param.getSubsamplingXOffset();
255        yOffset = param.getSubsamplingYOffset();
256
257        sourceRegion.translate(xOffset, yOffset);
258        sourceRegion.width -= xOffset;
259        sourceRegion.height -= yOffset;
260
261        xOffset = sourceRegion.x % scaleX;
262        yOffset = sourceRegion.y % scaleY;
263
264        int minX = sourceRegion.x / scaleX;
265        int minY = sourceRegion.y / scaleY;
266        int w = (sourceRegion.width + scaleX - 1) / scaleX;
267        int h = (sourceRegion.height + scaleY - 1) / scaleY;
268
269        destinationRegion = new Rectangle(minX, minY, w, h);
270        noTransform = destinationRegion.equals(originalRegion);
271
272        tileHeight = sampleModel.getHeight();
273        tileWidth = sampleModel.getWidth();
274        if (noTransform) {
275            if (writeRaster) {
276                tileXOffset = inputRaster.getMinX();
277                tileYOffset = inputRaster.getMinY();
278            } else {
279                tileXOffset = input.getTileGridXOffset();
280                tileYOffset = input.getTileGridYOffset();
281            }
282        } else {
283            tileXOffset = destinationRegion.x;
284            tileYOffset = destinationRegion.y;
285        }
286
287        sourceBands = param.getSourceBands();
288        boolean noSubband = true;
289        numBands = sampleModel.getNumBands();
290
291        if (sourceBands != null) {
292            sampleModel = sampleModel.createSubsetSampleModel(sourceBands);
293            colorModel = null;
294            noSubband = false;
295            numBands = sampleModel.getNumBands();
296        } else {
297            sourceBands = new int[numBands];
298            for (int i = 0; i < numBands; i++)
299                sourceBands[i] = i;
300        }
301
302        if (sampleModel instanceof ComponentSampleModel) {
303            ComponentSampleModel csm = (ComponentSampleModel)sampleModel;
304            int[] bandOffsets = csm.getBandOffsets();
305
306            bandStride = bandOffsets[0];
307
308            for (int i = 1; i < bandOffsets.length; i++)
309                if (bandStride > bandOffsets[i])
310                    bandStride = bandOffsets[i];
311
312            int[] bankIndices = csm.getBankIndices();
313            int numBank = bankIndices[0];
314            for (int i = 1; i < bankIndices.length; i++)
315                if (numBank > bankIndices[i])
316                    numBank = bankIndices[i];
317
318            pxlStride = csm.getPixelStride();
319            lineStride = csm.getScanlineStride();
320
321            optimal = bandStride == 0 ||
322                      (pxlStride < lineStride && pxlStride == numBands) ||
323                      (lineStride < pxlStride && lineStride == numBands) ||
324                      (pxlStride < lineStride &&
325                       lineStride == numBands * csm.getWidth()) ||
326                      (lineStride < pxlStride &&
327                       pxlStride == numBands * csm.getHeight()) ||
328                      csm instanceof BandedSampleModel;
329        } else if (sampleModel instanceof SinglePixelPackedSampleModel ||
330                   sampleModel instanceof MultiPixelPackedSampleModel) {
331            optimal = true;
332        }
333
334        int numXTiles = getMaxTileX() - getMinTileX() + 1;
335        int totalTiles = numXTiles * (getMaxTileY() - getMinTileY() + 1);
336
337        for (int y = getMinTileY(); y <= getMaxTileY(); y++) {
338            for (int x = getMinTileX(); x <= getMaxTileX(); x++) {
339                writeRaster(getTile(x, y));
340
341                float percentage = (x + y * numXTiles + 1.0F) / totalTiles;
342                processImageProgress(percentage * 100.0F);
343            }
344        }
345
346        stream.flush();
347        if (abortRequested())
348            processWriteAborted();
349        else
350            processImageComplete();
351    }
352
353    //XXX: just for test
354    public int getWidth() {
355        return destinationRegion.width;
356    }
357
358    public int getHeight() {
359        return destinationRegion.height;
360    }
361
362    private void writeRaster(Raster raster) throws IOException {
363        int numBank = 0;
364        int bandStride = 0;
365        int[] bankIndices = null;
366        int[] bandOffsets = null;
367        int bandSize = 0;
368        int numBand = sampleModel.getNumBands();
369        int type = sampleModel.getDataType();
370
371        if (sampleModel instanceof ComponentSampleModel) {
372            ComponentSampleModel csm = (ComponentSampleModel)sampleModel;
373
374            bandOffsets = csm.getBandOffsets();
375            for (int i = 0; i < numBand; i++)
376                if (bandStride < bandOffsets[i])
377                    bandStride = bandOffsets[i];
378
379            bankIndices = csm.getBankIndices();
380            for (int i = 0; i < numBand; i++)
381                if (numBank < bankIndices[i])
382                    numBank = bankIndices[i];
383
384            bandSize = (int)ImageUtil.getBandSize(sampleModel) ;
385        }
386
387        byte[] bdata = null;
388        short[] sdata = null;
389        int[] idata = null;
390        float[] fdata = null;
391        double[] ddata = null;
392
393        if (raster.getParent() != null &&
394            !sampleModel.equals(raster.getParent().getSampleModel())) {
395            WritableRaster ras =
396                Raster.createWritableRaster(sampleModel,
397                                            new Point(raster.getMinX(),
398                                                      raster.getMinY()));
399            ras.setRect(raster);
400            raster = ras;
401        }
402
403        DataBuffer data = raster.getDataBuffer();
404
405        if (optimal) {
406            if (numBank > 0) {  //multiple data bank
407                for (int i = 0; i < numBands; i++) {
408                    int bank = bankIndices[sourceBands[i]];
409                    switch (type) {
410                    case DataBuffer.TYPE_BYTE:
411                        bdata = ((DataBufferByte)data).getData(bank);
412                        stream.write(bdata, 0, bdata.length);
413                        break;
414                    case DataBuffer.TYPE_SHORT:
415                        sdata = ((DataBufferShort)data).getData(bank);
416                        stream.writeShorts(sdata, 0, sdata.length);
417                        break;
418                    case DataBuffer.TYPE_USHORT:
419                        sdata = ((DataBufferUShort)data).getData(bank);
420                        stream.writeShorts(sdata, 0, sdata.length);
421                        break;
422                    case DataBuffer.TYPE_INT:
423                        idata = ((DataBufferInt)data).getData(bank);
424                        stream.writeInts(idata, 0, idata.length);
425                        break;
426                    case DataBuffer.TYPE_FLOAT:
427                        fdata = ((DataBufferFloat)data).getData(bank);
428                        stream.writeFloats(fdata, 0, fdata.length);
429                        break;
430                    case DataBuffer.TYPE_DOUBLE:
431                        ddata = ((DataBufferDouble)data).getData(bank);
432                        stream.writeDoubles(ddata, 0, ddata.length);
433                        break;
434                    }
435                }
436            } else {  // Single data bank
437                switch (type) {
438                    case DataBuffer.TYPE_BYTE:
439                        bdata = ((DataBufferByte)data).getData();
440                        break;
441                    case DataBuffer.TYPE_SHORT:
442                        sdata = ((DataBufferShort)data).getData();
443                        break;
444                    case DataBuffer.TYPE_USHORT:
445                        sdata = ((DataBufferUShort)data).getData();
446                        break;
447                    case DataBuffer.TYPE_INT:
448                        idata = ((DataBufferInt)data).getData();
449                        break;
450                    case DataBuffer.TYPE_FLOAT:
451                        fdata = ((DataBufferFloat)data).getData();
452                        break;
453                    case DataBuffer.TYPE_DOUBLE:
454                        ddata = ((DataBufferDouble)data).getData();
455                        break;
456                }
457
458                if (!noSubband &&
459                    bandStride >= raster.getWidth() *
460                                  raster.getHeight() * (numBands-1)) {
461
462                    for (int i = 0; i < numBands; i++) {
463                        int offset = bandOffsets[sourceBands[i]];
464                        switch (type) {
465                        case DataBuffer.TYPE_BYTE:
466                            stream.write(bdata, offset, bandSize);
467                            break;
468                        case DataBuffer.TYPE_SHORT:
469                        case DataBuffer.TYPE_USHORT:
470                            stream.writeShorts(sdata, offset, bandSize);
471                            break;
472                        case DataBuffer.TYPE_INT:
473                            stream.writeInts(idata, offset, bandSize);
474                            break;
475                        case DataBuffer.TYPE_FLOAT:
476                            stream.writeFloats(fdata, offset, bandSize);
477                            break;
478                        case DataBuffer.TYPE_DOUBLE:
479                            stream.writeDoubles(ddata, offset, bandSize);
480                            break;
481                        }
482                    }
483                } else {
484                    switch (type) {
485                        case DataBuffer.TYPE_BYTE:
486                            stream.write(bdata, 0, bdata.length);
487                            break;
488                        case DataBuffer.TYPE_SHORT:
489                        case DataBuffer.TYPE_USHORT:
490                            stream.writeShorts(sdata, 0, sdata.length);
491                            break;
492                        case DataBuffer.TYPE_INT:
493                            stream.writeInts(idata, 0, idata.length);
494                            break;
495                        case DataBuffer.TYPE_FLOAT:
496                            stream.writeFloats(fdata, 0, fdata.length);
497                            break;
498                        case DataBuffer.TYPE_DOUBLE:
499                            stream.writeDoubles(ddata, 0, ddata.length);
500                            break;
501                    }
502                }
503            }
504        } else if (sampleModel instanceof ComponentSampleModel) {
505            // The others, must be a ComponentSampleModel
506            switch (type) {
507                case DataBuffer.TYPE_BYTE:
508                    bdata = ((DataBufferByte)data).getData();
509                    break;
510                case DataBuffer.TYPE_SHORT:
511                    sdata = ((DataBufferShort)data).getData();
512                    break;
513                case DataBuffer.TYPE_USHORT:
514                    sdata = ((DataBufferUShort)data).getData();
515                    break;
516                case DataBuffer.TYPE_INT:
517                    idata = ((DataBufferInt)data).getData();
518                    break;
519                case DataBuffer.TYPE_FLOAT:
520                    fdata = ((DataBufferFloat)data).getData();
521                    break;
522                case DataBuffer.TYPE_DOUBLE:
523                    ddata = ((DataBufferDouble)data).getData();
524                    break;
525            }
526
527            ComponentSampleModel csm = (ComponentSampleModel)sampleModel;
528            int offset =
529                csm.getOffset(raster.getMinX()-raster.getSampleModelTranslateX(),
530                              raster.getMinY()-raster.getSampleModelTranslateY())
531                              - bandOffsets[0];
532
533            int srcSkip = pxlStride;
534            int copyLength = 1;
535            int innerStep = pxlStride;
536
537            int width = raster.getWidth();
538            int height = raster.getHeight();
539
540            int innerBound = width;
541            int outerBound = height;
542
543            if (srcSkip < lineStride) {
544                if (bandStride > pxlStride)
545                    copyLength = width;
546                srcSkip = lineStride;
547            } else {
548                if (bandStride > lineStride)
549                    copyLength = height;
550                innerStep = lineStride;
551                innerBound = height;
552                outerBound = width;
553            }
554
555            int writeLength = innerBound * numBands;
556            byte[] destBBuf = null;
557            short[] destSBuf = null;
558            int[] destIBuf = null;
559            float[] destFBuf = null;
560            double[] destDBuf = null;
561            Object srcBuf = null;
562            Object dstBuf = null;
563
564            switch (type) {
565            case DataBuffer.TYPE_BYTE:
566                srcBuf = bdata;
567                dstBuf = destBBuf = new byte[writeLength];
568                break;
569            case DataBuffer.TYPE_SHORT:
570            case DataBuffer.TYPE_USHORT:
571                srcBuf = sdata;
572                dstBuf = destSBuf = new short[writeLength];
573                break;
574            case DataBuffer.TYPE_INT:
575                srcBuf = idata;
576                dstBuf = destIBuf = new int[writeLength];
577                break;
578            case DataBuffer.TYPE_FLOAT:
579                srcBuf = fdata;
580                dstBuf = destFBuf = new float[writeLength];
581                break;
582            case DataBuffer.TYPE_DOUBLE:
583                srcBuf = ddata;
584                dstBuf = destDBuf = new double[writeLength];
585                break;
586            }
587
588            if (copyLength > 1) {
589                for (int i = 0; i < outerBound; i++) {
590                    for (int b = 0; b < numBands; b++) {
591                        int bandOffset = bandOffsets[b];
592
593                        System.arraycopy(srcBuf, offset + bandOffset,
594                                         dstBuf, b * innerBound, innerBound);
595                    }
596
597                    switch (type) {
598                    case DataBuffer.TYPE_BYTE:
599                        stream.write((byte[])dstBuf, 0, writeLength);
600                        break;
601                    case DataBuffer.TYPE_SHORT:
602                    case DataBuffer.TYPE_USHORT:
603                        stream.writeShorts((short[])dstBuf, 0, writeLength);
604                        break;
605                    case DataBuffer.TYPE_INT:
606                        stream.writeInts((int[])dstBuf, 0, writeLength);
607                        break;
608                    case DataBuffer.TYPE_FLOAT:
609                        stream.writeFloats((float[])dstBuf, 0, writeLength);
610                        break;
611                    case DataBuffer.TYPE_DOUBLE:
612                        stream.writeDoubles((double[])dstBuf, 0, writeLength);
613                        break;
614                    }
615                    offset += srcSkip;
616                }
617            } else {
618                switch (type) {
619                    case DataBuffer.TYPE_BYTE: {
620                        for (int i = 0; i < outerBound; i++) {
621                            for (int b = 0, k = 0; b < numBands; b++) {
622                                int bandOffset = bandOffsets[b];
623
624                                for (int j = 0, m = offset; j < innerBound;
625                                     j++, m += innerStep)
626                                    // copy one sample to the destination buffer
627                                    destBBuf[k++] = bdata[m + bandOffset];
628                            }
629
630                            stream.write(destBBuf, 0, writeLength);
631                            offset += srcSkip;
632                        }
633                    }
634                    break;
635                    case DataBuffer.TYPE_SHORT:
636                    case DataBuffer.TYPE_USHORT: {
637                        for (int i = 0; i < outerBound; i++) {
638                            for (int b = 0, k = 0; b < numBands; b++) {
639                                int bandOffset = bandOffsets[b];
640
641                                for (int j = 0, m = offset; j < innerBound;
642                                     j++, m += innerStep)
643                                    // copy one sample to the destination buffer
644                                    destSBuf[k++] = sdata[m + bandOffset];
645                            }
646
647                            stream.writeShorts(destSBuf, 0, writeLength);
648                            offset += srcSkip;
649                        }
650                    }
651                    break;
652                    case DataBuffer.TYPE_INT: {
653                        for (int i = 0; i < outerBound; i++) {
654                            for (int b = 0, k = 0; b < numBands; b++) {
655                                int bandOffset = bandOffsets[b];
656
657                                for (int j = 0, m = offset; j < innerBound;
658                                     j++, m += innerStep)
659                                    // copy one sample to the destination buffer
660                                    destIBuf[k++] = idata[m + bandOffset];
661                            }
662
663                            stream.writeInts(destIBuf, 0, writeLength);
664                            offset += srcSkip;
665                        }
666                    }
667                    break;
668                    case DataBuffer.TYPE_FLOAT: {
669                        for (int i = 0; i < outerBound; i++) {
670                            for (int b = 0, k = 0; b < numBands; b++) {
671                                int bandOffset = bandOffsets[b];
672
673                                for (int j = 0, m = offset; j < innerBound;
674                                     j++, m += innerStep)
675                                    // copy one sample to the destination buffer
676                                    destFBuf[k++] = fdata[m + bandOffset];
677                            }
678
679                            stream.writeFloats(destFBuf, 0, writeLength);
680                            offset += srcSkip;
681                        }
682                    }
683                    break;
684                    case DataBuffer.TYPE_DOUBLE: {
685                        for (int i = 0; i < outerBound; i++) {
686                            for (int b = 0, k = 0; b < numBands; b++) {
687                                int bandOffset = bandOffsets[b];
688
689                                for (int j = 0, m = offset; j < innerBound;
690                                     j++, m += innerStep)
691                                    // copy one sample to the destination buffer
692                                    destDBuf[k++] = ddata[m + bandOffset];
693                            }
694
695                            stream.writeDoubles(destDBuf, 0, writeLength);
696                            offset += srcSkip;
697                        }
698                    }
699                    break;
700                }
701            }
702        }
703    }
704
705    private Raster getTile(int tileX, int tileY) {
706        int sx = tileXOffset + tileX * tileWidth;
707        int sy = tileYOffset + tileY * tileHeight;
708        Rectangle bounds = new Rectangle(sx, sy, tileWidth, tileHeight);
709
710        if (writeRaster) {
711            bounds = bounds.intersection(destinationRegion);
712            if (noTransform) {
713                return inputRaster.createChild(bounds.x, bounds.y,
714                                           bounds.width, bounds.height,
715                                           bounds.x, bounds.y, sourceBands);
716            }
717
718            sx = bounds.x;
719            sy = bounds.y;
720
721            WritableRaster ras =
722                Raster.createWritableRaster(sampleModel, new Point(sx, sy));
723
724            int x = mapToSourceX(sx);
725            int y = mapToSourceY(sy);
726
727            int minY = inputRaster.getMinY();
728            int maxY = inputRaster.getMinY() + inputRaster.getHeight();
729
730            int cTileWidth = bounds.width;
731
732            int length = (cTileWidth - 1) * scaleX + 1;
733
734            for (int j = 0; j < bounds.height; j++, sy++, y += scaleY) {
735                if (y < minY || y >= maxY)
736                    continue;
737                Raster source =
738                    inputRaster.createChild(x, y, length, 1,
739                                            x, y, null);
740                int tempX = sx;
741                for (int i = 0, offset = x; i < cTileWidth;
742                    i++, tempX++, offset += scaleX) {
743                    for (int k = 0; k < numBands; k++) {
744                        int p = source.getSample(offset, y, sourceBands[k]);
745                        ras.setSample(tempX, sy, k, p);
746                    }
747                }
748            }
749
750            return ras;
751
752        } else {
753            if (noTransform) {
754                Raster ras = input.getTile(tileX, tileY);
755                if (destinationRegion.contains(bounds) && noSubband)
756                    return ras;
757                else {
758                    bounds = bounds.intersection(destinationRegion);
759                    return ras.createChild(bounds.x, bounds.y,
760                                           bounds.width, bounds.height,
761                                           bounds.x, bounds.y, sourceBands);
762                }
763            }
764
765            bounds = bounds.intersection(destinationRegion);
766            sx = bounds.x;
767            sy = bounds.y;
768
769            WritableRaster ras =
770                Raster.createWritableRaster(sampleModel, new Point(sx, sy));
771
772            int x = mapToSourceX(sx);
773            int y = mapToSourceY(sy);
774
775            int minY = input.getMinY();
776            int maxY = input.getMinY() + input.getHeight();
777
778            int cTileWidth = bounds.width;
779            int length = (cTileWidth -1) * scaleX + 1;
780
781            for (int j = 0; j < bounds.height; j++, sy++, y += scaleY) {
782                if (y < minY || y >= maxY)
783                    continue;
784
785                Raster source =
786                    input.getData(new Rectangle(x, y, length, 1));
787
788                int tempX = sx;
789                for (int i = 0, offset = x; i < cTileWidth;
790                    i++, tempX++, offset += scaleX) {
791                    for (int k = 0; k < numBands; k++) {
792                        int p = source.getSample(offset, y, sourceBands[k]);
793                        ras.setSample(tempX, sy, k, p);
794                    }
795                }
796            }
797            return ras;
798        }
799    }
800
801    private int mapToSourceX(int x) {
802        return x * scaleX + xOffset;
803    }
804
805    private int mapToSourceY(int y) {
806        return y * scaleY + yOffset;
807    }
808
809    private int getMinTileX() {
810        return ToTile(destinationRegion.x, tileXOffset, tileWidth);
811    }
812
813    private int getMaxTileX() {
814        return ToTile(destinationRegion.x + destinationRegion.width - 1,
815                      tileXOffset, tileWidth);
816    }
817
818    private int getMinTileY() {
819        return ToTile(destinationRegion.y, tileYOffset, tileHeight);
820    }
821
822    private int getMaxTileY() {
823        return ToTile(destinationRegion.y + destinationRegion.height - 1,
824                      tileYOffset, tileHeight);
825    }
826
827    private static int ToTile(int pos, int tileOffset, int tileSize) {
828        pos -= tileOffset;
829        if (pos < 0) {
830            pos += 1 - tileSize;         // force round to -infinity (ceiling)
831        }
832        return pos/tileSize;
833    }
834
835    public void reset() {
836        super.reset();
837        stream = null;
838        optimal = false;
839        sourceBands = null;
840        destinationRegion = null;
841        noTransform = true;
842        noSubband = true;
843        writeRaster = false;
844    }
845}