1 /*
   2  * Copyright (c) 1997, 2008, 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 /**
  39  * The <code>MultiPixelPackedSampleModel</code> class represents
  40  * one-banded images and can pack multiple one-sample
  41  * pixels into one data element.  Pixels are not allowed to span data elements.
  42  * The data type can be DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT,
  43  * or DataBuffer.TYPE_INT.  Each pixel must be a power of 2 number of bits
  44  * and a power of 2 number of pixels must fit exactly in one data element.
  45  * Pixel bit stride is equal to the number of bits per pixel.  Scanline
  46  * stride is in data elements and the last several data elements might be
  47  * padded with unused pixels.  Data bit offset is the offset in bits from
  48  * the beginning of the {@link DataBuffer} to the first pixel and must be
  49  * a multiple of pixel bit stride.
  50  * <p>
  51  * The following code illustrates extracting the bits for pixel
  52  * <code>x,&nbsp;y</code> from <code>DataBuffer</code> <code>data</code>
  53  * and storing the pixel data in data elements of type
  54  * <code>dataType</code>:
  55  * <pre>
  56  *      int dataElementSize = DataBuffer.getDataTypeSize(dataType);
  57  *      int bitnum = dataBitOffset + x*pixelBitStride;
  58  *      int element = data.getElem(y*scanlineStride + bitnum/dataElementSize);
  59  *      int shift = dataElementSize - (bitnum & (dataElementSize-1))
  60  *                  - pixelBitStride;
  61  *      int pixel = (element >> shift) & ((1 << pixelBitStride) - 1);
  62  * </pre>
  63  */
  64 
  65 public class MultiPixelPackedSampleModel extends SampleModel
  66 {
  67     /** The number of bits from one pixel to the next. */
  68     int pixelBitStride;
  69 
  70     /** Bitmask that extracts the rightmost pixel of a data element. */
  71     int bitMask;
  72 
  73     /**
  74       * The number of pixels that fit in a data element.  Also used
  75       * as the number of bits per pixel.
  76       */
  77     int pixelsPerDataElement;
  78 
  79     /** The size of a data element in bits. */
  80     int dataElementSize;
  81 
  82     /** The bit offset into the data array where the first pixel begins.
  83      */
  84     int dataBitOffset;
  85 
  86     /** ScanlineStride of the data buffer described in data array elements. */
  87     int scanlineStride;
  88 
  89     /**
  90      * Constructs a <code>MultiPixelPackedSampleModel</code> with the
  91      * specified data type, width, height and number of bits per pixel.
  92      * @param dataType  the data type for storing samples
  93      * @param w         the width, in pixels, of the region of
  94      *                  image data described
  95      * @param h         the height, in pixels, of the region of
  96      *                  image data described
  97      * @param numberOfBits the number of bits per pixel
  98      * @throws IllegalArgumentException if <code>dataType</code> is not
  99      *         either <code>DataBuffer.TYPE_BYTE</code>,
 100      *         <code>DataBuffer.TYPE_USHORT</code>, or
 101      *         <code>DataBuffer.TYPE_INT</code>
 102      */
 103     public MultiPixelPackedSampleModel(int dataType,
 104                                        int w,
 105                                        int h,
 106                                        int numberOfBits) {
 107         this(dataType,w,h,
 108              numberOfBits,
 109             (w*numberOfBits+DataBuffer.getDataTypeSize(dataType)-1)/
 110                 DataBuffer.getDataTypeSize(dataType),
 111              0);
 112         if (dataType != DataBuffer.TYPE_BYTE &&
 113             dataType != DataBuffer.TYPE_USHORT &&
 114             dataType != DataBuffer.TYPE_INT) {
 115             throw new IllegalArgumentException("Unsupported data type "+
 116                                                dataType);
 117         }
 118     }
 119 
 120     /**
 121      * Constructs a <code>MultiPixelPackedSampleModel</code> with
 122      * specified data type, width, height, number of bits per pixel,
 123      * scanline stride and data bit offset.
 124      * @param dataType  the data type for storing samples
 125      * @param w         the width, in pixels, of the region of
 126      *                  image data described
 127      * @param h         the height, in pixels, of the region of
 128      *                  image data described
 129      * @param numberOfBits the number of bits per pixel
 130      * @param scanlineStride the line stride of the image data
 131      * @param dataBitOffset the data bit offset for the region of image
 132      *                  data described
 133      * @exception RasterFormatException if the number of bits per pixel
 134      *                  is not a power of 2 or if a power of 2 number of
 135      *                  pixels do not fit in one data element.
 136      * @throws IllegalArgumentException if <code>w</code> or
 137      *         <code>h</code> is not greater than 0
 138      * @throws IllegalArgumentException if <code>dataType</code> is not
 139      *         either <code>DataBuffer.TYPE_BYTE</code>,
 140      *         <code>DataBuffer.TYPE_USHORT</code>, or
 141      *         <code>DataBuffer.TYPE_INT</code>
 142      */
 143     public MultiPixelPackedSampleModel(int dataType, int w, int h,
 144                                        int numberOfBits,
 145                                        int scanlineStride,
 146                                        int dataBitOffset) {
 147         super(dataType, w, h, 1);
 148         if (dataType != DataBuffer.TYPE_BYTE &&
 149             dataType != DataBuffer.TYPE_USHORT &&
 150             dataType != DataBuffer.TYPE_INT) {
 151             throw new IllegalArgumentException("Unsupported data type "+
 152                                                dataType);
 153         }
 154         this.dataType = dataType;
 155         this.pixelBitStride = numberOfBits;
 156         this.scanlineStride = scanlineStride;
 157         this.dataBitOffset = dataBitOffset;
 158         this.dataElementSize = DataBuffer.getDataTypeSize(dataType);
 159         this.pixelsPerDataElement = dataElementSize/numberOfBits;
 160         if (pixelsPerDataElement*numberOfBits != dataElementSize) {
 161            throw new RasterFormatException("MultiPixelPackedSampleModel " +
 162                                              "does not allow pixels to " +
 163                                              "span data element boundaries");
 164         }
 165         this.bitMask = (1 << numberOfBits) - 1;
 166     }
 167 
 168 
 169     /**
 170      * Creates a new <code>MultiPixelPackedSampleModel</code> with the
 171      * specified width and height.  The new
 172      * <code>MultiPixelPackedSampleModel</code> has the
 173      * same storage data type and number of bits per pixel as this
 174      * <code>MultiPixelPackedSampleModel</code>.
 175      * @param w the specified width
 176      * @param h the specified height
 177      * @return a {@link SampleModel} with the specified width and height
 178      * and with the same storage data type and number of bits per pixel
 179      * as this <code>MultiPixelPackedSampleModel</code>.
 180      * @throws IllegalArgumentException if <code>w</code> or
 181      *         <code>h</code> is not greater than 0
 182      */
 183     public SampleModel createCompatibleSampleModel(int w, int h) {
 184       SampleModel sampleModel =
 185             new MultiPixelPackedSampleModel(dataType, w, h, pixelBitStride);
 186       return sampleModel;
 187     }
 188 
 189     /**
 190      * Creates a <code>DataBuffer</code> that corresponds to this
 191      * <code>MultiPixelPackedSampleModel</code>.  The
 192      * <code>DataBuffer</code> object's data type and size
 193      * is consistent with this <code>MultiPixelPackedSampleModel</code>.
 194      * The <code>DataBuffer</code> has a single bank.
 195      * @return a <code>DataBuffer</code> with the same data type and
 196      * size as this <code>MultiPixelPackedSampleModel</code>.
 197      */
 198     public DataBuffer createDataBuffer() {
 199         DataBuffer dataBuffer = null;
 200 
 201         int size = (int)scanlineStride*height;
 202         switch (dataType) {
 203         case DataBuffer.TYPE_BYTE:
 204             dataBuffer = new DataBufferByte(size+(dataBitOffset+7)/8);
 205             break;
 206         case DataBuffer.TYPE_USHORT:
 207             dataBuffer = new DataBufferUShort(size+(dataBitOffset+15)/16);
 208             break;
 209         case DataBuffer.TYPE_INT:
 210             dataBuffer = new DataBufferInt(size+(dataBitOffset+31)/32);
 211             break;
 212         }
 213         return dataBuffer;
 214     }
 215 
 216     /**
 217      * Returns the number of data elements needed to transfer one pixel
 218      * via the {@link #getDataElements} and {@link #setDataElements}
 219      * methods.  For a <code>MultiPixelPackedSampleModel</code>, this is
 220      * one.
 221      * @return the number of data elements.
 222      */
 223     public int getNumDataElements() {
 224         return 1;
 225     }
 226 
 227     /**
 228      * Returns the number of bits per sample for all bands.
 229      * @return the number of bits per sample.
 230      */
 231     public int[] getSampleSize() {
 232         int sampleSize[] = {pixelBitStride};
 233         return sampleSize;
 234     }
 235 
 236     /**
 237      * Returns the number of bits per sample for the specified band.
 238      * @param band the specified band
 239      * @return the number of bits per sample for the specified band.
 240      */
 241     public int getSampleSize(int band) {
 242         return pixelBitStride;
 243     }
 244 
 245     /**
 246      * Returns the offset of pixel (x,&nbsp;y) in data array elements.
 247      * @param x the X coordinate of the specified pixel
 248      * @param y the Y coordinate of the specified pixel
 249      * @return the offset of the specified pixel.
 250      */
 251     public int getOffset(int x, int y) {
 252         int offset = y * scanlineStride;
 253         offset +=  (x*pixelBitStride+dataBitOffset)/dataElementSize;
 254         return offset;
 255     }
 256 
 257     /**
 258      *  Returns the offset, in bits, into the data element in which it is
 259      *  stored for the <code>x</code>th pixel of a scanline.
 260      *  This offset is the same for all scanlines.
 261      *  @param x the specified pixel
 262      *  @return the bit offset of the specified pixel.
 263      */
 264     public int getBitOffset(int x){
 265        return  (x*pixelBitStride+dataBitOffset)%dataElementSize;
 266     }
 267 
 268     /**
 269      * Returns the scanline stride.
 270      * @return the scanline stride of this
 271      * <code>MultiPixelPackedSampleModel</code>.
 272      */
 273     public int getScanlineStride() {
 274         return scanlineStride;
 275     }
 276 
 277     /**
 278      * Returns the pixel bit stride in bits.  This value is the same as
 279      * the number of bits per pixel.
 280      * @return the <code>pixelBitStride</code> of this
 281      * <code>MultiPixelPackedSampleModel</code>.
 282      */
 283     public int getPixelBitStride() {
 284         return pixelBitStride;
 285     }
 286 
 287     /**
 288      * Returns the data bit offset in bits.
 289      * @return the <code>dataBitOffset</code> of this
 290      * <code>MultiPixelPackedSampleModel</code>.
 291      */
 292     public int getDataBitOffset() {
 293         return dataBitOffset;
 294     }
 295 
 296     /**
 297      *  Returns the TransferType used to transfer pixels by way of the
 298      *  <code>getDataElements</code> and <code>setDataElements</code>
 299      *  methods. The TransferType might or might not be the same as the
 300      *  storage DataType.  The TransferType is one of
 301      *  DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT,
 302      *  or DataBuffer.TYPE_INT.
 303      *  @return the transfertype.
 304      */
 305     public int getTransferType() {
 306         if (pixelBitStride > 16)
 307             return DataBuffer.TYPE_INT;
 308         else if (pixelBitStride > 8)
 309             return DataBuffer.TYPE_USHORT;
 310         else
 311             return DataBuffer.TYPE_BYTE;
 312     }
 313 
 314     /**
 315      * Creates a new <code>MultiPixelPackedSampleModel</code> with a
 316      * subset of the bands of this
 317      * <code>MultiPixelPackedSampleModel</code>.  Since a
 318      * <code>MultiPixelPackedSampleModel</code> only has one band, the
 319      * bands argument must have a length of one and indicate the zeroth
 320      * band.
 321      * @param bands the specified bands
 322      * @return a new <code>SampleModel</code> with a subset of bands of
 323      * this <code>MultiPixelPackedSampleModel</code>.
 324      * @exception RasterFormatException if the number of bands requested
 325      * is not one.
 326      * @throws IllegalArgumentException if <code>w</code> or
 327      *         <code>h</code> is not greater than 0
 328      */
 329     public SampleModel createSubsetSampleModel(int bands[]) {
 330         if (bands != null) {
 331            if (bands.length != 1)
 332             throw new RasterFormatException("MultiPixelPackedSampleModel has "
 333                                             + "only one band.");
 334         }
 335         SampleModel sm = createCompatibleSampleModel(width, height);
 336         return sm;
 337     }
 338 
 339     /**
 340      * Returns as <code>int</code> the sample in a specified band for the
 341      * pixel located at (x,&nbsp;y).  An
 342      * <code>ArrayIndexOutOfBoundsException</code> is thrown if the
 343      * coordinates are not in bounds.
 344      * @param x         the X coordinate of the specified pixel
 345      * @param y         the Y coordinate of the specified pixel
 346      * @param b         the band to return, which is assumed to be 0
 347      * @param data      the <code>DataBuffer</code> containing the image
 348      *                  data
 349      * @return the specified band containing the sample of the specified
 350      * pixel.
 351      * @exception ArrayIndexOutOfBoundException if the specified
 352      *          coordinates are not in bounds.
 353      * @see #setSample(int, int, int, int, DataBuffer)
 354      */
 355     public int getSample(int x, int y, int b, DataBuffer data) {
 356         // 'b' must be 0
 357         if ((x < 0) || (y < 0) || (x >= width) || (y >= height) ||
 358             (b != 0)) {
 359             throw new ArrayIndexOutOfBoundsException
 360                 ("Coordinate out of bounds!");
 361         }
 362         int bitnum = dataBitOffset + x*pixelBitStride;
 363         int element = data.getElem(y*scanlineStride + bitnum/dataElementSize);
 364         int shift = dataElementSize - (bitnum & (dataElementSize-1))
 365                     - pixelBitStride;
 366         return (element >> shift) & bitMask;
 367     }
 368 
 369     /**
 370      * Sets a sample in the specified band for the pixel located at
 371      * (x,&nbsp;y) in the <code>DataBuffer</code> using an
 372      * <code>int</code> for input.
 373      * An <code>ArrayIndexOutOfBoundsException</code> is thrown if the
 374      * coordinates are not in bounds.
 375      * @param x the X coordinate of the specified pixel
 376      * @param y the Y coordinate of the specified pixel
 377      * @param b the band to return, which is assumed to be 0
 378      * @param s the input sample as an <code>int</code>
 379      * @param data the <code>DataBuffer</code> where image data is stored
 380      * @exception ArrayIndexOutOfBoundsException if the coordinates are
 381      * not in bounds.
 382      * @see #getSample(int, int, int, DataBuffer)
 383      */
 384     public void setSample(int x, int y, int b, int s,
 385                           DataBuffer data) {
 386         // 'b' must be 0
 387         if ((x < 0) || (y < 0) || (x >= width) || (y >= height) ||
 388             (b != 0)) {
 389             throw new ArrayIndexOutOfBoundsException
 390                 ("Coordinate out of bounds!");
 391         }
 392         int bitnum = dataBitOffset + x * pixelBitStride;
 393         int index = y * scanlineStride + (bitnum / dataElementSize);
 394         int shift = dataElementSize - (bitnum & (dataElementSize-1))
 395                     - pixelBitStride;
 396         int element = data.getElem(index);
 397         element &= ~(bitMask << shift);
 398         element |= (s & bitMask) << shift;
 399         data.setElem(index,element);
 400     }
 401 
 402     /**
 403      * Returns data for a single pixel in a primitive array of type
 404      * TransferType.  For a <code>MultiPixelPackedSampleModel</code>,
 405      * the array has one element, and the type is the smallest of
 406      * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, or DataBuffer.TYPE_INT
 407      * that can hold a single pixel.  Generally, <code>obj</code>
 408      * should be passed in as <code>null</code>, so that the
 409      * <code>Object</code> is created automatically and is the
 410      * correct primitive data type.
 411      * <p>
 412      * The following code illustrates transferring data for one pixel from
 413      * <code>DataBuffer</code> <code>db1</code>, whose storage layout is
 414      * described by <code>MultiPixelPackedSampleModel</code>
 415      * <code>mppsm1</code>, to <code>DataBuffer</code> <code>db2</code>,
 416      * whose storage layout is described by
 417      * <code>MultiPixelPackedSampleModel</code> <code>mppsm2</code>.
 418      * The transfer is generally more efficient than using
 419      * <code>getPixel</code> or <code>setPixel</code>.
 420      * <pre>
 421      *       MultiPixelPackedSampleModel mppsm1, mppsm2;
 422      *       DataBufferInt db1, db2;
 423      *       mppsm2.setDataElements(x, y, mppsm1.getDataElements(x, y, null,
 424      *                              db1), db2);
 425      * </pre>
 426      * Using <code>getDataElements</code> or <code>setDataElements</code>
 427      * to transfer between two <code>DataBuffer/SampleModel</code> pairs
 428      * is legitimate if the <code>SampleModels</code> have the same number
 429      * of bands, corresponding bands have the same number of
 430      * bits per sample, and the TransferTypes are the same.
 431      * <p>
 432      * If <code>obj</code> is not <code>null</code>, it should be a
 433      * primitive array of type TransferType.  Otherwise, a
 434      * <code>ClassCastException</code> is thrown.  An
 435      * <code>ArrayIndexOutOfBoundsException</code> is thrown if the
 436      * coordinates are not in bounds, or if <code>obj</code> is not
 437      * <code>null</code> and is not large enough to hold the pixel data.
 438      * @param x the X coordinate of the specified pixel
 439      * @param y the Y coordinate of the specified pixel
 440      * @param obj a primitive array in which to return the pixel data or
 441      *          <code>null</code>.
 442      * @param data the <code>DataBuffer</code> containing the image data.
 443      * @return an <code>Object</code> containing data for the specified
 444      *  pixel.
 445      * @exception ClassCastException if <code>obj</code> is not a
 446      *  primitive array of type TransferType or is not <code>null</code>
 447      * @exception ArrayIndexOutOfBoundsException if the coordinates are
 448      * not in bounds, or if <code>obj</code> is not <code>null</code> or
 449      * not large enough to hold the pixel data
 450      * @see #setDataElements(int, int, Object, DataBuffer)
 451      */
 452     public Object getDataElements(int x, int y, Object obj, DataBuffer data) {
 453         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
 454             throw new ArrayIndexOutOfBoundsException
 455                 ("Coordinate out of bounds!");
 456         }
 457 
 458         int type = getTransferType();
 459         int bitnum = dataBitOffset + x*pixelBitStride;
 460         int shift = dataElementSize - (bitnum & (dataElementSize-1))
 461                     - pixelBitStride;
 462         int element = 0;
 463 
 464         switch(type) {
 465 
 466         case DataBuffer.TYPE_BYTE:
 467 
 468             byte[] bdata;
 469 
 470             if (obj == null)
 471                 bdata = new byte[1];
 472             else
 473                 bdata = (byte[])obj;
 474 
 475             element = data.getElem(y*scanlineStride +
 476                                     bitnum/dataElementSize);
 477             bdata[0] = (byte)((element >> shift) & bitMask);
 478 
 479             obj = (Object)bdata;
 480             break;
 481 
 482         case DataBuffer.TYPE_USHORT:
 483 
 484             short[] sdata;
 485 
 486             if (obj == null)
 487                 sdata = new short[1];
 488             else
 489                 sdata = (short[])obj;
 490 
 491             element = data.getElem(y*scanlineStride +
 492                                    bitnum/dataElementSize);
 493             sdata[0] = (short)((element >> shift) & bitMask);
 494 
 495             obj = (Object)sdata;
 496             break;
 497 
 498         case DataBuffer.TYPE_INT:
 499 
 500             int[] idata;
 501 
 502             if (obj == null)
 503                 idata = new int[1];
 504             else
 505                 idata = (int[])obj;
 506 
 507             element = data.getElem(y*scanlineStride +
 508                                    bitnum/dataElementSize);
 509             idata[0] = (element >> shift) & bitMask;
 510 
 511             obj = (Object)idata;
 512             break;
 513         }
 514 
 515         return obj;
 516     }
 517 
 518     /**
 519      * Returns the specified single band pixel in the first element
 520      * of an <code>int</code> array.
 521      * <code>ArrayIndexOutOfBoundsException</code> is thrown if the
 522      * coordinates are not in bounds.
 523      * @param x the X coordinate of the specified pixel
 524      * @param y the Y coordinate of the specified pixel
 525      * @param iArray the array containing the pixel to be returned or
 526      *  <code>null</code>
 527      * @param data the <code>DataBuffer</code> where image data is stored
 528      * @return an array containing the specified pixel.
 529      * @exception ArrayIndexOutOfBoundsException if the coordinates
 530      *  are not in bounds
 531      * @see #setPixel(int, int, int[], DataBuffer)
 532      */
 533     public int[] getPixel(int x, int y, int iArray[], DataBuffer data) {
 534         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
 535             throw new ArrayIndexOutOfBoundsException
 536                 ("Coordinate out of bounds!");
 537         }
 538         int pixels[];
 539         if (iArray != null) {
 540            pixels = iArray;
 541         } else {
 542            pixels = new int [numBands];
 543         }
 544         int bitnum = dataBitOffset + x*pixelBitStride;
 545         int element = data.getElem(y*scanlineStride + bitnum/dataElementSize);
 546         int shift = dataElementSize - (bitnum & (dataElementSize-1))
 547                     - pixelBitStride;
 548         pixels[0] = (element >> shift) & bitMask;
 549         return pixels;
 550     }
 551 
 552     /**
 553      * Sets the data for a single pixel in the specified
 554      * <code>DataBuffer</code> from a primitive array of type
 555      * TransferType.  For a <code>MultiPixelPackedSampleModel</code>,
 556      * only the first element of the array holds valid data,
 557      * and the type must be the smallest of
 558      * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, or DataBuffer.TYPE_INT
 559      * that can hold a single pixel.
 560      * <p>
 561      * The following code illustrates transferring data for one pixel from
 562      * <code>DataBuffer</code> <code>db1</code>, whose storage layout is
 563      * described by <code>MultiPixelPackedSampleModel</code>
 564      * <code>mppsm1</code>, to <code>DataBuffer</code> <code>db2</code>,
 565      * whose storage layout is described by
 566      * <code>MultiPixelPackedSampleModel</code> <code>mppsm2</code>.
 567      * The transfer is generally more efficient than using
 568      * <code>getPixel</code> or <code>setPixel</code>.
 569      * <pre>
 570      *       MultiPixelPackedSampleModel mppsm1, mppsm2;
 571      *       DataBufferInt db1, db2;
 572      *       mppsm2.setDataElements(x, y, mppsm1.getDataElements(x, y, null,
 573      *                              db1), db2);
 574      * </pre>
 575      * Using <code>getDataElements</code> or <code>setDataElements</code> to
 576      * transfer between two <code>DataBuffer/SampleModel</code> pairs is
 577      * legitimate if the <code>SampleModel</code> objects have
 578      * the same number of bands, corresponding bands have the same number of
 579      * bits per sample, and the TransferTypes are the same.
 580      * <p>
 581      * <code>obj</code> must be a primitive array of type TransferType.
 582      * Otherwise, a <code>ClassCastException</code> is thrown.  An
 583      * <code>ArrayIndexOutOfBoundsException</code> is thrown if the
 584      * coordinates are not in bounds, or if <code>obj</code> is not large
 585      * enough to hold the pixel data.
 586      * @param x the X coordinate of the pixel location
 587      * @param y the Y coordinate of the pixel location
 588      * @param obj a primitive array containing pixel data
 589      * @param data the <code>DataBuffer</code> containing the image data
 590      * @see #getDataElements(int, int, Object, DataBuffer)
 591      */
 592     public void setDataElements(int x, int y, Object obj, DataBuffer data) {
 593         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
 594             throw new ArrayIndexOutOfBoundsException
 595                 ("Coordinate out of bounds!");
 596         }
 597 
 598         int type = getTransferType();
 599         int bitnum = dataBitOffset + x * pixelBitStride;
 600         int index = y * scanlineStride + (bitnum / dataElementSize);
 601         int shift = dataElementSize - (bitnum & (dataElementSize-1))
 602                     - pixelBitStride;
 603         int element = data.getElem(index);
 604         element &= ~(bitMask << shift);
 605 
 606         switch(type) {
 607 
 608         case DataBuffer.TYPE_BYTE:
 609 
 610             byte[] barray = (byte[])obj;
 611             element |= ( ((int)(barray[0])&0xff) & bitMask) << shift;
 612             data.setElem(index, element);
 613             break;
 614 
 615         case DataBuffer.TYPE_USHORT:
 616 
 617             short[] sarray = (short[])obj;
 618             element |= ( ((int)(sarray[0])&0xffff) & bitMask) << shift;
 619             data.setElem(index, element);
 620             break;
 621 
 622         case DataBuffer.TYPE_INT:
 623 
 624             int[] iarray = (int[])obj;
 625             element |= (iarray[0] & bitMask) << shift;
 626             data.setElem(index, element);
 627             break;
 628         }
 629     }
 630 
 631     /**
 632      * Sets a pixel in the <code>DataBuffer</code> using an
 633      * <code>int</code> array for input.
 634      * <code>ArrayIndexOutOfBoundsException</code> is thrown if
 635      * the coordinates are not in bounds.
 636      * @param x the X coordinate of the pixel location
 637      * @param y the Y coordinate of the pixel location
 638      * @param iArray the input pixel in an <code>int</code> array
 639      * @param data the <code>DataBuffer</code> containing the image data
 640      * @see #getPixel(int, int, int[], DataBuffer)
 641      */
 642     public void setPixel(int x, int y, int[] iArray, DataBuffer data) {
 643         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
 644             throw new ArrayIndexOutOfBoundsException
 645                 ("Coordinate out of bounds!");
 646         }
 647         int bitnum = dataBitOffset + x * pixelBitStride;
 648         int index = y * scanlineStride + (bitnum / dataElementSize);
 649         int shift = dataElementSize - (bitnum & (dataElementSize-1))
 650                     - pixelBitStride;
 651         int element = data.getElem(index);
 652         element &= ~(bitMask << shift);
 653         element |= (iArray[0] & bitMask) << shift;
 654         data.setElem(index,element);
 655     }
 656 
 657     public boolean equals(Object o) {
 658         if ((o == null) || !(o instanceof MultiPixelPackedSampleModel)) {
 659             return false;
 660         }
 661 
 662         MultiPixelPackedSampleModel that = (MultiPixelPackedSampleModel)o;
 663         return this.width == that.width &&
 664             this.height == that.height &&
 665             this.numBands == that.numBands &&
 666             this.dataType == that.dataType &&
 667             this.pixelBitStride == that.pixelBitStride &&
 668             this.bitMask == that.bitMask &&
 669             this.pixelsPerDataElement == that.pixelsPerDataElement &&
 670             this.dataElementSize == that.dataElementSize &&
 671             this.dataBitOffset == that.dataBitOffset &&
 672             this.scanlineStride == that.scanlineStride;
 673     }
 674 
 675     // If we implement equals() we must also implement hashCode
 676     public int hashCode() {
 677         int hash = 0;
 678         hash = width;
 679         hash <<= 8;
 680         hash ^= height;
 681         hash <<= 8;
 682         hash ^= numBands;
 683         hash <<= 8;
 684         hash ^= dataType;
 685         hash <<= 8;
 686         hash ^= pixelBitStride;
 687         hash <<= 8;
 688         hash ^= bitMask;
 689         hash <<= 8;
 690         hash ^= pixelsPerDataElement;
 691         hash <<= 8;
 692         hash ^= dataElementSize;
 693         hash <<= 8;
 694         hash ^= dataBitOffset;
 695         hash <<= 8;
 696         hash ^= scanlineStride;
 697         return hash;
 698     }
 699 }