001/* Copyright (C) 2000, 2002, 2006,  Free Software Foundation
002
003This file is part of GNU Classpath.
004
005GNU Classpath is free software; you can redistribute it and/or modify
006it under the terms of the GNU General Public License as published by
007the Free Software Foundation; either version 2, or (at your option)
008any later version.
009
010GNU Classpath is distributed in the hope that it will be useful, but
011WITHOUT ANY WARRANTY; without even the implied warranty of
012MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013General Public License for more details.
014
015You should have received a copy of the GNU General Public License
016along with GNU Classpath; see the file COPYING.  If not, write to the
017Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01802110-1301 USA.
019
020Linking this library statically or dynamically with other modules is
021making a combined work based on this library.  Thus, the terms and
022conditions of the GNU General Public License cover the whole
023combination.
024
025As a special exception, the copyright holders of this library give you
026permission to link this library with independent modules to produce an
027executable, regardless of the license terms of these independent
028modules, and to copy and distribute the resulting executable under
029terms of your choice, provided that you also meet, for each linked
030independent module, the terms and conditions of the license of that
031module.  An independent module is a module which is not derived from
032or based on this library.  If you modify this library, you may extend
033this exception to your version of the library, but you are not
034obligated to do so.  If you do not wish to do so, delete this
035exception statement from your version. */
036
037package java.awt.image;
038
039import 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 */
059public 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}