1 /*
2  * Copyright (c) 1997, 2018, 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 
36 package java.awt.image;
37 
38 import 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 
76 public 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      */
ComponentSampleModel(int dataType, int w, int h, int pixelStride, int scanlineStride, int[] bandOffsets)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      */
ComponentSampleModel(int dataType, int w, int h, int pixelStride, int scanlineStride, int[] bankIndices, int[] bandOffsets)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 
verify()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      */
getBufferSize()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       */
orderBands(int[] orig, int step)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      */
createCompatibleSampleModel(int w, int h)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      */
createSubsetSampleModel(int[] bands)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      */
createDataBuffer()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      */
getOffset(int x, int y)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      */
getOffset(int x, int y, int b)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      */
getSampleSize()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      */
getSampleSize(int band)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      */
getBankIndices()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      */
getBandOffsets()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      */
getScanlineStride()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      */
getPixelStride()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      */
getNumDataElements()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      */
getDataElements(int x, int y, Object obj, DataBuffer data)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      */
getPixel(int x, int y, int[] iArray, DataBuffer data)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      */
getPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data)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      */
getSample(int x, int y, int b, DataBuffer data)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      */
getSampleFloat(int x, int y, int b, DataBuffer data)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      */
getSampleDouble(int x, int y, int b, DataBuffer data)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      */
getSamples(int x, int y, int w, int h, int b, int[] iArray, DataBuffer data)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      */
setDataElements(int x, int y, Object obj, DataBuffer data)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      */
setPixel(int x, int y, int[] iArray, DataBuffer data)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      */
setPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data)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      */
setSample(int x, int y, int b, int s, DataBuffer data)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      */
setSample(int x, int y, int b, float s , DataBuffer data)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      */
setSample(int x, int y, int b, double s, DataBuffer data)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      */
setSamples(int x, int y, int w, int h, int b, int[] iArray, DataBuffer data)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 
equals(Object o)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
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