1/*
2 * Copyright (c) 1997, 2014, 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
36package java.awt.image;
37
38import 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} for pixel {@code x,y}
59 * from DataBuffer {@code data}:
60 * <pre>{@code
61 *      int sample = data.getElem(y * scanlineStride + x);
62 *      sample = (sample & bitMasks[b]) >>> bitOffsets[b];
63 * }</pre>
64 */
65
66public 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} is not
104     *         either {@code DataBuffer.TYPE_BYTE},
105     *         {@code DataBuffer.TYPE_USHORT}, or
106     *         {@code DataBuffer.TYPE_INT}
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} or
134     *         {@code h} is not greater than 0
135     * @throws IllegalArgumentException if any mask in
136     *         {@code bitMask} is not contiguous
137     * @throws IllegalArgumentException if {@code dataType} is not
138     *         either {@code DataBuffer.TYPE_BYTE},
139     *         {@code DataBuffer.TYPE_USHORT}, or
140     *         {@code DataBuffer.TYPE_INT}
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 = 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}
212     * @param h the height of the resulting {@code SampleModel}
213     * @return a {@code SinglePixelPackedSampleModel} with the
214     *         specified width and height.
215     * @throws IllegalArgumentException if {@code w} or
216     *         {@code h} 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}
260     *  can be retrieved from a DataBuffer {@code data} with a
261     *  SinglePixelPackedSampleModel {@code sppsm} 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 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 bitMasks.clone();
287    }
288
289    /** Returns the scanline stride of this SinglePixelPackedSampleModel.
290     *  @return the scanline stride of this
291     *          {@code SinglePixelPackedSampleModel}.
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}, whose storage layout is described by
332     * SinglePixelPackedSampleModel {@code sppsm1}, to
333     * DataBuffer {@code db2}, whose storage layout is described by
334     * SinglePixelPackedSampleModel {@code sppsm2}.
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}, whose storage layout is described by
570     * SinglePixelPackedSampleModel {@code sppsm1},
571     * to DataBuffer {@code db2}, whose storage layout is described by
572     * SinglePixelPackedSampleModel {@code sppsm2}.
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 |= (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}
802