1/*
2 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/* ****************************************************************
27 ******************************************************************
28 ******************************************************************
29 *** COPYRIGHT (c) Eastman Kodak Company, 1997
30 *** As  an unpublished  work pursuant to Title 17 of the United
31 *** States Code.  All rights reserved.
32 ******************************************************************
33 ******************************************************************
34 ******************************************************************/
35
36package java.awt.image;
37
38import java.util.Arrays;
39
40/**
41 *  This class represents image data which is stored such that each sample
42 *  of a pixel occupies one data element of the DataBuffer.  It stores the
43 *  N samples which make up a pixel in N separate data array elements.
44 *  Different bands may be in different banks of the DataBuffer.
45 *  Accessor methods are provided so that image data can be manipulated
46 *  directly. This class can support different kinds of interleaving, e.g.
47 *  band interleaving, scanline interleaving, and pixel interleaving.
48 *  Pixel stride is the number of data array elements between two samples
49 *  for the same band on the same scanline. Scanline stride is the number
50 *  of data array elements between a given sample and the corresponding sample
51 *  in the same column of the next scanline.  Band offsets denote the number
52 *  of data array elements from the first data array element of the bank
53 *  of the DataBuffer holding each band to the first sample of the band.
54 *  The bands are numbered from 0 to N-1.  This class can represent image
55 *  data for which each sample is an unsigned integral number which can be
56 *  stored in 8, 16, or 32 bits (using {@code DataBuffer.TYPE_BYTE},
57 *  {@code DataBuffer.TYPE_USHORT}, or {@code DataBuffer.TYPE_INT},
58 *  respectively), data for which each sample is a signed integral number
59 *  which can be stored in 16 bits (using {@code DataBuffer.TYPE_SHORT}),
60 *  or data for which each sample is a signed float or double quantity
61 *  (using {@code DataBuffer.TYPE_FLOAT} or
62 *  {@code DataBuffer.TYPE_DOUBLE}, respectively).
63 *  All samples of a given ComponentSampleModel
64 *  are stored with the same precision.  All strides and offsets must be
65 *  non-negative.  This class supports
66 *  {@link DataBuffer#TYPE_BYTE TYPE_BYTE},
67 *  {@link DataBuffer#TYPE_USHORT TYPE_USHORT},
68 *  {@link DataBuffer#TYPE_SHORT TYPE_SHORT},
69 *  {@link DataBuffer#TYPE_INT TYPE_INT},
70 *  {@link DataBuffer#TYPE_FLOAT TYPE_FLOAT},
71 *  {@link DataBuffer#TYPE_DOUBLE TYPE_DOUBLE},
72 *  @see java.awt.image.PixelInterleavedSampleModel
73 *  @see java.awt.image.BandedSampleModel
74 */
75
76public class ComponentSampleModel extends SampleModel
77{
78    /** Offsets for all bands in data array elements. */
79    protected int bandOffsets[];
80
81    /** Index for each bank storing a band of image data. */
82    protected int[] bankIndices;
83
84    /**
85     * The number of bands in this
86     * {@code ComponentSampleModel}.
87     */
88    protected int numBands = 1;
89
90    /**
91     * The number of banks in this
92     * {@code ComponentSampleModel}.
93     */
94    protected int numBanks = 1;
95
96    /**
97     *  Line stride (in data array elements) of the region of image
98     *  data described by this ComponentSampleModel.
99     */
100    protected int scanlineStride;
101
102    /** Pixel stride (in data array elements) of the region of image
103     *  data described by this ComponentSampleModel.
104     */
105    protected int pixelStride;
106
107    /**
108     * Constructs a ComponentSampleModel with the specified parameters.
109     * The number of bands will be given by the length of the bandOffsets array.
110     * All bands will be stored in the first bank of the DataBuffer.
111     * @param dataType  the data type for storing samples
112     * @param w         the width (in pixels) of the region of
113     *     image data described
114     * @param h         the height (in pixels) of the region of
115     *     image data described
116     * @param pixelStride the pixel stride of the region of image
117     *     data described
118     * @param scanlineStride the line stride of the region of image
119     *     data described
120     * @param bandOffsets the offsets of all bands
121     * @throws IllegalArgumentException if {@code w} or
122     *         {@code h} is not greater than 0
123     * @throws IllegalArgumentException if {@code pixelStride}
124     *         is less than 0
125     * @throws IllegalArgumentException if {@code scanlineStride}
126     *         is less than 0
127     * @throws IllegalArgumentException if {@code numBands}
128     *         is less than 1
129     * @throws IllegalArgumentException if the product of {@code w}
130     *         and {@code h} is greater than
131     *         {@code Integer.MAX_VALUE}
132     * @throws IllegalArgumentException if {@code dataType} is not
133     *         one of the supported data types
134     */
135    public ComponentSampleModel(int dataType,
136                                int w, int h,
137                                int pixelStride,
138                                int scanlineStride,
139                                int bandOffsets[]) {
140        super(dataType, w, h, bandOffsets.length);
141        this.dataType = dataType;
142        this.pixelStride = pixelStride;
143        this.scanlineStride  = scanlineStride;
144        this.bandOffsets = bandOffsets.clone();
145        numBands = this.bandOffsets.length;
146        if (pixelStride < 0) {
147            throw new IllegalArgumentException("Pixel stride must be >= 0");
148        }
149        // TODO - bug 4296691 - remove this check
150        if (scanlineStride < 0) {
151            throw new IllegalArgumentException("Scanline stride must be >= 0");
152        }
153        if (numBands < 1) {
154            throw new IllegalArgumentException("Must have at least one band.");
155        }
156        if ((dataType < DataBuffer.TYPE_BYTE) ||
157            (dataType > DataBuffer.TYPE_DOUBLE)) {
158            throw new IllegalArgumentException("Unsupported dataType.");
159        }
160        bankIndices = new int[numBands];
161        for (int i=0; i<numBands; i++) {
162            bankIndices[i] = 0;
163        }
164        verify();
165    }
166
167
168    /**
169     * Constructs a ComponentSampleModel with the specified parameters.
170     * The number of bands will be given by the length of the bandOffsets array.
171     * Different bands may be stored in different banks of the DataBuffer.
172     *
173     * @param dataType  the data type for storing samples
174     * @param w         the width (in pixels) of the region of
175     *     image data described
176     * @param h         the height (in pixels) of the region of
177     *     image data described
178     * @param pixelStride the pixel stride of the region of image
179     *     data described
180     * @param scanlineStride The line stride of the region of image
181     *     data described
182     * @param bankIndices the bank indices of all bands
183     * @param bandOffsets the band offsets of all bands
184     * @throws IllegalArgumentException if {@code w} or
185     *         {@code h} is not greater than 0
186     * @throws IllegalArgumentException if {@code pixelStride}
187     *         is less than 0
188     * @throws IllegalArgumentException if {@code scanlineStride}
189     *         is less than 0
190     * @throws IllegalArgumentException if the length of
191     *         {@code bankIndices} does not equal the length of
192     *         {@code bankOffsets}
193     * @throws IllegalArgumentException if any of the bank indices
194     *         of {@code bandIndices} is less than 0
195     * @throws IllegalArgumentException if {@code dataType} is not
196     *         one of the supported data types
197     */
198    public ComponentSampleModel(int dataType,
199                                int w, int h,
200                                int pixelStride,
201                                int scanlineStride,
202                                int bankIndices[],
203                                int bandOffsets[]) {
204        super(dataType, w, h, bandOffsets.length);
205        this.dataType = dataType;
206        this.pixelStride = pixelStride;
207        this.scanlineStride  = scanlineStride;
208        this.bandOffsets = bandOffsets.clone();
209        this.bankIndices = bankIndices.clone();
210        if (pixelStride < 0) {
211            throw new IllegalArgumentException("Pixel stride must be >= 0");
212        }
213        // TODO - bug 4296691 - remove this check
214        if (scanlineStride < 0) {
215            throw new IllegalArgumentException("Scanline stride must be >= 0");
216        }
217        if ((dataType < DataBuffer.TYPE_BYTE) ||
218            (dataType > DataBuffer.TYPE_DOUBLE)) {
219            throw new IllegalArgumentException("Unsupported dataType.");
220        }
221        int maxBank = this.bankIndices[0];
222        if (maxBank < 0) {
223            throw new IllegalArgumentException("Index of bank 0 is less than "+
224                                               "0 ("+maxBank+")");
225        }
226        for (int i=1; i < this.bankIndices.length; i++) {
227            if (this.bankIndices[i] > maxBank) {
228                maxBank = this.bankIndices[i];
229            }
230            else if (this.bankIndices[i] < 0) {
231                throw new IllegalArgumentException("Index of bank "+i+
232                                                   " is less than 0 ("+
233                                                   maxBank+")");
234            }
235        }
236        numBanks         = maxBank+1;
237        numBands         = this.bandOffsets.length;
238        if (this.bandOffsets.length != this.bankIndices.length) {
239            throw new IllegalArgumentException("Length of bandOffsets must "+
240                                               "equal length of bankIndices.");
241        }
242        verify();
243    }
244
245    private void verify() {
246        int requiredSize = getBufferSize();
247    }
248
249    /**
250     * Returns the size of the data buffer (in data elements) needed
251     * for a data buffer that matches this ComponentSampleModel.
252     */
253     private int getBufferSize() {
254         int maxBandOff=bandOffsets[0];
255         for (int i=1; i<bandOffsets.length; i++) {
256             maxBandOff = Math.max(maxBandOff,bandOffsets[i]);
257         }
258
259         if (maxBandOff < 0 || maxBandOff > (Integer.MAX_VALUE - 1)) {
260             throw new IllegalArgumentException("Invalid band offset");
261         }
262
263         if (pixelStride < 0 || pixelStride > (Integer.MAX_VALUE / width)) {
264             throw new IllegalArgumentException("Invalid pixel stride");
265         }
266
267         if (scanlineStride < 0 || scanlineStride > (Integer.MAX_VALUE / height)) {
268             throw new IllegalArgumentException("Invalid scanline stride");
269         }
270
271         int size = maxBandOff + 1;
272
273         int val = pixelStride * (width - 1);
274
275         if (val > (Integer.MAX_VALUE - size)) {
276             throw new IllegalArgumentException("Invalid pixel stride");
277         }
278
279         size += val;
280
281         val = scanlineStride * (height - 1);
282
283         if (val > (Integer.MAX_VALUE - size)) {
284             throw new IllegalArgumentException("Invalid scan stride");
285         }
286
287         size += val;
288
289         return size;
290     }
291
292     /**
293      * Preserves band ordering with new step factor...
294      */
295    int []orderBands(int orig[], int step) {
296        int map[] = new int[orig.length];
297        int ret[] = new int[orig.length];
298
299        for (int i=0; i<map.length; i++) map[i] = i;
300
301        for (int i = 0; i < ret.length; i++) {
302            int index = i;
303            for (int j = i+1; j < ret.length; j++) {
304                if (orig[map[index]] > orig[map[j]]) {
305                    index = j;
306                }
307            }
308            ret[map[index]] = i*step;
309            map[index]  = map[i];
310        }
311        return ret;
312    }
313
314    /**
315     * Creates a new {@code ComponentSampleModel} with the specified
316     * width and height.  The new {@code SampleModel} will have the same
317     * number of bands, storage data type, interleaving scheme, and
318     * pixel stride as this {@code SampleModel}.
319     * @param w the width of the resulting {@code SampleModel}
320     * @param h the height of the resulting {@code SampleModel}
321     * @return a new {@code ComponentSampleModel} with the specified size
322     * @throws IllegalArgumentException if {@code w} or
323     *         {@code h} is not greater than 0
324     */
325    public SampleModel createCompatibleSampleModel(int w, int h) {
326        SampleModel ret=null;
327        long size;
328        int minBandOff=bandOffsets[0];
329        int maxBandOff=bandOffsets[0];
330        for (int i=1; i<bandOffsets.length; i++) {
331            minBandOff = Math.min(minBandOff,bandOffsets[i]);
332            maxBandOff = Math.max(maxBandOff,bandOffsets[i]);
333        }
334        maxBandOff -= minBandOff;
335
336        int bands   = bandOffsets.length;
337        int bandOff[];
338        int pStride = Math.abs(pixelStride);
339        int lStride = Math.abs(scanlineStride);
340        int bStride = Math.abs(maxBandOff);
341
342        if (pStride > lStride) {
343            if (pStride > bStride) {
344                if (lStride > bStride) { // pix > line > band
345                    bandOff = new int[bandOffsets.length];
346                    for (int i=0; i<bands; i++)
347                        bandOff[i] = bandOffsets[i]-minBandOff;
348                    lStride = bStride+1;
349                    pStride = lStride*h;
350                } else { // pix > band > line
351                    bandOff = orderBands(bandOffsets,lStride*h);
352                    pStride = bands*lStride*h;
353                }
354            } else { // band > pix > line
355                pStride = lStride*h;
356                bandOff = orderBands(bandOffsets,pStride*w);
357            }
358        } else {
359            if (pStride > bStride) { // line > pix > band
360                bandOff = new int[bandOffsets.length];
361                for (int i=0; i<bands; i++)
362                    bandOff[i] = bandOffsets[i]-minBandOff;
363                pStride = bStride+1;
364                lStride = pStride*w;
365            } else {
366                if (lStride > bStride) { // line > band > pix
367                    bandOff = orderBands(bandOffsets,pStride*w);
368                    lStride = bands*pStride*w;
369                } else { // band > line > pix
370                    lStride = pStride*w;
371                    bandOff = orderBands(bandOffsets,lStride*h);
372                }
373            }
374        }
375
376        // make sure we make room for negative offsets...
377        int base = 0;
378        if (scanlineStride < 0) {
379            base += lStride*h;
380            lStride *= -1;
381        }
382        if (pixelStride    < 0) {
383            base += pStride*w;
384            pStride *= -1;
385        }
386
387        for (int i=0; i<bands; i++)
388            bandOff[i] += base;
389        return new ComponentSampleModel(dataType, w, h, pStride,
390                                        lStride, bankIndices, bandOff);
391    }
392
393    /**
394     * Creates a new ComponentSampleModel with a subset of the bands
395     * of this ComponentSampleModel.  The new ComponentSampleModel can be
396     * used with any DataBuffer that the existing ComponentSampleModel
397     * can be used with.  The new ComponentSampleModel/DataBuffer
398     * combination will represent an image with a subset of the bands
399     * of the original ComponentSampleModel/DataBuffer combination.
400     * @param bands a subset of bands from this
401     *              {@code ComponentSampleModel}
402     * @return a {@code ComponentSampleModel} created with a subset
403     *          of bands from this {@code ComponentSampleModel}.
404     */
405    public SampleModel createSubsetSampleModel(int bands[]) {
406       if (bands.length > bankIndices.length)
407            throw new RasterFormatException("There are only " +
408                                            bankIndices.length +
409                                            " bands");
410        int newBankIndices[] = new int[bands.length];
411        int newBandOffsets[] = new int[bands.length];
412
413        for (int i=0; i<bands.length; i++) {
414            newBankIndices[i] = bankIndices[bands[i]];
415            newBandOffsets[i] = bandOffsets[bands[i]];
416        }
417
418        return new ComponentSampleModel(this.dataType, width, height,
419                                        this.pixelStride,
420                                        this.scanlineStride,
421                                        newBankIndices, newBandOffsets);
422    }
423
424    /**
425     * Creates a {@code DataBuffer} that corresponds to this
426     * {@code ComponentSampleModel}.
427     * The {@code DataBuffer} object's data type, number of banks,
428     * and size are be consistent with this {@code ComponentSampleModel}.
429     * @return a {@code DataBuffer} whose data type, number of banks
430     *         and size are consistent with this
431     *         {@code ComponentSampleModel}.
432     */
433    public DataBuffer createDataBuffer() {
434        DataBuffer dataBuffer = null;
435
436        int size = getBufferSize();
437        switch (dataType) {
438        case DataBuffer.TYPE_BYTE:
439            dataBuffer = new DataBufferByte(size, numBanks);
440            break;
441        case DataBuffer.TYPE_USHORT:
442            dataBuffer = new DataBufferUShort(size, numBanks);
443            break;
444        case DataBuffer.TYPE_SHORT:
445            dataBuffer = new DataBufferShort(size, numBanks);
446            break;
447        case DataBuffer.TYPE_INT:
448            dataBuffer = new DataBufferInt(size, numBanks);
449            break;
450        case DataBuffer.TYPE_FLOAT:
451            dataBuffer = new DataBufferFloat(size, numBanks);
452            break;
453        case DataBuffer.TYPE_DOUBLE:
454            dataBuffer = new DataBufferDouble(size, numBanks);
455            break;
456        }
457
458        return dataBuffer;
459    }
460
461
462    /** Gets the offset for the first band of pixel (x,y).
463     *  A sample of the first band can be retrieved from a
464     * {@code DataBuffer}
465     *  {@code data} with a {@code ComponentSampleModel}
466     * {@code csm} as
467     * <pre>
468     *        data.getElem(csm.getOffset(x, y));
469     * </pre>
470     * @param x the X location of the pixel
471     * @param y the Y location of the pixel
472     * @return the offset for the first band of the specified pixel.
473     */
474    public int getOffset(int x, int y) {
475        int offset = y*scanlineStride + x*pixelStride + bandOffsets[0];
476        return offset;
477    }
478
479    /** Gets the offset for band b of pixel (x,y).
480     *  A sample of band {@code b} can be retrieved from a
481     *  {@code DataBuffer data}
482     *  with a {@code ComponentSampleModel csm} as
483     * <pre>
484     *       data.getElem(csm.getOffset(x, y, b));
485     * </pre>
486     * @param x the X location of the specified pixel
487     * @param y the Y location of the specified pixel
488     * @param b the specified band
489     * @return the offset for the specified band of the specified pixel.
490     */
491    public int getOffset(int x, int y, int b) {
492        int offset = y*scanlineStride + x*pixelStride + bandOffsets[b];
493        return offset;
494    }
495
496    /** Returns the number of bits per sample for all bands.
497     *  @return an array containing the number of bits per sample
498     *          for all bands, where each element in the array
499     *          represents a band.
500     */
501    public final int[] getSampleSize() {
502        int sampleSize[] = new int [numBands];
503        int sizeInBits = getSampleSize(0);
504
505        for (int i=0; i<numBands; i++)
506            sampleSize[i] = sizeInBits;
507
508        return sampleSize;
509    }
510
511    /** Returns the number of bits per sample for the specified band.
512     *  @param band the specified band
513     *  @return the number of bits per sample for the specified band.
514     */
515    public final int getSampleSize(int band) {
516        return DataBuffer.getDataTypeSize(dataType);
517    }
518
519    /** Returns the bank indices for all bands.
520     *  @return the bank indices for all bands.
521     */
522    public final int [] getBankIndices() {
523        return bankIndices.clone();
524    }
525
526    /** Returns the band offset for all bands.
527     *  @return the band offsets for all bands.
528     */
529    public final int [] getBandOffsets() {
530        return bandOffsets.clone();
531    }
532
533    /** Returns the scanline stride of this ComponentSampleModel.
534     *  @return the scanline stride of this {@code ComponentSampleModel}.
535     */
536    public final int getScanlineStride() {
537        return scanlineStride;
538    }
539
540    /** Returns the pixel stride of this ComponentSampleModel.
541     *  @return the pixel stride of this {@code ComponentSampleModel}.
542     */
543    public final int getPixelStride() {
544        return pixelStride;
545    }
546
547    /**
548     * Returns the number of data elements needed to transfer a pixel
549     * with the
550     * {@link #getDataElements(int, int, Object, DataBuffer) } and
551     * {@link #setDataElements(int, int, Object, DataBuffer) }
552     * methods.
553     * For a {@code ComponentSampleModel}, this is identical to the
554     * number of bands.
555     * @return the number of data elements needed to transfer a pixel with
556     *         the {@code getDataElements} and
557     *         {@code setDataElements} methods.
558     * @see java.awt.image.SampleModel#getNumDataElements
559     * @see #getNumBands
560     */
561    public final int getNumDataElements() {
562        return getNumBands();
563    }
564
565    /**
566     * Returns data for a single pixel in a primitive array of type
567     * {@code TransferType}.  For a {@code ComponentSampleModel},
568     * this is the same as the data type, and samples are returned
569     * one per array element.  Generally, {@code obj} should
570     * be passed in as {@code null}, so that the {@code Object}
571     * is created automatically and is the right primitive data type.
572     * <p>
573     * The following code illustrates transferring data for one pixel from
574     * {@code DataBuffer db1}, whose storage layout is
575     * described by {@code ComponentSampleModel csm1},
576     * to {@code DataBuffer db2}, whose storage layout
577     * is described by {@code ComponentSampleModel csm2}.
578     * The transfer is usually more efficient than using
579     * {@code getPixel} and {@code setPixel}.
580     * <pre>
581     *       ComponentSampleModel csm1, csm2;
582     *       DataBufferInt db1, db2;
583     *       csm2.setDataElements(x, y,
584     *                            csm1.getDataElements(x, y, null, db1), db2);
585     * </pre>
586     *
587     * Using {@code getDataElements} and {@code setDataElements}
588     * to transfer between two {@code DataBuffer/SampleModel}
589     * pairs is legitimate if the {@code SampleModel} objects have
590     * the same number of bands, corresponding bands have the same number of
591     * bits per sample, and the {@code TransferType}s are the same.
592     * <p>
593     * If {@code obj} is not {@code null}, it should be a
594     * primitive array of type {@code TransferType}.
595     * Otherwise, a {@code ClassCastException} is thrown.  An
596     * {@code ArrayIndexOutOfBoundsException} might be thrown if the
597     * coordinates are not in bounds, or if {@code obj} is not
598     * {@code null} and is not large enough to hold
599     * the pixel data.
600     *
601     * @param x         the X coordinate of the pixel location
602     * @param y         the Y coordinate of the pixel location
603     * @param obj       if non-{@code null}, a primitive array
604     *                  in which to return the pixel data
605     * @param data      the {@code DataBuffer} containing the image data
606     * @return the data of the specified pixel
607     * @see #setDataElements(int, int, Object, DataBuffer)
608     *
609     * @throws NullPointerException if data is null.
610     * @throws ArrayIndexOutOfBoundsException if the coordinates are
611     * not in bounds, or if obj is too small to hold the output.
612     */
613    public Object getDataElements(int x, int y, Object obj, DataBuffer data) {
614        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
615            throw new ArrayIndexOutOfBoundsException
616                ("Coordinate out of bounds!");
617        }
618
619        int type = getTransferType();
620        int numDataElems = getNumDataElements();
621        int pixelOffset = y*scanlineStride + x*pixelStride;
622
623        switch(type) {
624
625        case DataBuffer.TYPE_BYTE:
626
627            byte[] bdata;
628
629            if (obj == null)
630                bdata = new byte[numDataElems];
631            else
632                bdata = (byte[])obj;
633
634            for (int i=0; i<numDataElems; i++) {
635                bdata[i] = (byte)data.getElem(bankIndices[i],
636                                              pixelOffset + bandOffsets[i]);
637            }
638
639            obj = (Object)bdata;
640            break;
641
642        case DataBuffer.TYPE_USHORT:
643        case DataBuffer.TYPE_SHORT:
644
645            short[] sdata;
646
647            if (obj == null)
648                sdata = new short[numDataElems];
649            else
650                sdata = (short[])obj;
651
652            for (int i=0; i<numDataElems; i++) {
653                sdata[i] = (short)data.getElem(bankIndices[i],
654                                               pixelOffset + bandOffsets[i]);
655            }
656
657            obj = (Object)sdata;
658            break;
659
660        case DataBuffer.TYPE_INT:
661
662            int[] idata;
663
664            if (obj == null)
665                idata = new int[numDataElems];
666            else
667                idata = (int[])obj;
668
669            for (int i=0; i<numDataElems; i++) {
670                idata[i] = data.getElem(bankIndices[i],
671                                        pixelOffset + bandOffsets[i]);
672            }
673
674            obj = (Object)idata;
675            break;
676
677        case DataBuffer.TYPE_FLOAT:
678
679            float[] fdata;
680
681            if (obj == null)
682                fdata = new float[numDataElems];
683            else
684                fdata = (float[])obj;
685
686            for (int i=0; i<numDataElems; i++) {
687                fdata[i] = data.getElemFloat(bankIndices[i],
688                                             pixelOffset + bandOffsets[i]);
689            }
690
691            obj = (Object)fdata;
692            break;
693
694        case DataBuffer.TYPE_DOUBLE:
695
696            double[] ddata;
697
698            if (obj == null)
699                ddata = new double[numDataElems];
700            else
701                ddata = (double[])obj;
702
703            for (int i=0; i<numDataElems; i++) {
704                ddata[i] = data.getElemDouble(bankIndices[i],
705                                              pixelOffset + bandOffsets[i]);
706            }
707
708            obj = (Object)ddata;
709            break;
710        }
711
712        return obj;
713    }
714
715    /**
716     * Returns all samples for the specified pixel in an int array,
717     * one sample per array element.
718     * An {@code ArrayIndexOutOfBoundsException} might be thrown if
719     * the coordinates are not in bounds.
720     * @param x         the X coordinate of the pixel location
721     * @param y         the Y coordinate of the pixel location
722     * @param iArray    If non-null, returns the samples in this array
723     * @param data      The DataBuffer containing the image data
724     * @return the samples of the specified pixel.
725     * @see #setPixel(int, int, int[], DataBuffer)
726     *
727     * @throws NullPointerException if data is null.
728     * @throws ArrayIndexOutOfBoundsException if the coordinates are
729     * not in bounds, or if iArray is too small to hold the output.
730     */
731    public int[] getPixel(int x, int y, int iArray[], DataBuffer data) {
732        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
733            throw new ArrayIndexOutOfBoundsException
734                ("Coordinate out of bounds!");
735        }
736        int pixels[];
737        if (iArray != null) {
738           pixels = iArray;
739        } else {
740           pixels = new int [numBands];
741        }
742        int pixelOffset = y*scanlineStride + x*pixelStride;
743        for (int i=0; i<numBands; i++) {
744            pixels[i] = data.getElem(bankIndices[i],
745                                     pixelOffset + bandOffsets[i]);
746        }
747        return pixels;
748    }
749
750    /**
751     * Returns all samples for the specified rectangle of pixels in
752     * an int array, one sample per array element.
753     * An {@code ArrayIndexOutOfBoundsException} might be thrown if
754     * the coordinates are not in bounds.
755     * @param x         The X coordinate of the upper left pixel location
756     * @param y         The Y coordinate of the upper left pixel location
757     * @param w         The width of the pixel rectangle
758     * @param h         The height of the pixel rectangle
759     * @param iArray    If non-null, returns the samples in this array
760     * @param data      The DataBuffer containing the image data
761     * @return the samples of the pixels within the specified region.
762     * @see #setPixels(int, int, int, int, int[], DataBuffer)
763     */
764    public int[] getPixels(int x, int y, int w, int h,
765                           int iArray[], DataBuffer data) {
766        int x1 = x + w;
767        int y1 = y + h;
768
769        if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width ||
770            y < 0 || y >= height || y > height || y1 < 0 || y1 >  height)
771        {
772            throw new ArrayIndexOutOfBoundsException
773                ("Coordinate out of bounds!");
774        }
775        int pixels[];
776        if (iArray != null) {
777           pixels = iArray;
778        } else {
779           pixels = new int [w*h*numBands];
780        }
781        int lineOffset = y*scanlineStride + x*pixelStride;
782        int srcOffset = 0;
783
784        for (int i = 0; i < h; i++) {
785           int pixelOffset = lineOffset;
786           for (int j = 0; j < w; j++) {
787              for (int k=0; k < numBands; k++) {
788                 pixels[srcOffset++] =
789                    data.getElem(bankIndices[k], pixelOffset + bandOffsets[k]);
790              }
791              pixelOffset += pixelStride;
792           }
793           lineOffset += scanlineStride;
794        }
795        return pixels;
796    }
797
798    /**
799     * Returns as int the sample in a specified band for the pixel
800     * located at (x,y).
801     * An {@code ArrayIndexOutOfBoundsException} might be thrown if
802     * the coordinates are not in bounds.
803     * @param x         the X coordinate of the pixel location
804     * @param y         the Y coordinate of the pixel location
805     * @param b         the band to return
806     * @param data      the {@code DataBuffer} containing the image data
807     * @return the sample in a specified band for the specified pixel
808     * @see #setSample(int, int, int, int, DataBuffer)
809     */
810    public int getSample(int x, int y, int b, DataBuffer data) {
811        // Bounds check for 'b' will be performed automatically
812        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
813            throw new ArrayIndexOutOfBoundsException
814                ("Coordinate out of bounds!");
815        }
816        int sample = data.getElem(bankIndices[b],
817                                  y*scanlineStride + x*pixelStride +
818                                  bandOffsets[b]);
819        return sample;
820    }
821
822    /**
823     * Returns the sample in a specified band
824     * for the pixel located at (x,y) as a float.
825     * An {@code ArrayIndexOutOfBoundsException} might be
826     * thrown if the coordinates are not in bounds.
827     * @param x         The X coordinate of the pixel location
828     * @param y         The Y coordinate of the pixel location
829     * @param b         The band to return
830     * @param data      The DataBuffer containing the image data
831     * @return a float value representing the sample in the specified
832     * band for the specified pixel.
833     */
834    public float getSampleFloat(int x, int y, int b, DataBuffer data) {
835        // Bounds check for 'b' will be performed automatically
836        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
837            throw new ArrayIndexOutOfBoundsException
838                ("Coordinate out of bounds!");
839        }
840
841        float sample = data.getElemFloat(bankIndices[b],
842                                         y*scanlineStride + x*pixelStride +
843                                         bandOffsets[b]);
844        return sample;
845    }
846
847    /**
848     * Returns the sample in a specified band
849     * for a pixel located at (x,y) as a double.
850     * An {@code ArrayIndexOutOfBoundsException} might be
851     * thrown if the coordinates are not in bounds.
852     * @param x         The X coordinate of the pixel location
853     * @param y         The Y coordinate of the pixel location
854     * @param b         The band to return
855     * @param data      The DataBuffer containing the image data
856     * @return a double value representing the sample in the specified
857     * band for the specified pixel.
858     */
859    public double getSampleDouble(int x, int y, int b, DataBuffer data) {
860        // Bounds check for 'b' will be performed automatically
861        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
862            throw new ArrayIndexOutOfBoundsException
863                ("Coordinate out of bounds!");
864        }
865
866        double sample = data.getElemDouble(bankIndices[b],
867                                           y*scanlineStride + x*pixelStride +
868                                           bandOffsets[b]);
869        return sample;
870    }
871
872    /**
873     * Returns the samples in a specified band for the specified rectangle
874     * of pixels in an int array, one sample per data array element.
875     * An {@code ArrayIndexOutOfBoundsException} might be thrown if
876     * the coordinates are not in bounds.
877     * @param x         The X coordinate of the upper left pixel location
878     * @param y         The Y coordinate of the upper left pixel location
879     * @param w         the width of the pixel rectangle
880     * @param h         the height of the pixel rectangle
881     * @param b         the band to return
882     * @param iArray    if non-{@code null}, returns the samples
883     *                  in this array
884     * @param data      the {@code DataBuffer} containing the image data
885     * @return the samples in the specified band of the specified pixel
886     * @see #setSamples(int, int, int, int, int, int[], DataBuffer)
887     */
888    public int[] getSamples(int x, int y, int w, int h, int b,
889                            int iArray[], DataBuffer data) {
890        // Bounds check for 'b' will be performed automatically
891        if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) {
892            throw new ArrayIndexOutOfBoundsException
893                ("Coordinate out of bounds!");
894        }
895        int samples[];
896        if (iArray != null) {
897           samples = iArray;
898        } else {
899           samples = new int [w*h];
900        }
901        int lineOffset = y*scanlineStride + x*pixelStride +  bandOffsets[b];
902        int srcOffset = 0;
903
904        for (int i = 0; i < h; i++) {
905           int sampleOffset = lineOffset;
906           for (int j = 0; j < w; j++) {
907              samples[srcOffset++] = data.getElem(bankIndices[b],
908                                                  sampleOffset);
909              sampleOffset += pixelStride;
910           }
911           lineOffset += scanlineStride;
912        }
913        return samples;
914    }
915
916    /**
917     * Sets the data for a single pixel in the specified
918     * {@code DataBuffer} from a primitive array of type
919     * {@code TransferType}.  For a {@code ComponentSampleModel},
920     * this is the same as the data type, and samples are transferred
921     * one per array element.
922     * <p>
923     * The following code illustrates transferring data for one pixel from
924     * {@code DataBuffer db1}, whose storage layout is
925     * described by {@code ComponentSampleModel csm1},
926     * to {@code DataBuffer db2}, whose storage layout
927     * is described by {@code ComponentSampleModel csm2}.
928     * The transfer is usually more efficient than using
929     * {@code getPixel} and {@code setPixel}.
930     * <pre>
931     *       ComponentSampleModel csm1, csm2;
932     *       DataBufferInt db1, db2;
933     *       csm2.setDataElements(x, y, csm1.getDataElements(x, y, null, db1),
934     *                            db2);
935     * </pre>
936     * Using {@code getDataElements} and {@code setDataElements}
937     * to transfer between two {@code DataBuffer/SampleModel} pairs
938     * is legitimate if the {@code SampleModel} objects have
939     * the same number of bands, corresponding bands have the same number of
940     * bits per sample, and the {@code TransferType}s are the same.
941     * <p>
942     * A {@code ClassCastException} is thrown if {@code obj} is not
943     * a primitive array of type {@code TransferType}.
944     * An {@code ArrayIndexOutOfBoundsException} might be thrown if
945     * the coordinates are not in bounds, or if {@code obj} is not large
946     * enough to hold the pixel data.
947     * @param x         the X coordinate of the pixel location
948     * @param y         the Y coordinate of the pixel location
949     * @param obj       a primitive array containing pixel data
950     * @param data      the DataBuffer containing the image data
951     * @see #getDataElements(int, int, Object, DataBuffer)
952     */
953    public void setDataElements(int x, int y, Object obj, DataBuffer data) {
954        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
955            throw new ArrayIndexOutOfBoundsException
956                ("Coordinate out of bounds!");
957        }
958
959        int type = getTransferType();
960        int numDataElems = getNumDataElements();
961        int pixelOffset = y*scanlineStride + x*pixelStride;
962
963        switch(type) {
964
965        case DataBuffer.TYPE_BYTE:
966
967            byte[] barray = (byte[])obj;
968
969            for (int i=0; i<numDataElems; i++) {
970                data.setElem(bankIndices[i], pixelOffset + bandOffsets[i],
971                           ((int)barray[i])&0xff);
972            }
973            break;
974
975        case DataBuffer.TYPE_USHORT:
976        case DataBuffer.TYPE_SHORT:
977
978            short[] sarray = (short[])obj;
979
980            for (int i=0; i<numDataElems; i++) {
981                data.setElem(bankIndices[i], pixelOffset + bandOffsets[i],
982                           ((int)sarray[i])&0xffff);
983            }
984            break;
985
986        case DataBuffer.TYPE_INT:
987
988            int[] iarray = (int[])obj;
989
990            for (int i=0; i<numDataElems; i++) {
991                data.setElem(bankIndices[i],
992                             pixelOffset + bandOffsets[i], iarray[i]);
993            }
994            break;
995
996        case DataBuffer.TYPE_FLOAT:
997
998            float[] farray = (float[])obj;
999
1000            for (int i=0; i<numDataElems; i++) {
1001                data.setElemFloat(bankIndices[i],
1002                             pixelOffset + bandOffsets[i], farray[i]);
1003            }
1004            break;
1005
1006        case DataBuffer.TYPE_DOUBLE:
1007
1008            double[] darray = (double[])obj;
1009
1010            for (int i=0; i<numDataElems; i++) {
1011                data.setElemDouble(bankIndices[i],
1012                             pixelOffset + bandOffsets[i], darray[i]);
1013            }
1014            break;
1015
1016        }
1017    }
1018
1019    /**
1020     * Sets a pixel in the {@code DataBuffer} using an int array of
1021     * samples for input.  An {@code ArrayIndexOutOfBoundsException}
1022     * might be thrown if the coordinates are
1023     * not in bounds.
1024     * @param x         The X coordinate of the pixel location
1025     * @param y         The Y coordinate of the pixel location
1026     * @param iArray    The input samples in an int array
1027     * @param data      The DataBuffer containing the image data
1028     * @see #getPixel(int, int, int[], DataBuffer)
1029     */
1030    public void setPixel(int x, int y, int iArray[], DataBuffer data) {
1031        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
1032            throw new ArrayIndexOutOfBoundsException
1033                ("Coordinate out of bounds!");
1034        }
1035       int pixelOffset = y*scanlineStride + x*pixelStride;
1036       for (int i=0; i<numBands; i++) {
1037           data.setElem(bankIndices[i],
1038                        pixelOffset + bandOffsets[i],iArray[i]);
1039       }
1040    }
1041
1042    /**
1043     * Sets all samples for a rectangle of pixels from an int array containing
1044     * one sample per array element.
1045     * An {@code ArrayIndexOutOfBoundsException} might be thrown if the
1046     * coordinates are not in bounds.
1047     * @param x         The X coordinate of the upper left pixel location
1048     * @param y         The Y coordinate of the upper left pixel location
1049     * @param w         The width of the pixel rectangle
1050     * @param h         The height of the pixel rectangle
1051     * @param iArray    The input samples in an int array
1052     * @param data      The DataBuffer containing the image data
1053     * @see #getPixels(int, int, int, int, int[], DataBuffer)
1054     */
1055    public void setPixels(int x, int y, int w, int h,
1056                          int iArray[], DataBuffer data) {
1057        int x1 = x + w;
1058        int y1 = y + h;
1059
1060        if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width ||
1061            y < 0 || y >= height || h > height || y1 < 0 || y1 >  height)
1062        {
1063            throw new ArrayIndexOutOfBoundsException
1064                ("Coordinate out of bounds!");
1065        }
1066
1067        int lineOffset = y*scanlineStride + x*pixelStride;
1068        int srcOffset = 0;
1069
1070        for (int i = 0; i < h; i++) {
1071           int pixelOffset = lineOffset;
1072           for (int j = 0; j < w; j++) {
1073              for (int k=0; k < numBands; k++) {
1074                 data.setElem(bankIndices[k], pixelOffset + bandOffsets[k],
1075                              iArray[srcOffset++]);
1076              }
1077              pixelOffset += pixelStride;
1078           }
1079           lineOffset += scanlineStride;
1080        }
1081    }
1082
1083    /**
1084     * Sets a sample in the specified band for the pixel located at (x,y)
1085     * in the {@code DataBuffer} using an int for input.
1086     * An {@code ArrayIndexOutOfBoundsException} might be thrown if the
1087     * coordinates are not in bounds.
1088     * @param x         The X coordinate of the pixel location
1089     * @param y         The Y coordinate of the pixel location
1090     * @param b         the band to set
1091     * @param s         the input sample as an int
1092     * @param data      the DataBuffer containing the image data
1093     * @see #getSample(int, int, int, DataBuffer)
1094     */
1095    public void setSample(int x, int y, int b, int s,
1096                          DataBuffer data) {
1097        // Bounds check for 'b' will be performed automatically
1098        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
1099            throw new ArrayIndexOutOfBoundsException
1100                ("Coordinate out of bounds!");
1101        }
1102        data.setElem(bankIndices[b],
1103                     y*scanlineStride + x*pixelStride + bandOffsets[b], s);
1104    }
1105
1106    /**
1107     * Sets a sample in the specified band for the pixel located at (x,y)
1108     * in the {@code DataBuffer} using a float for input.
1109     * An {@code ArrayIndexOutOfBoundsException} might be thrown if
1110     * the coordinates are not in bounds.
1111     * @param x         The X coordinate of the pixel location
1112     * @param y         The Y coordinate of the pixel location
1113     * @param b         The band to set
1114     * @param s         The input sample as a float
1115     * @param data      The DataBuffer containing the image data
1116     * @see #getSample(int, int, int, DataBuffer)
1117     */
1118    public void setSample(int x, int y, int b,
1119                          float s ,
1120                          DataBuffer data) {
1121        // Bounds check for 'b' will be performed automatically
1122        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
1123            throw new ArrayIndexOutOfBoundsException
1124                ("Coordinate out of bounds!");
1125        }
1126        data.setElemFloat(bankIndices[b],
1127                          y*scanlineStride + x*pixelStride + bandOffsets[b],
1128                          s);
1129    }
1130
1131    /**
1132     * Sets a sample in the specified band for the pixel located at (x,y)
1133     * in the {@code DataBuffer} using a double for input.
1134     * An {@code ArrayIndexOutOfBoundsException} might be thrown if
1135     * the coordinates are not in bounds.
1136     * @param x         The X coordinate of the pixel location
1137     * @param y         The Y coordinate of the pixel location
1138     * @param b         The band to set
1139     * @param s         The input sample as a double
1140     * @param data      The DataBuffer containing the image data
1141     * @see #getSample(int, int, int, DataBuffer)
1142     */
1143    public void setSample(int x, int y, int b,
1144                          double s,
1145                          DataBuffer data) {
1146        // Bounds check for 'b' will be performed automatically
1147        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
1148            throw new ArrayIndexOutOfBoundsException
1149                ("Coordinate out of bounds!");
1150        }
1151        data.setElemDouble(bankIndices[b],
1152                          y*scanlineStride + x*pixelStride + bandOffsets[b],
1153                          s);
1154    }
1155
1156    /**
1157     * Sets the samples in the specified band for the specified rectangle
1158     * of pixels from an int array containing one sample per data array element.
1159     * An {@code ArrayIndexOutOfBoundsException} might be thrown if the
1160     * coordinates are not in bounds.
1161     * @param x         The X coordinate of the upper left pixel location
1162     * @param y         The Y coordinate of the upper left pixel location
1163     * @param w         The width of the pixel rectangle
1164     * @param h         The height of the pixel rectangle
1165     * @param b         The band to set
1166     * @param iArray    The input samples in an int array
1167     * @param data      The DataBuffer containing the image data
1168     * @see #getSamples(int, int, int, int, int, int[], DataBuffer)
1169     */
1170    public void setSamples(int x, int y, int w, int h, int b,
1171                           int iArray[], DataBuffer data) {
1172        // Bounds check for 'b' will be performed automatically
1173        if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) {
1174            throw new ArrayIndexOutOfBoundsException
1175                ("Coordinate out of bounds!");
1176        }
1177        int lineOffset = y*scanlineStride + x*pixelStride + bandOffsets[b];
1178        int srcOffset = 0;
1179
1180        for (int i = 0; i < h; i++) {
1181           int sampleOffset = lineOffset;
1182           for (int j = 0; j < w; j++) {
1183              data.setElem(bankIndices[b], sampleOffset, iArray[srcOffset++]);
1184              sampleOffset += pixelStride;
1185           }
1186           lineOffset += scanlineStride;
1187        }
1188    }
1189
1190    public boolean equals(Object o) {
1191        if ((o == null) || !(o instanceof ComponentSampleModel)) {
1192            return false;
1193        }
1194
1195        ComponentSampleModel that = (ComponentSampleModel)o;
1196        return this.width == that.width &&
1197            this.height == that.height &&
1198            this.numBands == that.numBands &&
1199            this.dataType == that.dataType &&
1200            Arrays.equals(this.bandOffsets, that.bandOffsets) &&
1201            Arrays.equals(this.bankIndices, that.bankIndices) &&
1202            this.numBands == that.numBands &&
1203            this.numBanks == that.numBanks &&
1204            this.scanlineStride == that.scanlineStride &&
1205            this.pixelStride == that.pixelStride;
1206    }
1207
1208    // If we implement equals() we must also implement hashCode
1209    public int hashCode() {
1210        int hash = 0;
1211        hash = width;
1212        hash <<= 8;
1213        hash ^= height;
1214        hash <<= 8;
1215        hash ^= numBands;
1216        hash <<= 8;
1217        hash ^= dataType;
1218        hash <<= 8;
1219        for (int i = 0; i < bandOffsets.length; i++) {
1220            hash ^= bandOffsets[i];
1221            hash <<= 8;
1222        }
1223        for (int i = 0; i < bankIndices.length; i++) {
1224            hash ^= bankIndices[i];
1225            hash <<= 8;
1226        }
1227        hash ^= numBands;
1228        hash <<= 8;
1229        hash ^= numBanks;
1230        hash <<= 8;
1231        hash ^= scanlineStride;
1232        hash <<= 8;
1233        hash ^= pixelStride;
1234        return hash;
1235    }
1236}
1237