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