001    /* Copyright (C) 2000, 2002, 2006,  Free Software Foundation
002    
003    This file is part of GNU Classpath.
004    
005    GNU Classpath is free software; you can redistribute it and/or modify
006    it under the terms of the GNU General Public License as published by
007    the Free Software Foundation; either version 2, or (at your option)
008    any later version.
009    
010    GNU Classpath is distributed in the hope that it will be useful, but
011    WITHOUT ANY WARRANTY; without even the implied warranty of
012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013    General Public License for more details.
014    
015    You should have received a copy of the GNU General Public License
016    along with GNU Classpath; see the file COPYING.  If not, write to the
017    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
018    02110-1301 USA.
019    
020    Linking this library statically or dynamically with other modules is
021    making a combined work based on this library.  Thus, the terms and
022    conditions of the GNU General Public License cover the whole
023    combination.
024    
025    As a special exception, the copyright holders of this library give you
026    permission to link this library with independent modules to produce an
027    executable, regardless of the license terms of these independent
028    modules, and to copy and distribute the resulting executable under
029    terms of your choice, provided that you also meet, for each linked
030    independent module, the terms and conditions of the license of that
031    module.  An independent module is a module which is not derived from
032    or based on this library.  If you modify this library, you may extend
033    this exception to your version of the library, but you are not
034    obligated to do so.  If you do not wish to do so, delete this
035    exception statement from your version. */
036    
037    package java.awt.image;
038    
039    import java.util.Arrays;
040    
041    /**
042     * ComponentSampleModel supports a flexible organization of pixel samples in
043     * memory, permitting pixel samples to be interleaved by band, by scanline,
044     * and by pixel.
045     *
046     * A DataBuffer for this sample model has K banks of data.  Pixels have N
047     * samples, so there are N bands in the DataBuffer.  Each band is completely
048     * contained in one bank of data, but a bank may contain more than one band.
049     * Each pixel sample is stored in a single data element.
050     *
051     * Within a bank, each band begins at an offset stored in bandOffsets.  The
052     * banks containing the band is given by bankIndices.  Within the bank, there
053     * are three dimensions - band, pixel, and scanline.  The dimension ordering
054     * is controlled by bandOffset, pixelStride, and scanlineStride, which means
055     * that any combination of interleavings is supported.
056     *
057     * @author Rolf W. Rasmussen (rolfwr@ii.uib.no)
058     */
059    public class ComponentSampleModel extends SampleModel
060    {
061      /** The offsets to the first sample for each band. */
062      protected int[] bandOffsets;
063    
064      /** The indices of the bank used to store each band in a data buffer. */
065      protected int[] bankIndices;
066    
067      /**
068       * The number of bands in the image.
069       * @specnote This field shadows the protected numBands in SampleModel.
070       */
071      protected int numBands;
072    
073      /** Used when creating data buffers. */
074      protected int numBanks;
075    
076      /**
077       * The number of data elements between a sample in one row and the
078       * corresponding sample in the next row.
079       */
080      protected int scanlineStride;
081    
082      /**
083       * The number of data elements between a sample for one pixel and the
084       * corresponding sample for the next pixel in the same row.
085       */
086      protected int pixelStride;
087    
088      /**
089       * Creates a new sample model that assumes that all bands are stored in a
090       * single bank of the {@link DataBuffer}.
091       * <p>
092       * Note that the <code>bandOffsets</code> array is copied to internal storage
093       * to prevent subsequent changes to the array from affecting this object.
094       *
095       * @param dataType  the data type (one of {@link DataBuffer#TYPE_BYTE},
096       *   {@link DataBuffer#TYPE_USHORT}, {@link DataBuffer#TYPE_SHORT},
097       *   {@link DataBuffer#TYPE_INT}, {@link DataBuffer#TYPE_FLOAT} or
098       *   {@link DataBuffer#TYPE_DOUBLE}).
099       * @param w  the width in pixels.
100       * @param h  the height in pixels.
101       * @param pixelStride  the number of data elements in the step from a sample
102       *   in one pixel to the corresponding sample in the next pixel.
103       * @param scanlineStride  the number of data elements in the step from a
104       *   sample in a pixel to the corresponding sample in the pixel in the next
105       *   row.
106       * @param bandOffsets  the offset to the first element for each band, with
107       *   the size of the array defining the number of bands (<code>null</code>
108       *   not permitted).
109       *
110       * @throws IllegalArgumentException if <code>dataType</code> is not one of
111       *   the specified values.
112       * @throws IllegalArgumentException if <code>w</code> is less than or equal
113       *   to zero.
114       * @throws IllegalArgumentException if <code>h</code> is less than or equal
115       *   to zero.
116       * @throws IllegalArgumentException if <code>w * h</code> exceeds
117       *   {@link Integer#MAX_VALUE}.
118       * @throws IllegalArgumentException if <code>pixelStride</code> is negative.
119       * @throws IllegalArgumentException if <code>scanlineStride</code> is less
120       *   than or equal to zero.
121       * @throws IllegalArgumentException if <code>bandOffsets</code> has zero
122       *   length.
123       */
124      public ComponentSampleModel(int dataType,
125                                  int w, int h,
126                                  int pixelStride,
127                                  int scanlineStride,
128                                  int[] bandOffsets)
129      {
130        this(dataType, w, h, pixelStride, scanlineStride,
131             new int[bandOffsets.length], bandOffsets);
132      }
133    
134      /**
135       * Creates a new sample model that assumes that all bands are stored in a
136       * single bank of the {@link DataBuffer}.
137       *
138       * @param dataType  the data type (one of {@link DataBuffer#TYPE_BYTE},
139       *   {@link DataBuffer#TYPE_USHORT}, {@link DataBuffer#TYPE_SHORT},
140       *   {@link DataBuffer#TYPE_INT}, {@link DataBuffer#TYPE_FLOAT} or
141       *   {@link DataBuffer#TYPE_DOUBLE}).
142       * @param w  the width in pixels.
143       * @param h  the height in pixels.
144       * @param pixelStride  the number of data elements in the step from a sample
145       *   in one pixel to the corresponding sample in the next pixel.
146       * @param scanlineStride  the number of data elements in the step from a
147       *   sample in a pixel to the corresponding sample in the pixel in the next
148       *   row.
149       * @param bankIndices  the index of the bank in which each band is stored
150       *   (<code>null</code> not permitted).  This array is copied to internal
151       *   storage so that subsequent updates to the array do not affect the sample
152       *   model.
153       * @param bandOffsets  the offset to the first element for each band, with
154       *   the size of the array defining the number of bands (<code>null</code>
155       *   not permitted).  This array is copied to internal storage so that
156       *   subsequent updates to the array do not affect the sample model.
157       *
158       * @throws IllegalArgumentException if <code>dataType</code> is not one of
159       *   the specified values.
160       * @throws IllegalArgumentException if <code>w</code> is less than or equal
161       *   to zero.
162       * @throws IllegalArgumentException if <code>h</code> is less than or equal
163       *   to zero.
164       * @throws IllegalArgumentException if <code>w * h</code> exceeds
165       *   {@link Integer#MAX_VALUE}.
166       * @throws IllegalArgumentException if <code>pixelStride</code> is negative.
167       * @throws IllegalArgumentException if <code>scanlineStride</code> is less
168       *   than or equal to zero.
169       * @throws IllegalArgumentException if <code>bandOffsets</code> has zero
170       *   length.
171       */
172      public ComponentSampleModel(int dataType,
173                                  int w, int h,
174                                  int pixelStride,
175                                  int scanlineStride,
176                                  int[] bankIndices,
177                                  int[] bandOffsets)
178      {
179        super(dataType, w, h, bandOffsets.length);
180    
181        // super permits DataBuffer.TYPE_UNDEFINED but this class doesn't...
182        if (dataType == DataBuffer.TYPE_UNDEFINED)
183          throw new IllegalArgumentException("Unsupported dataType.");
184    
185        if ((pixelStride < 0) || (scanlineStride < 0) || (bandOffsets.length < 1)
186            || (bandOffsets.length != bankIndices.length))
187          throw new IllegalArgumentException();
188    
189        this.bandOffsets = (int[]) bandOffsets.clone();
190        this.bankIndices = (int[]) bankIndices.clone();
191        this.numBands = bandOffsets.length;
192    
193        this.numBanks = 0;
194        for (int b = 0; b < bankIndices.length; b++)
195          this.numBanks = Math.max(this.numBanks, bankIndices[b] + 1);
196    
197        this.scanlineStride = scanlineStride;
198        this.pixelStride = pixelStride;
199    
200      }
201    
202      /**
203       * Creates a new sample model that is compatible with this one, but with the
204       * specified dimensions.
205       *
206       * @param w  the width (must be greater than zero).
207       * @param h  the height (must be greater than zero).
208       *
209       * @return A new sample model.
210       */
211      public SampleModel createCompatibleSampleModel(int w, int h)
212      {
213        return new ComponentSampleModel(dataType, w, h, pixelStride,
214                                        scanlineStride, bankIndices,
215                                        bandOffsets);
216      }
217    
218      /**
219       * Creates a new sample model that provides access to a subset of the bands
220       * that this sample model supports.
221       *
222       * @param bands  the bands (<code>null</code> not permitted).
223       *
224       * @return The new sample model.
225       */
226      public SampleModel createSubsetSampleModel(int[] bands)
227      {
228        int numBands = bands.length;
229    
230        int[] bankIndices = new int[numBands];
231        int[] bandOffsets = new int[numBands];
232        for (int b = 0; b < numBands; b++)
233          {
234            bankIndices[b] = this.bankIndices[bands[b]];
235            bandOffsets[b] = this.bandOffsets[bands[b]];
236          }
237    
238        return new ComponentSampleModel(dataType, width, height, pixelStride,
239                                        scanlineStride, bankIndices,
240                                        bandOffsets);
241      }
242    
243      /**
244       * Creates a new data buffer that is compatible with this sample model.
245       *
246       * @return The new data buffer.
247       */
248      public DataBuffer createDataBuffer()
249      {
250        // Maybe this value should be precalculated in the constructor?
251        int highestOffset = 0;
252        for (int b = 0; b < numBands; b++)
253          highestOffset = Math.max(highestOffset, bandOffsets[b]);
254        int size = pixelStride * (width - 1) + scanlineStride * (height - 1)
255            + highestOffset + 1;
256    
257        DataBuffer buffer = null;
258        switch (getTransferType())
259          {
260          case DataBuffer.TYPE_BYTE:
261            buffer = new DataBufferByte(size, numBanks);
262            break;
263          case DataBuffer.TYPE_SHORT:
264            buffer = new DataBufferShort(size, numBanks);
265            break;
266          case DataBuffer.TYPE_USHORT:
267            buffer = new DataBufferUShort(size, numBanks);
268            break;
269          case DataBuffer.TYPE_INT:
270            buffer = new DataBufferInt(size, numBanks);
271            break;
272          case DataBuffer.TYPE_FLOAT:
273            buffer = new DataBufferFloat(size, numBanks);
274            break;
275          case DataBuffer.TYPE_DOUBLE:
276            buffer = new DataBufferDouble(size, numBanks);
277            break;
278          }
279        return buffer;
280      }
281    
282      /**
283       * Returns the offset of the sample in band 0 for the pixel at location
284       * <code>(x, y)</code>.  This offset can be used to read a sample value from
285       * a {@link DataBuffer}.
286       *
287       * @param x  the x-coordinate.
288       * @param y  the y-coordinate.
289       *
290       * @return The offset.
291       *
292       * @see #getOffset(int, int, int)
293       */
294      public int getOffset(int x, int y)
295      {
296        return getOffset(x, y, 0);
297      }
298    
299      /**
300       * Returns the offset of the sample in band <code>b</code> for the pixel at
301       * location <code>(x, y)</code>.  This offset can be used to read a sample
302       * value from a {@link DataBuffer}.
303       *
304       * @param x  the x-coordinate.
305       * @param y  the y-coordinate.
306       * @param b  the band index.
307       *
308       * @return The offset.
309       */
310      public int getOffset(int x, int y, int b)
311      {
312        return bandOffsets[b] + pixelStride * x + scanlineStride * y;
313      }
314    
315      /**
316       * Returns the size in bits for each sample (one per band).  For this sample
317       * model, each band has the same sample size and this is determined by the
318       * data type for the sample model.
319       *
320       * @return The sample sizes.
321       *
322       * @see SampleModel#getDataType()
323       */
324      public final int[] getSampleSize()
325      {
326        int size = DataBuffer.getDataTypeSize(getDataType());
327        int[] sizes = new int[numBands];
328    
329        java.util.Arrays.fill(sizes, size);
330        return sizes;
331      }
332    
333      /**
334       * Returns the size in bits for the samples in the specified band.  In this
335       * class, the sample size is the same for every band and is determined from
336       * the data type for the model.
337       *
338       * @param band  the band index (ignored here).
339       *
340       * @return The sample size in bits.
341       *
342       * @see SampleModel#getDataType()
343       */
344      public final int getSampleSize(int band)
345      {
346        return DataBuffer.getDataTypeSize(getDataType());
347      }
348    
349      /**
350       * Returns the indices of the bank(s) in the {@link DataBuffer} used to
351       * store the samples for each band.  The returned array is a copy, so that
352       * altering it will not impact the sample model.
353       *
354       * @return The bank indices.
355       */
356      public final int[] getBankIndices()
357      {
358        return (int[]) bankIndices.clone();
359      }
360    
361      /**
362       * Returns the offsets to the first sample in each band.  The returned array
363       * is a copy, so that altering it will not impact the sample model.
364       *
365       * @return The offsets.
366       */
367      public final int[] getBandOffsets()
368      {
369        return (int[]) bandOffsets.clone();
370      }
371    
372      /**
373       * Returns the distance (in terms of element indices) between the sample for
374       * one pixel and the corresponding sample for the equivalent pixel in the
375       * next row.  This is used in the calculation of the element offset for
376       * retrieving samples from a {@link DataBuffer}.
377       *
378       * @return The distance between pixel samples in consecutive rows.
379       */
380      public final int getScanlineStride()
381      {
382        return scanlineStride;
383      }
384    
385      /**
386       * Returns the distance (in terms of element indices) between the sample for
387       * one pixel and the corresponding sample for the next pixel in a row.  This
388       * is used in the calculation of the element offset for retrieving samples
389       * from a {@link DataBuffer}.
390       *
391       * @return The distance between pixel samples in the same row.
392       */
393      public final int getPixelStride()
394      {
395        return pixelStride;
396      }
397    
398      /**
399       * Returns the number of data elements used to store the samples for one
400       * pixel.  In this model, this is the same as the number of bands.
401       *
402       * @return The number of data elements used to store the samples for one
403       *   pixel.
404       */
405      public final int getNumDataElements()
406      {
407        return numBands;
408      }
409    
410      /**
411       * Returns the samples for the pixel at location <code>(x, y)</code> in
412       * a primitive array (the array type is determined by the data type for
413       * this model).  The <code>obj</code> argument provides an option to supply
414       * an existing array to hold the result, if this is <code>null</code> a new
415       * array will be allocated.
416       *
417       * @param x  the x-coordinate.
418       * @param y  the y-coordinate.
419       * @param obj  a primitive array that, if not <code>null</code>, will be
420       *   used to store and return the sample values.
421       * @param data  the data buffer (<code>null</code> not permitted).
422       *
423       * @return An array of sample values for the specified pixel.
424       */
425      public Object getDataElements(int x, int y, Object obj, DataBuffer data)
426      {
427        int type = getTransferType();
428        int numDataEls = getNumDataElements();
429        int offset = y * scanlineStride + x * pixelStride;
430        switch (type)
431          {
432          case DataBuffer.TYPE_BYTE:
433            byte[] bData;
434            if (obj == null)
435              bData = new byte[numDataEls];
436            else
437              bData = (byte[]) obj;
438            for (int i = 0; i < numDataEls; i++)
439              {
440                bData[i] = (byte) data.getElem(bankIndices[i],
441                                               offset + bandOffsets[i]);
442              }
443            obj = bData;
444            break;
445          case DataBuffer.TYPE_SHORT:
446          case DataBuffer.TYPE_USHORT:
447            short[] sData;
448            if (obj == null)
449              sData = new short[numDataEls];
450            else
451              sData = (short[]) obj;
452            for (int i = 0; i < numDataEls; i++)
453              {
454                sData[i] = (short) data.getElem(bankIndices[i],
455                                                offset + bandOffsets[i]);
456              }
457            obj = sData;
458            break;
459          case DataBuffer.TYPE_INT:
460            int[] iData;
461            if (obj == null)
462              iData = new int[numDataEls];
463            else
464              iData = (int[]) obj;
465            for (int i = 0; i < numDataEls; i++)
466              {
467                iData[i] = data.getElem(bankIndices[i], offset + bandOffsets[i]);
468              }
469            obj = iData;
470            break;
471          case DataBuffer.TYPE_FLOAT:
472            float[] fData;
473            if (obj == null)
474              fData = new float[numDataEls];
475            else
476              fData = (float[]) obj;
477            for (int i = 0; i < numDataEls; i++)
478              {
479                fData[i] = data.getElemFloat(bankIndices[i],
480                                             offset + bandOffsets[i]);
481              }
482            obj = fData;
483            break;
484          case DataBuffer.TYPE_DOUBLE:
485            double[] dData;
486            if (obj == null)
487              dData = new double[numDataEls];
488            else
489              dData = (double[]) obj;
490            for (int i = 0; i < numDataEls; i++)
491              {
492                dData[i] = data.getElemDouble(bankIndices[i],
493                                              offset + bandOffsets[i]);
494              }
495            obj = dData;
496            break;
497          }
498        return obj;
499      }
500    
501    
502      /**
503       * Returns all the samples for the pixel at location <code>(x, y)</code>
504       * stored in the specified data buffer.
505       *
506       * @param x  the x-coordinate.
507       * @param y  the y-coordinate.
508       * @param iArray  an array that will be populated with the sample values and
509       *   returned as the result.  The size of this array should be equal to the
510       *   number of bands in the model.  If the array is <code>null</code>, a new
511       *   array is created.
512       * @param data  the data buffer (<code>null</code> not permitted).
513       *
514       * @return The samples for the specified pixel.
515       *
516       * @see #setPixel(int, int, int[], DataBuffer)
517       */
518      public int[] getPixel(int x, int y, int[] iArray, DataBuffer data)
519      {
520        if (x < 0 || x >= width || y < 0 || y >= height)
521          throw new ArrayIndexOutOfBoundsException("Pixel (" + x + ", " + y
522                                                   + ") is out of bounds.");
523        int offset = pixelStride * x + scanlineStride * y;
524        if (iArray == null)
525          iArray = new int[numBands];
526        for (int b = 0; b < numBands; b++)
527          {
528            iArray[b] = data.getElem(bankIndices[b], offset + bandOffsets[b]);
529          }
530        return iArray;
531      }
532    
533      /**
534       * Returns the samples for all the pixels in a rectangular region.
535       *
536       * @param x  the x-coordinate.
537       * @param y  the y-coordinate.
538       * @param w  the width.
539       * @param h  the height.
540       * @param iArray  an array that if non-<code>null</code> will be populated
541       *   with the sample values and returned as the result.
542       * @param data  the data buffer (<code>null</code> not permitted).
543       *
544       * @return The samples for all the pixels in the rectangle.
545       */
546      public int[] getPixels(int x, int y, int w, int h, int[] iArray,
547                             DataBuffer data)
548      {
549        int offset = pixelStride * x + scanlineStride * y;
550        if (iArray == null)
551          iArray = new int[numBands * w * h];
552        int outOffset = 0;
553        for (y = 0; y < h; y++)
554          {
555            int lineOffset = offset;
556            for (x = 0; x < w; x++)
557              {
558                for (int b = 0; b < numBands; b++)
559                  {
560                    iArray[outOffset++]
561                        = data.getElem(bankIndices[b], lineOffset+bandOffsets[b]);
562                  }
563                lineOffset += pixelStride;
564              }
565            offset += scanlineStride;
566          }
567        return iArray;
568      }
569    
570      /**
571       * Returns the sample for band <code>b</code> of the pixel at
572       * <code>(x, y)</code> that is stored in the specified data buffer.
573       *
574       * @param x  the x-coordinate.
575       * @param y  the y-coordinate.
576       * @param b  the band index.
577       * @param data  the data buffer (<code>null</code> not permitted).
578       *
579       * @return The sample value.
580       *
581       * @throws ArrayIndexOutOfBoundsException if <code>(x, y)</code> is outside
582       *     the bounds <code>[0, 0, width, height]</code>.
583       *
584       * @see #setSample(int, int, int, int, DataBuffer)
585       */
586      public int getSample(int x, int y, int b, DataBuffer data)
587      {
588        if (x < 0 || x >= width || y < 0 || y >= height)
589          throw new ArrayIndexOutOfBoundsException("Sample (" + x + ", " + y
590                                                   + ") is out of bounds.");
591        return data.getElem(bankIndices[b], getOffset(x, y, b));
592      }
593    
594      /**
595       * Sets the samples for the pixel at location <code>(x, y)</code> from the
596       * supplied primitive array (the array type must be consistent with the data
597       * type for this model).
598       *
599       * @param x  the x-coordinate.
600       * @param y  the y-coordinate.
601       * @param obj  a primitive array containing the pixel's sample values.
602       * @param data  the data buffer (<code>null</code> not permitted).
603       *
604       * @see #setDataElements(int, int, Object, DataBuffer)
605       */
606      public void setDataElements(int x, int y, Object obj, DataBuffer data)
607      {
608        int type = getTransferType();
609        int numDataEls = getNumDataElements();
610        int offset = y * scanlineStride + x * pixelStride;
611        switch (type)
612          {
613          case DataBuffer.TYPE_BYTE:
614            byte[] bData = (byte[]) obj;
615            for (int i = 0; i < numDataEls; i++)
616              {
617                data.setElem(bankIndices[i], offset + bandOffsets[i],
618                             ((int) bData[i]) & 0xFF);
619              }
620            break;
621          case DataBuffer.TYPE_SHORT:
622          case DataBuffer.TYPE_USHORT:
623            short[] sData = (short[]) obj;
624            for (int i = 0; i < numDataEls; i++)
625              {
626                data.setElem(bankIndices[i], offset + bandOffsets[i],
627                             ((int) sData[i]) & 0xFFFF);
628              }
629            break;
630          case DataBuffer.TYPE_INT:
631            int[] iData = (int[]) obj;
632            for (int i = 0; i < numDataEls; i++)
633              {
634                data.setElem(bankIndices[i], offset + bandOffsets[i], iData[i]);
635              }
636            break;
637          case DataBuffer.TYPE_FLOAT:
638            float[] fData = (float[]) obj;
639            for (int i = 0; i < numDataEls; i++)
640              {
641                data.setElemFloat(bankIndices[i], offset + bandOffsets[i],
642                                  fData[i]);
643              }
644            break;
645          case DataBuffer.TYPE_DOUBLE:
646            double[] dData = (double[]) obj;
647            for (int i = 0; i < numDataEls; i++)
648              {
649                data.setElemDouble(bankIndices[i], offset + bandOffsets[i],
650                                   dData[i]);
651              }
652            break;
653          }
654      }
655    
656      /**
657       * Sets the sample values for the pixel at location <code>(x, y)</code>
658       * stored in the specified data buffer.
659       *
660       * @param x  the x-coordinate.
661       * @param y  the y-coordinate.
662       * @param iArray  the pixel sample values (<code>null</code> not permitted).
663       * @param data  the data buffer (<code>null</code> not permitted).
664       *
665       * @see #getPixel(int, int, int[], DataBuffer)
666       */
667      public void setPixel(int x, int y, int[] iArray, DataBuffer data)
668      {
669        int offset = pixelStride * x + scanlineStride * y;
670        for (int b = 0; b < numBands; b++)
671          data.setElem(bankIndices[b], offset + bandOffsets[b], iArray[b]);
672      }
673    
674      /**
675       * Sets the sample value for band <code>b</code> of the pixel at location
676       * <code>(x, y)</code> in the specified data buffer.
677       *
678       * @param x  the x-coordinate.
679       * @param y  the y-coordinate.
680       * @param b  the band index.
681       * @param s  the sample value.
682       * @param data  the data buffer (<code>null</code> not permitted).
683       *
684       * @see #getSample(int, int, int, DataBuffer)
685       */
686      public void setSample(int x, int y, int b, int s, DataBuffer data)
687      {
688        data.setElem(bankIndices[b], getOffset(x, y, b), s);
689      }
690    
691      /**
692       * Tests this sample model for equality with an arbitrary object.  Returns
693       * <code>true</code> if and only if:
694       * <ul>
695       * <li><code>obj</code> is not <code>null</code>;</li>
696       * <li><code>obj</code> is an instance of <code>ComponentSampleModel</code>;
697       *   </li>
698       * <li>both models have the same values for the <code>dataType</code>,
699       *   <code>width</code>, <code>height</code>, <code>pixelStride</code>,
700       *   <code>scanlineStride</code>, <code>bandOffsets</code> and
701       *   <code>bankIndices</code> fields.</li>
702       * </ul>
703       *
704       * @param obj  the object to test (<code>null</code> permitted).
705       *
706       * @return <code>true</code> if this sample model is equal to
707       *   <code>obj</code>, and <code>false</code> otherwise.
708       */
709      public boolean equals(Object obj)
710      {
711        if (obj == null)
712          return false;
713        if (! (obj instanceof ComponentSampleModel))
714          return false;
715        ComponentSampleModel that = (ComponentSampleModel) obj;
716        if (this.dataType != that.dataType)
717          return false;
718        if (this.width != that.width)
719          return false;
720        if (this.height != that.height)
721          return false;
722        if (this.pixelStride != that.pixelStride)
723          return false;
724        if (this.scanlineStride != that.scanlineStride)
725          return false;
726        if (! Arrays.equals(this.bandOffsets, that.bandOffsets))
727          return false;
728        if (! Arrays.equals(this.bankIndices, that.bankIndices))
729          return false;
730        // couldn't find any difference, so...
731        return true;
732      }
733    
734      /**
735       * Returns a hash code for this sample model.
736       *
737       * @return The hash code.
738       */
739      public int hashCode()
740      {
741        // this computation is based on the method described in Chapter 3
742        // of Joshua Bloch's Effective Java...
743        int result = 17;
744        result = 37 * result + dataType;
745        result = 37 * result + width;
746        result = 37 * result + height;
747        result = 37 * result + pixelStride;
748        result = 37 * result + scanlineStride;
749        for (int i = 0; i < bandOffsets.length; i++)
750          result = 37 * result + bandOffsets[i];
751        for (int i = 0; i < bankIndices.length; i++)
752          result = 37 * result + bankIndices[i];
753        return result;
754      }
755    }