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