View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  /* ****************************************************************
27   ******************************************************************
28   ******************************************************************
29   *** COPYRIGHT (c) Eastman Kodak Company, 1997
30   *** As  an unpublished  work pursuant to Title 17 of the United
31   *** States Code.  All rights reserved.
32   ******************************************************************
33   ******************************************************************
34   ******************************************************************/
35  
36  package java.awt.image;
37  
38  import java.util.Arrays;
39  
40  /**
41   *  This class represents pixel data packed such that the N samples which make
42   *  up a single pixel are stored in a single data array element, and each data
43   *  data array element holds samples for only one pixel.
44   *  This class supports
45   *  {@link DataBuffer#TYPE_BYTE TYPE_BYTE},
46   *  {@link DataBuffer#TYPE_USHORT TYPE_USHORT},
47   *  {@link DataBuffer#TYPE_INT TYPE_INT} data types.
48   *  All data array elements reside
49   *  in the first bank of a DataBuffer.  Accessor methods are provided so
50   *  that the image data can be manipulated directly. Scanline stride is the
51   *  number of data array elements between a given sample and the corresponding
52   *  sample in the same column of the next scanline. Bit masks are the masks
53   *  required to extract the samples representing the bands of the pixel.
54   *  Bit offsets are the offsets in bits into the data array
55   *  element of the samples representing the bands of the pixel.
56   * <p>
57   * The following code illustrates extracting the bits of the sample
58   * representing band <code>b</code> for pixel <code>x,y</code>
59   * from DataBuffer <code>data</code>:
60   * <pre>{@code
61   *      int sample = data.getElem(y * scanlineStride + x);
62   *      sample = (sample & bitMasks[b]) >>> bitOffsets[b];
63   * }</pre>
64   */
65  
66  public class SinglePixelPackedSampleModel extends SampleModel
67  {
68      /** Bit masks for all bands of the image data. */
69      private int bitMasks[];
70  
71      /** Bit Offsets for all bands of the image data. */
72      private int bitOffsets[];
73  
74      /** Bit sizes for all the bands of the image data. */
75      private int bitSizes[];
76  
77      /** Maximum bit size. */
78      private int maxBitSize;
79  
80      /** Line stride of the region of image data described by this
81       *  SinglePixelPackedSampleModel.
82       */
83      private int scanlineStride;
84  
85      private static native void initIDs();
86      static {
87          ColorModel.loadLibraries();
88          initIDs();
89      }
90  
91      /**
92       * Constructs a SinglePixelPackedSampleModel with bitMasks.length bands.
93       * Each sample is stored in a data array element in the position of
94       * its corresponding bit mask.  Each bit mask must be contiguous and
95       * masks must not overlap. Bit masks exceeding data type capacity are
96       * truncated.
97       * @param dataType  The data type for storing samples.
98       * @param w         The width (in pixels) of the region of the
99       *                  image data described.
100      * @param h         The height (in pixels) of the region of the
101      *                  image data described.
102      * @param bitMasks  The bit masks for all bands.
103      * @throws IllegalArgumentException if <code>dataType</code> is not
104      *         either <code>DataBuffer.TYPE_BYTE</code>,
105      *         <code>DataBuffer.TYPE_USHORT</code>, or
106      *         <code>DataBuffer.TYPE_INT</code>
107      */
108     public SinglePixelPackedSampleModel(int dataType, int w, int h,
109                                    int bitMasks[]) {
110         this(dataType, w, h, w, bitMasks);
111         if (dataType != DataBuffer.TYPE_BYTE &&
112             dataType != DataBuffer.TYPE_USHORT &&
113             dataType != DataBuffer.TYPE_INT) {
114             throw new IllegalArgumentException("Unsupported data type "+
115                                                dataType);
116         }
117     }
118 
119     /**
120      * Constructs a SinglePixelPackedSampleModel with bitMasks.length bands
121      * and a scanline stride equal to scanlineStride data array elements.
122      * Each sample is stored in a data array element in the position of
123      * its corresponding bit mask.  Each bit mask must be contiguous and
124      * masks must not overlap. Bit masks exceeding data type capacity are
125      * truncated.
126      * @param dataType  The data type for storing samples.
127      * @param w         The width (in pixels) of the region of
128      *                  image data described.
129      * @param h         The height (in pixels) of the region of
130      *                  image data described.
131      * @param scanlineStride The line stride of the image data.
132      * @param bitMasks The bit masks for all bands.
133      * @throws IllegalArgumentException if <code>w</code> or
134      *         <code>h</code> is not greater than 0
135      * @throws IllegalArgumentException if any mask in
136      *         <code>bitMask</code> is not contiguous
137      * @throws IllegalArgumentException if <code>dataType</code> is not
138      *         either <code>DataBuffer.TYPE_BYTE</code>,
139      *         <code>DataBuffer.TYPE_USHORT</code>, or
140      *         <code>DataBuffer.TYPE_INT</code>
141      */
142     public SinglePixelPackedSampleModel(int dataType, int w, int h,
143                                    int scanlineStride, int bitMasks[]) {
144         super(dataType, w, h, bitMasks.length);
145         if (dataType != DataBuffer.TYPE_BYTE &&
146             dataType != DataBuffer.TYPE_USHORT &&
147             dataType != DataBuffer.TYPE_INT) {
148             throw new IllegalArgumentException("Unsupported data type "+
149                                                dataType);
150         }
151         this.dataType = dataType;
152         this.bitMasks = (int[]) bitMasks.clone();
153         this.scanlineStride = scanlineStride;
154 
155         this.bitOffsets = new int[numBands];
156         this.bitSizes = new int[numBands];
157 
158         int maxMask = (int)((1L << DataBuffer.getDataTypeSize(dataType)) - 1);
159 
160         this.maxBitSize = 0;
161         for (int i=0; i<numBands; i++) {
162             int bitOffset = 0, bitSize = 0, mask;
163             this.bitMasks[i] &= maxMask;
164             mask = this.bitMasks[i];
165             if (mask != 0) {
166                 while ((mask & 1) == 0) {
167                     mask = mask >>> 1;
168                     bitOffset++;
169                 }
170                 while ((mask & 1) == 1) {
171                     mask = mask >>> 1;
172                     bitSize++;
173                 }
174                 if (mask != 0) {
175                     throw new IllegalArgumentException("Mask "+bitMasks[i]+
176                                                        " must be contiguous");
177                 }
178             }
179             bitOffsets[i] = bitOffset;
180             bitSizes[i] = bitSize;
181             if (bitSize > maxBitSize) {
182                 maxBitSize = bitSize;
183             }
184         }
185     }
186 
187     /**
188      * Returns the number of data elements needed to transfer one pixel
189      * via the getDataElements and setDataElements methods.
190      * For a SinglePixelPackedSampleModel, this is one.
191      */
192     public int getNumDataElements() {
193         return 1;
194     }
195 
196     /**
197      * Returns the size of the buffer (in data array elements)
198      * needed for a data buffer that matches this
199      * SinglePixelPackedSampleModel.
200      */
201     private long getBufferSize() {
202       long size = scanlineStride * (height-1) + width;
203       return size;
204     }
205 
206     /**
207      * Creates a new SinglePixelPackedSampleModel with the specified
208      * width and height.  The new SinglePixelPackedSampleModel will have the
209      * same storage data type and bit masks as this
210      * SinglePixelPackedSampleModel.
211      * @param w the width of the resulting <code>SampleModel</code>
212      * @param h the height of the resulting <code>SampleModel</code>
213      * @return a <code>SinglePixelPackedSampleModel</code> with the
214      *         specified width and height.
215      * @throws IllegalArgumentException if <code>w</code> or
216      *         <code>h</code> is not greater than 0
217      */
218     public SampleModel createCompatibleSampleModel(int w, int h) {
219       SampleModel sampleModel = new SinglePixelPackedSampleModel(dataType, w, h,
220                                                               bitMasks);
221       return sampleModel;
222     }
223 
224     /**
225      * Creates a DataBuffer that corresponds to this
226      * SinglePixelPackedSampleModel.  The DataBuffer's data type and size
227      * will be consistent with this SinglePixelPackedSampleModel.  The
228      * DataBuffer will have a single bank.
229      */
230     public DataBuffer createDataBuffer() {
231         DataBuffer dataBuffer = null;
232 
233         int size = (int)getBufferSize();
234         switch (dataType) {
235         case DataBuffer.TYPE_BYTE:
236             dataBuffer = new DataBufferByte(size);
237             break;
238         case DataBuffer.TYPE_USHORT:
239             dataBuffer = new DataBufferUShort(size);
240             break;
241         case DataBuffer.TYPE_INT:
242             dataBuffer = new DataBufferInt(size);
243             break;
244         }
245         return dataBuffer;
246     }
247 
248     /** Returns the number of bits per sample for all bands. */
249     public int[] getSampleSize() {
250         return bitSizes.clone();
251     }
252 
253     /** Returns the number of bits per sample for the specified band. */
254     public int getSampleSize(int band) {
255         return bitSizes[band];
256     }
257 
258     /** Returns the offset (in data array elements) of pixel (x,y).
259      *  The data element containing pixel <code>x,y</code>
260      *  can be retrieved from a DataBuffer <code>data</code> with a
261      *  SinglePixelPackedSampleModel <code>sppsm</code> as:
262      * <pre>
263      *        data.getElem(sppsm.getOffset(x, y));
264      * </pre>
265      * @param x the X coordinate of the specified pixel
266      * @param y the Y coordinate of the specified pixel
267      * @return the offset of the specified pixel.
268      */
269     public int getOffset(int x, int y) {
270         int offset = y * scanlineStride + x;
271         return offset;
272     }
273 
274     /** Returns the bit offsets into the data array element representing
275      *  a pixel for all bands.
276      *  @return the bit offsets representing a pixel for all bands.
277      */
278     public int [] getBitOffsets() {
279       return (int[])bitOffsets.clone();
280     }
281 
282     /** Returns the bit masks for all bands.
283      *  @return the bit masks for all bands.
284      */
285     public int [] getBitMasks() {
286       return (int[])bitMasks.clone();
287     }
288 
289     /** Returns the scanline stride of this SinglePixelPackedSampleModel.
290      *  @return the scanline stride of this
291      *          <code>SinglePixelPackedSampleModel</code>.
292      */
293     public int getScanlineStride() {
294       return scanlineStride;
295     }
296 
297     /**
298      * This creates a new SinglePixelPackedSampleModel with a subset of the
299      * bands of this SinglePixelPackedSampleModel.  The new
300      * SinglePixelPackedSampleModel can be used with any DataBuffer that the
301      * existing SinglePixelPackedSampleModel can be used with.  The new
302      * SinglePixelPackedSampleModel/DataBuffer combination will represent
303      * an image with a subset of the bands of the original
304      * SinglePixelPackedSampleModel/DataBuffer combination.
305      * @exception RasterFormatException if the length of the bands argument is
306      *                                  greater than the number of bands in
307      *                                  the sample model.
308      */
309     public SampleModel createSubsetSampleModel(int bands[]) {
310         if (bands.length > numBands)
311             throw new RasterFormatException("There are only " +
312                                             numBands +
313                                             " bands");
314         int newBitMasks[] = new int[bands.length];
315         for (int i=0; i<bands.length; i++)
316             newBitMasks[i] = bitMasks[bands[i]];
317 
318         return new SinglePixelPackedSampleModel(this.dataType, width, height,
319                                            this.scanlineStride, newBitMasks);
320     }
321 
322     /**
323      * Returns data for a single pixel in a primitive array of type
324      * TransferType.  For a SinglePixelPackedSampleModel, the array will
325      * have one element, and the type will be the same as the storage
326      * data type.  Generally, obj
327      * should be passed in as null, so that the Object will be created
328      * automatically and will be of the right primitive data type.
329      * <p>
330      * The following code illustrates transferring data for one pixel from
331      * DataBuffer <code>db1</code>, whose storage layout is described by
332      * SinglePixelPackedSampleModel <code>sppsm1</code>, to
333      * DataBuffer <code>db2</code>, whose storage layout is described by
334      * SinglePixelPackedSampleModel <code>sppsm2</code>.
335      * The transfer will generally be more efficient than using
336      * getPixel/setPixel.
337      * <pre>
338      *       SinglePixelPackedSampleModel sppsm1, sppsm2;
339      *       DataBufferInt db1, db2;
340      *       sppsm2.setDataElements(x, y, sppsm1.getDataElements(x, y, null,
341      *                              db1), db2);
342      * </pre>
343      * Using getDataElements/setDataElements to transfer between two
344      * DataBuffer/SampleModel pairs is legitimate if the SampleModels have
345      * the same number of bands, corresponding bands have the same number of
346      * bits per sample, and the TransferTypes are the same.
347      * <p>
348      * If obj is non-null, it should be a primitive array of type TransferType.
349      * Otherwise, a ClassCastException is thrown.  An
350      * ArrayIndexOutOfBoundsException may be thrown if the coordinates are
351      * not in bounds, or if obj is non-null and is not large enough to hold
352      * the pixel data.
353      * @param x         The X coordinate of the pixel location.
354      * @param y         The Y coordinate of the pixel location.
355      * @param obj       If non-null, a primitive array in which to return
356      *                  the pixel data.
357      * @param data      The DataBuffer containing the image data.
358      * @return the data for the specified pixel.
359      * @see #setDataElements(int, int, Object, DataBuffer)
360      */
361     public Object getDataElements(int x, int y, Object obj, DataBuffer data) {
362         // Bounds check for 'b' will be performed automatically
363         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
364             throw new ArrayIndexOutOfBoundsException
365                 ("Coordinate out of bounds!");
366         }
367 
368         int type = getTransferType();
369 
370         switch(type) {
371 
372         case DataBuffer.TYPE_BYTE:
373 
374             byte[] bdata;
375 
376             if (obj == null)
377                 bdata = new byte[1];
378             else
379                 bdata = (byte[])obj;
380 
381             bdata[0] = (byte)data.getElem(y * scanlineStride + x);
382 
383             obj = (Object)bdata;
384             break;
385 
386         case DataBuffer.TYPE_USHORT:
387 
388             short[] sdata;
389 
390             if (obj == null)
391                 sdata = new short[1];
392             else
393                 sdata = (short[])obj;
394 
395             sdata[0] = (short)data.getElem(y * scanlineStride + x);
396 
397             obj = (Object)sdata;
398             break;
399 
400         case DataBuffer.TYPE_INT:
401 
402             int[] idata;
403 
404             if (obj == null)
405                 idata = new int[1];
406             else
407                 idata = (int[])obj;
408 
409             idata[0] = data.getElem(y * scanlineStride + x);
410 
411             obj = (Object)idata;
412             break;
413         }
414 
415         return obj;
416     }
417 
418     /**
419      * Returns all samples in for the specified pixel in an int array.
420      * ArrayIndexOutOfBoundsException may be thrown if the coordinates are
421      * not in bounds.
422      * @param x         The X coordinate of the pixel location.
423      * @param y         The Y coordinate of the pixel location.
424      * @param iArray    If non-null, returns the samples in this array
425      * @param data      The DataBuffer containing the image data.
426      * @return all samples for the specified pixel.
427      * @see #setPixel(int, int, int[], DataBuffer)
428      */
429     public int [] getPixel(int x, int y, int iArray[], DataBuffer data) {
430         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
431             throw new ArrayIndexOutOfBoundsException
432                 ("Coordinate out of bounds!");
433         }
434         int pixels[];
435         if (iArray == null) {
436             pixels = new int [numBands];
437         } else {
438             pixels = iArray;
439         }
440 
441         int value = data.getElem(y * scanlineStride + x);
442         for (int i=0; i<numBands; i++) {
443             pixels[i] = (value & bitMasks[i]) >>> bitOffsets[i];
444         }
445         return pixels;
446     }
447 
448     /**
449      * Returns all samples for the specified rectangle of pixels in
450      * an int array, one sample per array element.
451      * ArrayIndexOutOfBoundsException may be thrown if the coordinates are
452      * not in bounds.
453      * @param x         The X coordinate of the upper left pixel location.
454      * @param y         The Y coordinate of the upper left pixel location.
455      * @param w         The width of the pixel rectangle.
456      * @param h         The height of the pixel rectangle.
457      * @param iArray    If non-null, returns the samples in this array.
458      * @param data      The DataBuffer containing the image data.
459      * @return all samples for the specified region of pixels.
460      * @see #setPixels(int, int, int, int, int[], DataBuffer)
461      */
462     public int[] getPixels(int x, int y, int w, int h,
463                            int iArray[], DataBuffer data) {
464         int x1 = x + w;
465         int y1 = y + h;
466 
467         if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width ||
468             y < 0 || y >= height || h > height || y1 < 0 || y1 >  height)
469         {
470             throw new ArrayIndexOutOfBoundsException
471                 ("Coordinate out of bounds!");
472         }
473         int pixels[];
474         if (iArray != null) {
475            pixels = iArray;
476         } else {
477            pixels = new int [w*h*numBands];
478         }
479         int lineOffset = y*scanlineStride + x;
480         int dstOffset = 0;
481 
482         for (int i = 0; i < h; i++) {
483            for (int j = 0; j < w; j++) {
484               int value = data.getElem(lineOffset+j);
485               for (int k=0; k < numBands; k++) {
486                   pixels[dstOffset++] =
487                      ((value & bitMasks[k]) >>> bitOffsets[k]);
488               }
489            }
490            lineOffset += scanlineStride;
491         }
492         return pixels;
493     }
494 
495     /**
496      * Returns as int the sample in a specified band for the pixel
497      * located at (x,y).
498      * ArrayIndexOutOfBoundsException may be thrown if the coordinates are
499      * not in bounds.
500      * @param x         The X coordinate of the pixel location.
501      * @param y         The Y coordinate of the pixel location.
502      * @param b         The band to return.
503      * @param data      The DataBuffer containing the image data.
504      * @return the sample in a specified band for the specified
505      *         pixel.
506      * @see #setSample(int, int, int, int, DataBuffer)
507      */
508     public int getSample(int x, int y, int b, DataBuffer data) {
509         // Bounds check for 'b' will be performed automatically
510         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
511             throw new ArrayIndexOutOfBoundsException
512                 ("Coordinate out of bounds!");
513         }
514         int sample = data.getElem(y * scanlineStride + x);
515         return ((sample & bitMasks[b]) >>> bitOffsets[b]);
516     }
517 
518     /**
519      * Returns the samples for a specified band for the specified rectangle
520      * of pixels in an int array, one sample per array element.
521      * ArrayIndexOutOfBoundsException may be thrown if the coordinates are
522      * not in bounds.
523      * @param x         The X coordinate of the upper left pixel location.
524      * @param y         The Y coordinate of the upper left pixel location.
525      * @param w         The width of the pixel rectangle.
526      * @param h         The height of the pixel rectangle.
527      * @param b         The band to return.
528      * @param iArray    If non-null, returns the samples in this array.
529      * @param data      The DataBuffer containing the image data.
530      * @return the samples for the specified band for the specified
531      *         region of pixels.
532      * @see #setSamples(int, int, int, int, int, int[], DataBuffer)
533      */
534     public int[] getSamples(int x, int y, int w, int h, int b,
535                            int iArray[], DataBuffer data) {
536         // Bounds check for 'b' will be performed automatically
537         if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) {
538             throw new ArrayIndexOutOfBoundsException
539                 ("Coordinate out of bounds!");
540         }
541         int samples[];
542         if (iArray != null) {
543            samples = iArray;
544         } else {
545            samples = new int [w*h];
546         }
547         int lineOffset = y*scanlineStride + x;
548         int dstOffset = 0;
549 
550         for (int i = 0; i < h; i++) {
551            for (int j = 0; j < w; j++) {
552               int value = data.getElem(lineOffset+j);
553               samples[dstOffset++] =
554                  ((value & bitMasks[b]) >>> bitOffsets[b]);
555            }
556            lineOffset += scanlineStride;
557         }
558         return samples;
559     }
560 
561     /**
562      * Sets the data for a single pixel in the specified DataBuffer from a
563      * primitive array of type TransferType.  For a
564      * SinglePixelPackedSampleModel, only the first element of the array
565      * will hold valid data, and the type of the array must be the same as
566      * the storage data type of the SinglePixelPackedSampleModel.
567      * <p>
568      * The following code illustrates transferring data for one pixel from
569      * DataBuffer <code>db1</code>, whose storage layout is described by
570      * SinglePixelPackedSampleModel <code>sppsm1</code>,
571      * to DataBuffer <code>db2</code>, whose storage layout is described by
572      * SinglePixelPackedSampleModel <code>sppsm2</code>.
573      * The transfer will generally be more efficient than using
574      * getPixel/setPixel.
575      * <pre>
576      *       SinglePixelPackedSampleModel sppsm1, sppsm2;
577      *       DataBufferInt db1, db2;
578      *       sppsm2.setDataElements(x, y, sppsm1.getDataElements(x, y, null,
579      *                              db1), db2);
580      * </pre>
581      * Using getDataElements/setDataElements to transfer between two
582      * DataBuffer/SampleModel pairs is legitimate if the SampleModels have
583      * the same number of bands, corresponding bands have the same number of
584      * bits per sample, and the TransferTypes are the same.
585      * <p>
586      * obj must be a primitive array of type TransferType.  Otherwise,
587      * a ClassCastException is thrown.  An
588      * ArrayIndexOutOfBoundsException may be thrown if the coordinates are
589      * not in bounds, or if obj is not large enough to hold the pixel data.
590      * @param x         The X coordinate of the pixel location.
591      * @param y         The Y coordinate of the pixel location.
592      * @param obj       A primitive array containing pixel data.
593      * @param data      The DataBuffer containing the image data.
594      * @see #getDataElements(int, int, Object, DataBuffer)
595      */
596     public void setDataElements(int x, int y, Object obj, DataBuffer data) {
597         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
598             throw new ArrayIndexOutOfBoundsException
599                 ("Coordinate out of bounds!");
600         }
601 
602         int type = getTransferType();
603 
604         switch(type) {
605 
606         case DataBuffer.TYPE_BYTE:
607 
608             byte[] barray = (byte[])obj;
609             data.setElem(y*scanlineStride+x, ((int)barray[0])&0xff);
610             break;
611 
612         case DataBuffer.TYPE_USHORT:
613 
614             short[] sarray = (short[])obj;
615             data.setElem(y*scanlineStride+x, ((int)sarray[0])&0xffff);
616             break;
617 
618         case DataBuffer.TYPE_INT:
619 
620             int[] iarray = (int[])obj;
621             data.setElem(y*scanlineStride+x, iarray[0]);
622             break;
623         }
624     }
625 
626     /**
627      * Sets a pixel in the DataBuffer using an int array of samples for input.
628      * ArrayIndexOutOfBoundsException may be thrown if the coordinates are
629      * not in bounds.
630      * @param x         The X coordinate of the pixel location.
631      * @param y         The Y coordinate of the pixel location.
632      * @param iArray    The input samples in an int array.
633      * @param data      The DataBuffer containing the image data.
634      * @see #getPixel(int, int, int[], DataBuffer)
635      */
636     public void setPixel(int x, int y,
637                          int iArray[],
638                          DataBuffer data) {
639         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
640             throw new ArrayIndexOutOfBoundsException
641                 ("Coordinate out of bounds!");
642         }
643         int lineOffset = y * scanlineStride + x;
644         int value = data.getElem(lineOffset);
645         for (int i=0; i < numBands; i++) {
646             value &= ~bitMasks[i];
647             value |= ((iArray[i] << bitOffsets[i]) & bitMasks[i]);
648         }
649         data.setElem(lineOffset, value);
650     }
651 
652     /**
653      * Sets all samples for a rectangle of pixels from an int array containing
654      * one sample per array element.
655      * ArrayIndexOutOfBoundsException may be thrown if the coordinates are
656      * not in bounds.
657      * @param x         The X coordinate of the upper left pixel location.
658      * @param y         The Y coordinate of the upper left pixel location.
659      * @param w         The width of the pixel rectangle.
660      * @param h         The height of the pixel rectangle.
661      * @param iArray    The input samples in an int array.
662      * @param data      The DataBuffer containing the image data.
663      * @see #getPixels(int, int, int, int, int[], DataBuffer)
664      */
665     public void setPixels(int x, int y, int w, int h,
666                           int iArray[], DataBuffer data) {
667         int x1 = x + w;
668         int y1 = y + h;
669 
670         if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width ||
671             y < 0 || y >= height || h > height || y1 < 0 || y1 >  height)
672         {
673             throw new ArrayIndexOutOfBoundsException
674                 ("Coordinate out of bounds!");
675         }
676 
677         int lineOffset = y*scanlineStride + x;
678         int srcOffset = 0;
679 
680         for (int i = 0; i < h; i++) {
681            for (int j = 0; j < w; j++) {
682                int value = data.getElem(lineOffset+j);
683                for (int k=0; k < numBands; k++) {
684                    value &= ~bitMasks[k];
685                    int srcValue = iArray[srcOffset++];
686                    value |= ((srcValue << bitOffsets[k])
687                              & bitMasks[k]);
688                }
689                data.setElem(lineOffset+j, value);
690            }
691            lineOffset += scanlineStride;
692         }
693     }
694 
695     /**
696      * Sets a sample in the specified band for the pixel located at (x,y)
697      * in the DataBuffer using an int for input.
698      * ArrayIndexOutOfBoundsException may be thrown if the coordinates are
699      * not in bounds.
700      * @param x         The X coordinate of the pixel location.
701      * @param y         The Y coordinate of the pixel location.
702      * @param b         The band to set.
703      * @param s         The input sample as an int.
704      * @param data      The DataBuffer containing the image data.
705      * @see #getSample(int, int, int, DataBuffer)
706      */
707     public void setSample(int x, int y, int b, int s,
708                           DataBuffer data) {
709         // Bounds check for 'b' will be performed automatically
710         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
711             throw new ArrayIndexOutOfBoundsException
712                 ("Coordinate out of bounds!");
713         }
714         int value = data.getElem(y*scanlineStride + x);
715         value &= ~bitMasks[b];
716         value |= (s << bitOffsets[b]) & bitMasks[b];
717         data.setElem(y*scanlineStride + x,value);
718     }
719 
720     /**
721      * Sets the samples in the specified band for the specified rectangle
722      * of pixels from an int array containing one sample per array element.
723      * ArrayIndexOutOfBoundsException may be thrown if the coordinates are
724      * not in bounds.
725      * @param x         The X coordinate of the upper left pixel location.
726      * @param y         The Y coordinate of the upper left pixel location.
727      * @param w         The width of the pixel rectangle.
728      * @param h         The height of the pixel rectangle.
729      * @param b         The band to set.
730      * @param iArray    The input samples in an int array.
731      * @param data      The DataBuffer containing the image data.
732      * @see #getSamples(int, int, int, int, int, int[], DataBuffer)
733      */
734     public void setSamples(int x, int y, int w, int h, int b,
735                           int iArray[], DataBuffer data) {
736         // Bounds check for 'b' will be performed automatically
737         if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) {
738             throw new ArrayIndexOutOfBoundsException
739                 ("Coordinate out of bounds!");
740         }
741         int lineOffset = y*scanlineStride + x;
742         int srcOffset = 0;
743 
744         for (int i = 0; i < h; i++) {
745            for (int j = 0; j < w; j++) {
746               int value = data.getElem(lineOffset+j);
747               value &= ~bitMasks[b];
748               int sample = iArray[srcOffset++];
749               value |= ((int)sample << bitOffsets[b]) & bitMasks[b];
750               data.setElem(lineOffset+j,value);
751            }
752            lineOffset += scanlineStride;
753         }
754     }
755 
756     public boolean equals(Object o) {
757         if ((o == null) || !(o instanceof SinglePixelPackedSampleModel)) {
758             return false;
759         }
760 
761         SinglePixelPackedSampleModel that = (SinglePixelPackedSampleModel)o;
762         return this.width == that.width &&
763             this.height == that.height &&
764             this.numBands == that.numBands &&
765             this.dataType == that.dataType &&
766             Arrays.equals(this.bitMasks, that.bitMasks) &&
767             Arrays.equals(this.bitOffsets, that.bitOffsets) &&
768             Arrays.equals(this.bitSizes, that.bitSizes) &&
769             this.maxBitSize == that.maxBitSize &&
770             this.scanlineStride == that.scanlineStride;
771     }
772 
773     // If we implement equals() we must also implement hashCode
774     public int hashCode() {
775         int hash = 0;
776         hash = width;
777         hash <<= 8;
778         hash ^= height;
779         hash <<= 8;
780         hash ^= numBands;
781         hash <<= 8;
782         hash ^= dataType;
783         hash <<= 8;
784         for (int i = 0; i < bitMasks.length; i++) {
785             hash ^= bitMasks[i];
786             hash <<= 8;
787         }
788         for (int i = 0; i < bitOffsets.length; i++) {
789             hash ^= bitOffsets[i];
790             hash <<= 8;
791         }
792         for (int i = 0; i < bitSizes.length; i++) {
793             hash ^= bitSizes[i];
794             hash <<= 8;
795         }
796         hash ^= maxBitSize;
797         hash <<= 8;
798         hash ^= scanlineStride;
799         return hash;
800     }
801 }