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