jdk/src/share/classes/java/awt/image/ComponentSampleModel.java
changeset 539 7952521a4ad3
child 715 f16baef3a20e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/awt/image/ComponentSampleModel.java	Thu Apr 10 16:28:45 2008 -0700
@@ -0,0 +1,1202 @@
+/*
+ * Portions Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* ****************************************************************
+ ******************************************************************
+ ******************************************************************
+ *** COPYRIGHT (c) Eastman Kodak Company, 1997
+ *** As  an unpublished  work pursuant to Title 17 of the United
+ *** States Code.  All rights reserved.
+ ******************************************************************
+ ******************************************************************
+ ******************************************************************/
+
+package java.awt.image;
+
+import java.util.Arrays;
+
+/**
+ *  This class represents image data which is stored such that each sample
+ *  of a pixel occupies one data element of the DataBuffer.  It stores the
+ *  N samples which make up a pixel in N separate data array elements.
+ *  Different bands may be in different banks of the DataBuffer.
+ *  Accessor methods are provided so that image data can be manipulated
+ *  directly. This class can support different kinds of interleaving, e.g.
+ *  band interleaving, scanline interleaving, and pixel interleaving.
+ *  Pixel stride is the number of data array elements between two samples
+ *  for the same band on the same scanline. Scanline stride is the number
+ *  of data array elements between a given sample and the corresponding sample
+ *  in the same column of the next scanline.  Band offsets denote the number
+ *  of data array elements from the first data array element of the bank
+ *  of the DataBuffer holding each band to the first sample of the band.
+ *  The bands are numbered from 0 to N-1.  This class can represent image
+ *  data for which each sample is an unsigned integral number which can be
+ *  stored in 8, 16, or 32 bits (using <code>DataBuffer.TYPE_BYTE</code>,
+ *  <code>DataBuffer.TYPE_USHORT</code>, or <code>DataBuffer.TYPE_INT</code>,
+ *  respectively), data for which each sample is a signed integral number
+ *  which can be stored in 16 bits (using <code>DataBuffer.TYPE_SHORT</code>),
+ *  or data for which each sample is a signed float or double quantity
+ *  (using <code>DataBuffer.TYPE_FLOAT</code> or
+ *  <code>DataBuffer.TYPE_DOUBLE</code>, respectively).
+ *  All samples of a given ComponentSampleModel
+ *  are stored with the same precision.  All strides and offsets must be
+ *  non-negative.  This class supports
+ *  {@link DataBuffer#TYPE_BYTE TYPE_BYTE},
+ *  {@link DataBuffer#TYPE_USHORT TYPE_USHORT},
+ *  {@link DataBuffer#TYPE_SHORT TYPE_SHORT},
+ *  {@link DataBuffer#TYPE_INT TYPE_INT},
+ *  {@link DataBuffer#TYPE_FLOAT TYPE_FLOAT},
+ *  {@link DataBuffer#TYPE_DOUBLE TYPE_DOUBLE},
+ *  @see java.awt.image.PixelInterleavedSampleModel
+ *  @see java.awt.image.BandedSampleModel
+ */
+
+public class ComponentSampleModel extends SampleModel
+{
+    /** Offsets for all bands in data array elements. */
+    protected int bandOffsets[];
+
+    /** Index for each bank storing a band of image data. */
+    protected int[] bankIndices;
+
+    /**
+     * The number of bands in this
+     * <code>ComponentSampleModel</code>.
+     */
+    protected int numBands = 1;
+
+    /**
+     * The number of banks in this
+     * <code>ComponentSampleModel</code>.
+     */
+    protected int numBanks = 1;
+
+    /**
+     *  Line stride (in data array elements) of the region of image
+     *  data described by this ComponentSampleModel.
+     */
+    protected int scanlineStride;
+
+    /** Pixel stride (in data array elements) of the region of image
+     *  data described by this ComponentSampleModel.
+     */
+    protected int pixelStride;
+
+    static private native void initIDs();
+    static {
+        ColorModel.loadLibraries();
+        initIDs();
+    }
+
+    /**
+     * Constructs a ComponentSampleModel with the specified parameters.
+     * The number of bands will be given by the length of the bandOffsets array.
+     * All bands will be stored in the first bank of the DataBuffer.
+     * @param dataType  the data type for storing samples
+     * @param w         the width (in pixels) of the region of
+     *     image data described
+     * @param h         the height (in pixels) of the region of
+     *     image data described
+     * @param pixelStride the pixel stride of the region of image
+     *     data described
+     * @param scanlineStride the line stride of the region of image
+     *     data described
+     * @param bandOffsets the offsets of all bands
+     * @throws IllegalArgumentException if <code>w</code> or
+     *         <code>h</code> is not greater than 0
+     * @throws IllegalArgumentException if <code>pixelStride</code>
+     *         is less than 0
+     * @throws IllegalArgumentException if <code>scanlineStride</code>
+     *         is less than 0
+     * @throws IllegalArgumentException if <code>numBands</code>
+     *         is less than 1
+     * @throws IllegalArgumentException if the product of <code>w</code>
+     *         and <code>h</code> is greater than
+     *         <code>Integer.MAX_VALUE</code>
+     * @throws IllegalArgumentException if <code>dataType</code> is not
+     *         one of the supported data types
+     */
+    public ComponentSampleModel(int dataType,
+                                int w, int h,
+                                int pixelStride,
+                                int scanlineStride,
+                                int bandOffsets[]) {
+        super(dataType, w, h, bandOffsets.length);
+        this.dataType = dataType;
+        this.pixelStride = pixelStride;
+        this.scanlineStride  = scanlineStride;
+        this.bandOffsets = (int[])bandOffsets.clone();
+        numBands = bandOffsets.length;
+        if (pixelStride < 0) {
+            throw new IllegalArgumentException("Pixel stride must be >= 0");
+        }
+        // TODO - bug 4296691 - remove this check
+        if (scanlineStride < 0) {
+            throw new IllegalArgumentException("Scanline stride must be >= 0");
+        }
+        if (numBands < 1) {
+            throw new IllegalArgumentException("Must have at least one band.");
+        }
+        if ((dataType < DataBuffer.TYPE_BYTE) ||
+            (dataType > DataBuffer.TYPE_DOUBLE)) {
+            throw new IllegalArgumentException("Unsupported dataType.");
+        }
+        bankIndices = new int[numBands];
+        for (int i=0; i<numBands; i++) {
+            bankIndices[i] = 0;
+        }
+    }
+
+
+    /**
+     * Constructs a ComponentSampleModel with the specified parameters.
+     * The number of bands will be given by the length of the bandOffsets array.
+     * Different bands may be stored in different banks of the DataBuffer.
+     *
+     * @param dataType  the data type for storing samples
+     * @param w         the width (in pixels) of the region of
+     *     image data described
+     * @param h         the height (in pixels) of the region of
+     *     image data described
+     * @param pixelStride the pixel stride of the region of image
+     *     data described
+     * @param scanlineStride The line stride of the region of image
+     *     data described
+     * @param bankIndices the bank indices of all bands
+     * @param bandOffsets the band offsets of all bands
+     * @throws IllegalArgumentException if <code>w</code> or
+     *         <code>h</code> is not greater than 0
+     * @throws IllegalArgumentException if <code>pixelStride</code>
+     *         is less than 0
+     * @throws IllegalArgumentException if <code>scanlineStride</code>
+     *         is less than 0
+     * @throws IllegalArgumentException if the length of
+     *         <code>bankIndices</code> does not equal the length of
+     *         <code>bankOffsets</code>
+     * @throws IllegalArgumentException if any of the bank indices
+     *         of <code>bandIndices</code> is less than 0
+     * @throws IllegalArgumentException if <code>dataType</code> is not
+     *         one of the supported data types
+     */
+    public ComponentSampleModel(int dataType,
+                                int w, int h,
+                                int pixelStride,
+                                int scanlineStride,
+                                int bankIndices[],
+                                int bandOffsets[]) {
+        super(dataType, w, h, bandOffsets.length);
+        this.dataType = dataType;
+        this.pixelStride = pixelStride;
+        this.scanlineStride  = scanlineStride;
+        this.bandOffsets = (int[])bandOffsets.clone();
+        this.bankIndices = (int[]) bankIndices.clone();
+        if (pixelStride < 0) {
+            throw new IllegalArgumentException("Pixel stride must be >= 0");
+        }
+        // TODO - bug 4296691 - remove this check
+        if (scanlineStride < 0) {
+            throw new IllegalArgumentException("Scanline stride must be >= 0");
+        }
+        if ((dataType < DataBuffer.TYPE_BYTE) ||
+            (dataType > DataBuffer.TYPE_DOUBLE)) {
+            throw new IllegalArgumentException("Unsupported dataType.");
+        }
+        int maxBank = bankIndices[0];
+        if (maxBank < 0) {
+            throw new IllegalArgumentException("Index of bank 0 is less than "+
+                                               "0 ("+maxBank+")");
+        }
+        for (int i=1; i < bankIndices.length; i++) {
+            if (bankIndices[i] > maxBank) {
+                maxBank = bankIndices[i];
+            }
+            else if (bankIndices[i] < 0) {
+                throw new IllegalArgumentException("Index of bank "+i+
+                                                   " is less than 0 ("+
+                                                   maxBank+")");
+            }
+        }
+        numBanks         = maxBank+1;
+        numBands         = bandOffsets.length;
+        if (bandOffsets.length != bankIndices.length) {
+            throw new IllegalArgumentException("Length of bandOffsets must "+
+                                               "equal length of bankIndices.");
+        }
+    }
+
+    /**
+     * Returns the size of the data buffer (in data elements) needed
+     * for a data buffer that matches this ComponentSampleModel.
+     */
+     private long getBufferSize() {
+         int maxBandOff=bandOffsets[0];
+         for (int i=1; i<bandOffsets.length; i++)
+             maxBandOff = Math.max(maxBandOff,bandOffsets[i]);
+
+         long size = 0;
+         if (maxBandOff >= 0)
+             size += maxBandOff+1;
+         if (pixelStride > 0)
+             size += pixelStride * (width-1);
+         if (scanlineStride > 0)
+             size += scanlineStride*(height-1);
+         return size;
+     }
+
+     /**
+      * Preserves band ordering with new step factor...
+      */
+    int []orderBands(int orig[], int step) {
+        int map[] = new int[orig.length];
+        int ret[] = new int[orig.length];
+
+        for (int i=0; i<map.length; i++) map[i] = i;
+
+        for (int i = 0; i < ret.length; i++) {
+            int index = i;
+            for (int j = i+1; j < ret.length; j++) {
+                if (orig[map[index]] > orig[map[j]]) {
+                    index = j;
+                }
+            }
+            ret[map[index]] = i*step;
+            map[index]  = map[i];
+        }
+        return ret;
+    }
+
+    /**
+     * Creates a new <code>ComponentSampleModel</code> with the specified
+     * width and height.  The new <code>SampleModel</code> will have the same
+     * number of bands, storage data type, interleaving scheme, and
+     * pixel stride as this <code>SampleModel</code>.
+     * @param w the width of the resulting <code>SampleModel</code>
+     * @param h the height of the resulting <code>SampleModel</code>
+     * @return a new <code>ComponentSampleModel</code> with the specified size
+     * @throws IllegalArgumentException if <code>w</code> or
+     *         <code>h</code> is not greater than 0
+     */
+    public SampleModel createCompatibleSampleModel(int w, int h) {
+        SampleModel ret=null;
+        long size;
+        int minBandOff=bandOffsets[0];
+        int maxBandOff=bandOffsets[0];
+        for (int i=1; i<bandOffsets.length; i++) {
+            minBandOff = Math.min(minBandOff,bandOffsets[i]);
+            maxBandOff = Math.max(maxBandOff,bandOffsets[i]);
+        }
+        maxBandOff -= minBandOff;
+
+        int bands   = bandOffsets.length;
+        int bandOff[];
+        int pStride = Math.abs(pixelStride);
+        int lStride = Math.abs(scanlineStride);
+        int bStride = Math.abs(maxBandOff);
+
+        if (pStride > lStride) {
+            if (pStride > bStride) {
+                if (lStride > bStride) { // pix > line > band
+                    bandOff = new int[bandOffsets.length];
+                    for (int i=0; i<bands; i++)
+                        bandOff[i] = bandOffsets[i]-minBandOff;
+                    lStride = bStride+1;
+                    pStride = lStride*h;
+                } else { // pix > band > line
+                    bandOff = orderBands(bandOffsets,lStride*h);
+                    pStride = bands*lStride*h;
+                }
+            } else { // band > pix > line
+                pStride = lStride*h;
+                bandOff = orderBands(bandOffsets,pStride*w);
+            }
+        } else {
+            if (pStride > bStride) { // line > pix > band
+                bandOff = new int[bandOffsets.length];
+                for (int i=0; i<bands; i++)
+                    bandOff[i] = bandOffsets[i]-minBandOff;
+                pStride = bStride+1;
+                lStride = pStride*w;
+            } else {
+                if (lStride > bStride) { // line > band > pix
+                    bandOff = orderBands(bandOffsets,pStride*w);
+                    lStride = bands*pStride*w;
+                } else { // band > line > pix
+                    lStride = pStride*w;
+                    bandOff = orderBands(bandOffsets,lStride*h);
+                }
+            }
+        }
+
+        // make sure we make room for negative offsets...
+        int base = 0;
+        if (scanlineStride < 0) {
+            base += lStride*h;
+            lStride *= -1;
+        }
+        if (pixelStride    < 0) {
+            base += pStride*w;
+            pStride *= -1;
+        }
+
+        for (int i=0; i<bands; i++)
+            bandOff[i] += base;
+        return new ComponentSampleModel(dataType, w, h, pStride,
+                                        lStride, bankIndices, bandOff);
+    }
+
+    /**
+     * Creates a new ComponentSampleModel with a subset of the bands
+     * of this ComponentSampleModel.  The new ComponentSampleModel can be
+     * used with any DataBuffer that the existing ComponentSampleModel
+     * can be used with.  The new ComponentSampleModel/DataBuffer
+     * combination will represent an image with a subset of the bands
+     * of the original ComponentSampleModel/DataBuffer combination.
+     * @param bands a subset of bands from this
+     *              <code>ComponentSampleModel</code>
+     * @return a <code>ComponentSampleModel</code> created with a subset
+     *          of bands from this <code>ComponentSampleModel</code>.
+     */
+    public SampleModel createSubsetSampleModel(int bands[]) {
+       if (bands.length > bankIndices.length)
+            throw new RasterFormatException("There are only " +
+                                            bankIndices.length +
+                                            " bands");
+        int newBankIndices[] = new int[bands.length];
+        int newBandOffsets[] = new int[bands.length];
+
+        for (int i=0; i<bands.length; i++) {
+            newBankIndices[i] = bankIndices[bands[i]];
+            newBandOffsets[i] = bandOffsets[bands[i]];
+        }
+
+        return new ComponentSampleModel(this.dataType, width, height,
+                                        this.pixelStride,
+                                        this.scanlineStride,
+                                        newBankIndices, newBandOffsets);
+    }
+
+    /**
+     * Creates a <code>DataBuffer</code> that corresponds to this
+     * <code>ComponentSampleModel</code>.
+     * The <code>DataBuffer</code> object's data type, number of banks,
+     * and size are be consistent with this <code>ComponentSampleModel</code>.
+     * @return a <code>DataBuffer</code> whose data type, number of banks
+     *         and size are consistent with this
+     *         <code>ComponentSampleModel</code>.
+     */
+    public DataBuffer createDataBuffer() {
+        DataBuffer dataBuffer = null;
+
+        int size = (int)getBufferSize();
+        switch (dataType) {
+        case DataBuffer.TYPE_BYTE:
+            dataBuffer = new DataBufferByte(size, numBanks);
+            break;
+        case DataBuffer.TYPE_USHORT:
+            dataBuffer = new DataBufferUShort(size, numBanks);
+            break;
+        case DataBuffer.TYPE_SHORT:
+            dataBuffer = new DataBufferShort(size, numBanks);
+            break;
+        case DataBuffer.TYPE_INT:
+            dataBuffer = new DataBufferInt(size, numBanks);
+            break;
+        case DataBuffer.TYPE_FLOAT:
+            dataBuffer = new DataBufferFloat(size, numBanks);
+            break;
+        case DataBuffer.TYPE_DOUBLE:
+            dataBuffer = new DataBufferDouble(size, numBanks);
+            break;
+        }
+
+        return dataBuffer;
+    }
+
+
+    /** Gets the offset for the first band of pixel (x,y).
+     *  A sample of the first band can be retrieved from a
+     * <code>DataBuffer</code>
+     *  <code>data</code> with a <code>ComponentSampleModel</code>
+     * <code>csm</code> as
+     * <pre>
+     *        data.getElem(csm.getOffset(x, y));
+     * </pre>
+     * @param x the X location of the pixel
+     * @param y the Y location of the pixel
+     * @return the offset for the first band of the specified pixel.
+     */
+    public int getOffset(int x, int y) {
+        int offset = y*scanlineStride + x*pixelStride + bandOffsets[0];
+        return offset;
+    }
+
+    /** Gets the offset for band b of pixel (x,y).
+     *  A sample of band <code>b</code> can be retrieved from a
+     *  <code>DataBuffer</code> <code>data</code>
+     *  with a <code>ComponentSampleModel</code> <code>csm</code> as
+     * <pre>
+     *       data.getElem(csm.getOffset(x, y, b));
+     * </pre>
+     * @param x the X location of the specified pixel
+     * @param y the Y location of the specified pixel
+     * @param b the specified band
+     * @return the offset for the specified band of the specified pixel.
+     */
+    public int getOffset(int x, int y, int b) {
+        int offset = y*scanlineStride + x*pixelStride + bandOffsets[b];
+        return offset;
+    }
+
+    /** Returns the number of bits per sample for all bands.
+     *  @return an array containing the number of bits per sample
+     *          for all bands, where each element in the array
+     *          represents a band.
+     */
+    public final int[] getSampleSize() {
+        int sampleSize[] = new int [numBands];
+        int sizeInBits = getSampleSize(0);
+
+        for (int i=0; i<numBands; i++)
+            sampleSize[i] = sizeInBits;
+
+        return sampleSize;
+    }
+
+    /** Returns the number of bits per sample for the specified band.
+     *  @param band the specified band
+     *  @return the number of bits per sample for the specified band.
+     */
+    public final int getSampleSize(int band) {
+        return DataBuffer.getDataTypeSize(dataType);
+    }
+
+    /** Returns the bank indices for all bands.
+     *  @return the bank indices for all bands.
+     */
+    public final int [] getBankIndices() {
+        return (int[]) bankIndices.clone();
+    }
+
+    /** Returns the band offset for all bands.
+     *  @return the band offsets for all bands.
+     */
+    public final int [] getBandOffsets() {
+        return (int[])bandOffsets.clone();
+    }
+
+    /** Returns the scanline stride of this ComponentSampleModel.
+     *  @return the scanline stride of this <code>ComponentSampleModel</code>.
+     */
+    public final int getScanlineStride() {
+        return scanlineStride;
+    }
+
+    /** Returns the pixel stride of this ComponentSampleModel.
+     *  @return the pixel stride of this <code>ComponentSampleModel</code>.
+     */
+    public final int getPixelStride() {
+        return pixelStride;
+    }
+
+    /**
+     * Returns the number of data elements needed to transfer a pixel
+     * with the
+     * {@link #getDataElements(int, int, Object, DataBuffer) } and
+     * {@link #setDataElements(int, int, Object, DataBuffer) }
+     * methods.
+     * For a <code>ComponentSampleModel</code>, this is identical to the
+     * number of bands.
+     * @return the number of data elements needed to transfer a pixel with
+     *         the <code>getDataElements</code> and
+     *         <code>setDataElements</code> methods.
+     * @see java.awt.image.SampleModel#getNumDataElements
+     * @see #getNumBands
+     */
+    public final int getNumDataElements() {
+        return getNumBands();
+    }
+
+    /**
+     * Returns data for a single pixel in a primitive array of type
+     * <code>TransferType</code>.  For a <code>ComponentSampleModel</code>,
+     * this is the same as the data type, and samples are returned
+     * one per array element.  Generally, <code>obj</code> should
+     * be passed in as <code>null</code>, so that the <code>Object</code>
+     * is created automatically and is the right primitive data type.
+     * <p>
+     * The following code illustrates transferring data for one pixel from
+     * <code>DataBuffer</code> <code>db1</code>, whose storage layout is
+     * described by <code>ComponentSampleModel</code> <code>csm1</code>,
+     * to <code>DataBuffer</code> <code>db2</code>, whose storage layout
+     * is described by <code>ComponentSampleModel</code> <code>csm2</code>.
+     * The transfer is usually more efficient than using
+     * <code>getPixel</code> and <code>setPixel</code>.
+     * <pre>
+     *       ComponentSampleModel csm1, csm2;
+     *       DataBufferInt db1, db2;
+     *       csm2.setDataElements(x, y,
+     *                            csm1.getDataElements(x, y, null, db1), db2);
+     * </pre>
+     *
+     * Using <code>getDataElements</code> and <code>setDataElements</code>
+     * to transfer between two <code>DataBuffer/SampleModel</code>
+     * pairs is legitimate if the <code>SampleModel</code> objects have
+     * the same number of bands, corresponding bands have the same number of
+     * bits per sample, and the <code>TransferType</code>s are the same.
+     * <p>
+     * If <code>obj</code> is not <code>null</code>, it should be a
+     * primitive array of type <code>TransferType</code>.
+     * Otherwise, a <code>ClassCastException</code> is thrown.  An
+     * <code>ArrayIndexOutOfBoundsException</code> might be thrown if the
+     * coordinates are not in bounds, or if <code>obj</code> is not
+     * <code>null</code> and is not large enough to hold
+     * the pixel data.
+     *
+     * @param x         the X coordinate of the pixel location
+     * @param y         the Y coordinate of the pixel location
+     * @param obj       if non-<code>null</code>, a primitive array
+     *                  in which to return the pixel data
+     * @param data      the <code>DataBuffer</code> containing the image data
+     * @return the data of the specified pixel
+     * @see #setDataElements(int, int, Object, DataBuffer)
+     *
+     * @throws NullPointerException if data is null.
+     * @throws ArrayIndexOutOfBoundsException if the coordinates are
+     * not in bounds, or if obj is too small to hold the ouput.
+     */
+    public Object getDataElements(int x, int y, Object obj, DataBuffer data) {
+        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("Coordinate out of bounds!");
+        }
+
+        int type = getTransferType();
+        int numDataElems = getNumDataElements();
+        int pixelOffset = y*scanlineStride + x*pixelStride;
+
+        switch(type) {
+
+        case DataBuffer.TYPE_BYTE:
+
+            byte[] bdata;
+
+            if (obj == null)
+                bdata = new byte[numDataElems];
+            else
+                bdata = (byte[])obj;
+
+            for (int i=0; i<numDataElems; i++) {
+                bdata[i] = (byte)data.getElem(bankIndices[i],
+                                              pixelOffset + bandOffsets[i]);
+            }
+
+            obj = (Object)bdata;
+            break;
+
+        case DataBuffer.TYPE_USHORT:
+        case DataBuffer.TYPE_SHORT:
+
+            short[] sdata;
+
+            if (obj == null)
+                sdata = new short[numDataElems];
+            else
+                sdata = (short[])obj;
+
+            for (int i=0; i<numDataElems; i++) {
+                sdata[i] = (short)data.getElem(bankIndices[i],
+                                               pixelOffset + bandOffsets[i]);
+            }
+
+            obj = (Object)sdata;
+            break;
+
+        case DataBuffer.TYPE_INT:
+
+            int[] idata;
+
+            if (obj == null)
+                idata = new int[numDataElems];
+            else
+                idata = (int[])obj;
+
+            for (int i=0; i<numDataElems; i++) {
+                idata[i] = data.getElem(bankIndices[i],
+                                        pixelOffset + bandOffsets[i]);
+            }
+
+            obj = (Object)idata;
+            break;
+
+        case DataBuffer.TYPE_FLOAT:
+
+            float[] fdata;
+
+            if (obj == null)
+                fdata = new float[numDataElems];
+            else
+                fdata = (float[])obj;
+
+            for (int i=0; i<numDataElems; i++) {
+                fdata[i] = data.getElemFloat(bankIndices[i],
+                                             pixelOffset + bandOffsets[i]);
+            }
+
+            obj = (Object)fdata;
+            break;
+
+        case DataBuffer.TYPE_DOUBLE:
+
+            double[] ddata;
+
+            if (obj == null)
+                ddata = new double[numDataElems];
+            else
+                ddata = (double[])obj;
+
+            for (int i=0; i<numDataElems; i++) {
+                ddata[i] = data.getElemDouble(bankIndices[i],
+                                              pixelOffset + bandOffsets[i]);
+            }
+
+            obj = (Object)ddata;
+            break;
+        }
+
+        return obj;
+    }
+
+    /**
+     * Returns all samples for the specified pixel in an int array,
+     * one sample per array element.
+     * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if
+     * the coordinates are not in bounds.
+     * @param x         the X coordinate of the pixel location
+     * @param y         the Y coordinate of the pixel location
+     * @param iArray    If non-null, returns the samples in this array
+     * @param data      The DataBuffer containing the image data
+     * @return the samples of the specified pixel.
+     * @see #setPixel(int, int, int[], DataBuffer)
+     *
+     * @throws NullPointerException if data is null.
+     * @throws ArrayIndexOutOfBoundsException if the coordinates are
+     * not in bounds, or if iArray is too small to hold the output.
+     */
+    public int[] getPixel(int x, int y, int iArray[], DataBuffer data) {
+        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("Coordinate out of bounds!");
+        }
+        int pixels[];
+        if (iArray != null) {
+           pixels = iArray;
+        } else {
+           pixels = new int [numBands];
+        }
+        int pixelOffset = y*scanlineStride + x*pixelStride;
+        for (int i=0; i<numBands; i++) {
+            pixels[i] = data.getElem(bankIndices[i],
+                                     pixelOffset + bandOffsets[i]);
+        }
+        return pixels;
+    }
+
+    /**
+     * Returns all samples for the specified rectangle of pixels in
+     * an int array, one sample per array element.
+     * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if
+     * the coordinates are not in bounds.
+     * @param x         The X coordinate of the upper left pixel location
+     * @param y         The Y coordinate of the upper left pixel location
+     * @param w         The width of the pixel rectangle
+     * @param h         The height of the pixel rectangle
+     * @param iArray    If non-null, returns the samples in this array
+     * @param data      The DataBuffer containing the image data
+     * @return the samples of the pixels within the specified region.
+     * @see #setPixels(int, int, int, int, int[], DataBuffer)
+     */
+    public int[] getPixels(int x, int y, int w, int h,
+                           int iArray[], DataBuffer data) {
+        if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("Coordinate out of bounds!");
+        }
+        int pixels[];
+        if (iArray != null) {
+           pixels = iArray;
+        } else {
+           pixels = new int [w*h*numBands];
+        }
+        int lineOffset = y*scanlineStride + x*pixelStride;
+        int srcOffset = 0;
+
+        for (int i = 0; i < h; i++) {
+           int pixelOffset = lineOffset;
+           for (int j = 0; j < w; j++) {
+              for (int k=0; k < numBands; k++) {
+                 pixels[srcOffset++] =
+                    data.getElem(bankIndices[k], pixelOffset + bandOffsets[k]);
+              }
+              pixelOffset += pixelStride;
+           }
+           lineOffset += scanlineStride;
+        }
+        return pixels;
+    }
+
+    /**
+     * Returns as int the sample in a specified band for the pixel
+     * located at (x,y).
+     * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if
+     * the coordinates are not in bounds.
+     * @param x         the X coordinate of the pixel location
+     * @param y         the Y coordinate of the pixel location
+     * @param b         the band to return
+     * @param data      the <code>DataBuffer</code> containing the image data
+     * @return the sample in a specified band for the specified pixel
+     * @see #setSample(int, int, int, int, DataBuffer)
+     */
+    public int getSample(int x, int y, int b, DataBuffer data) {
+        // Bounds check for 'b' will be performed automatically
+        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("Coordinate out of bounds!");
+        }
+        int sample = data.getElem(bankIndices[b],
+                                  y*scanlineStride + x*pixelStride +
+                                  bandOffsets[b]);
+        return sample;
+    }
+
+    /**
+     * Returns the sample in a specified band
+     * for the pixel located at (x,y) as a float.
+     * An <code>ArrayIndexOutOfBoundsException</code> might be
+     * thrown if the coordinates are not in bounds.
+     * @param x         The X coordinate of the pixel location
+     * @param y         The Y coordinate of the pixel location
+     * @param b         The band to return
+     * @param data      The DataBuffer containing the image data
+     * @return a float value representing the sample in the specified
+     * band for the specified pixel.
+     */
+    public float getSampleFloat(int x, int y, int b, DataBuffer data) {
+        // Bounds check for 'b' will be performed automatically
+        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("Coordinate out of bounds!");
+        }
+
+        float sample = data.getElemFloat(bankIndices[b],
+                                         y*scanlineStride + x*pixelStride +
+                                         bandOffsets[b]);
+        return sample;
+    }
+
+    /**
+     * Returns the sample in a specified band
+     * for a pixel located at (x,y) as a double.
+     * An <code>ArrayIndexOutOfBoundsException</code> might be
+     * thrown if the coordinates are not in bounds.
+     * @param x         The X coordinate of the pixel location
+     * @param y         The Y coordinate of the pixel location
+     * @param b         The band to return
+     * @param data      The DataBuffer containing the image data
+     * @return a double value representing the sample in the specified
+     * band for the specified pixel.
+     */
+    public double getSampleDouble(int x, int y, int b, DataBuffer data) {
+        // Bounds check for 'b' will be performed automatically
+        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("Coordinate out of bounds!");
+        }
+
+        double sample = data.getElemDouble(bankIndices[b],
+                                           y*scanlineStride + x*pixelStride +
+                                           bandOffsets[b]);
+        return sample;
+    }
+
+    /**
+     * Returns the samples in a specified band for the specified rectangle
+     * of pixels in an int array, one sample per data array element.
+     * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if
+     * the coordinates are not in bounds.
+     * @param x         The X coordinate of the upper left pixel location
+     * @param y         The Y coordinate of the upper left pixel location
+     * @param w         the width of the pixel rectangle
+     * @param h         the height of the pixel rectangle
+     * @param b         the band to return
+     * @param iArray    if non-<code>null</code>, returns the samples
+     *                  in this array
+     * @param data      the <code>DataBuffer</code> containing the image data
+     * @return the samples in the specified band of the specified pixel
+     * @see #setSamples(int, int, int, int, int, int[], DataBuffer)
+     */
+    public int[] getSamples(int x, int y, int w, int h, int b,
+                            int iArray[], DataBuffer data) {
+        // Bounds check for 'b' will be performed automatically
+        if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("Coordinate out of bounds!");
+        }
+        int samples[];
+        if (iArray != null) {
+           samples = iArray;
+        } else {
+           samples = new int [w*h];
+        }
+        int lineOffset = y*scanlineStride + x*pixelStride +  bandOffsets[b];
+        int srcOffset = 0;
+
+        for (int i = 0; i < h; i++) {
+           int sampleOffset = lineOffset;
+           for (int j = 0; j < w; j++) {
+              samples[srcOffset++] = data.getElem(bankIndices[b],
+                                                  sampleOffset);
+              sampleOffset += pixelStride;
+           }
+           lineOffset += scanlineStride;
+        }
+        return samples;
+    }
+
+    /**
+     * Sets the data for a single pixel in the specified
+     * <code>DataBuffer</code> from a primitive array of type
+     * <code>TransferType</code>.  For a <code>ComponentSampleModel</code>,
+     * this is the same as the data type, and samples are transferred
+     * one per array element.
+     * <p>
+     * The following code illustrates transferring data for one pixel from
+     * <code>DataBuffer</code> <code>db1</code>, whose storage layout is
+     * described by <code>ComponentSampleModel</code> <code>csm1</code>,
+     * to <code>DataBuffer</code> <code>db2</code>, whose storage layout
+     * is described by <code>ComponentSampleModel</code> <code>csm2</code>.
+     * The transfer is usually more efficient than using
+     * <code>getPixel</code> and <code>setPixel</code>.
+     * <pre>
+     *       ComponentSampleModel csm1, csm2;
+     *       DataBufferInt db1, db2;
+     *       csm2.setDataElements(x, y, csm1.getDataElements(x, y, null, db1),
+     *                            db2);
+     * </pre>
+     * Using <code>getDataElements</code> and <code>setDataElements</code>
+     * to transfer between two <code>DataBuffer/SampleModel</code> pairs
+     * is legitimate if the <code>SampleModel</code> objects have
+     * the same number of bands, corresponding bands have the same number of
+     * bits per sample, and the <code>TransferType</code>s are the same.
+     * <p>
+     * A <code>ClassCastException</code> is thrown if <code>obj</code> is not
+     * a primitive array of type <code>TransferType</code>.
+     * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if
+     * the coordinates are not in bounds, or if <code>obj</code> is not large
+     * enough to hold the pixel data.
+     * @param x         the X coordinate of the pixel location
+     * @param y         the Y coordinate of the pixel location
+     * @param obj       a primitive array containing pixel data
+     * @param data      the DataBuffer containing the image data
+     * @see #getDataElements(int, int, Object, DataBuffer)
+     */
+    public void setDataElements(int x, int y, Object obj, DataBuffer data) {
+        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("Coordinate out of bounds!");
+        }
+
+        int type = getTransferType();
+        int numDataElems = getNumDataElements();
+        int pixelOffset = y*scanlineStride + x*pixelStride;
+
+        switch(type) {
+
+        case DataBuffer.TYPE_BYTE:
+
+            byte[] barray = (byte[])obj;
+
+            for (int i=0; i<numDataElems; i++) {
+                data.setElem(bankIndices[i], pixelOffset + bandOffsets[i],
+                           ((int)barray[i])&0xff);
+            }
+            break;
+
+        case DataBuffer.TYPE_USHORT:
+        case DataBuffer.TYPE_SHORT:
+
+            short[] sarray = (short[])obj;
+
+            for (int i=0; i<numDataElems; i++) {
+                data.setElem(bankIndices[i], pixelOffset + bandOffsets[i],
+                           ((int)sarray[i])&0xffff);
+            }
+            break;
+
+        case DataBuffer.TYPE_INT:
+
+            int[] iarray = (int[])obj;
+
+            for (int i=0; i<numDataElems; i++) {
+                data.setElem(bankIndices[i],
+                             pixelOffset + bandOffsets[i], iarray[i]);
+            }
+            break;
+
+        case DataBuffer.TYPE_FLOAT:
+
+            float[] farray = (float[])obj;
+
+            for (int i=0; i<numDataElems; i++) {
+                data.setElemFloat(bankIndices[i],
+                             pixelOffset + bandOffsets[i], farray[i]);
+            }
+            break;
+
+        case DataBuffer.TYPE_DOUBLE:
+
+            double[] darray = (double[])obj;
+
+            for (int i=0; i<numDataElems; i++) {
+                data.setElemDouble(bankIndices[i],
+                             pixelOffset + bandOffsets[i], darray[i]);
+            }
+            break;
+
+        }
+    }
+
+    /**
+     * Sets a pixel in the <code>DataBuffer</code> using an int array of
+     * samples for input.  An <code>ArrayIndexOutOfBoundsException</code>
+     * might be thrown if the coordinates are
+     * not in bounds.
+     * @param x         The X coordinate of the pixel location
+     * @param y         The Y coordinate of the pixel location
+     * @param iArray    The input samples in an int array
+     * @param data      The DataBuffer containing the image data
+     * @see #getPixel(int, int, int[], DataBuffer)
+     */
+    public void setPixel(int x, int y, int iArray[], DataBuffer data) {
+        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("Coordinate out of bounds!");
+        }
+       int pixelOffset = y*scanlineStride + x*pixelStride;
+       for (int i=0; i<numBands; i++) {
+           data.setElem(bankIndices[i],
+                        pixelOffset + bandOffsets[i],iArray[i]);
+       }
+    }
+
+    /**
+     * Sets all samples for a rectangle of pixels from an int array containing
+     * one sample per array element.
+     * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if the
+     * coordinates are not in bounds.
+     * @param x         The X coordinate of the upper left pixel location
+     * @param y         The Y coordinate of the upper left pixel location
+     * @param w         The width of the pixel rectangle
+     * @param h         The height of the pixel rectangle
+     * @param iArray    The input samples in an int array
+     * @param data      The DataBuffer containing the image data
+     * @see #getPixels(int, int, int, int, int[], DataBuffer)
+     */
+    public void setPixels(int x, int y, int w, int h,
+                          int iArray[], DataBuffer data) {
+        if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("Coordinate out of bounds!");
+        }
+
+        int lineOffset = y*scanlineStride + x*pixelStride;
+        int srcOffset = 0;
+
+        for (int i = 0; i < h; i++) {
+           int pixelOffset = lineOffset;
+           for (int j = 0; j < w; j++) {
+              for (int k=0; k < numBands; k++) {
+                 data.setElem(bankIndices[k], pixelOffset + bandOffsets[k],
+                              iArray[srcOffset++]);
+              }
+              pixelOffset += pixelStride;
+           }
+           lineOffset += scanlineStride;
+        }
+    }
+
+    /**
+     * Sets a sample in the specified band for the pixel located at (x,y)
+     * in the <code>DataBuffer</code> using an int for input.
+     * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if the
+     * coordinates are not in bounds.
+     * @param x         The X coordinate of the pixel location
+     * @param y         The Y coordinate of the pixel location
+     * @param b         the band to set
+     * @param s         the input sample as an int
+     * @param data      the DataBuffer containing the image data
+     * @see #getSample(int, int, int, DataBuffer)
+     */
+    public void setSample(int x, int y, int b, int s,
+                          DataBuffer data) {
+        // Bounds check for 'b' will be performed automatically
+        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("Coordinate out of bounds!");
+        }
+        data.setElem(bankIndices[b],
+                     y*scanlineStride + x*pixelStride + bandOffsets[b], s);
+    }
+
+    /**
+     * Sets a sample in the specified band for the pixel located at (x,y)
+     * in the <code>DataBuffer</code> using a float for input.
+     * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if
+     * the coordinates are not in bounds.
+     * @param x         The X coordinate of the pixel location
+     * @param y         The Y coordinate of the pixel location
+     * @param b         The band to set
+     * @param s         The input sample as a float
+     * @param data      The DataBuffer containing the image data
+     * @see #getSample(int, int, int, DataBuffer)
+     */
+    public void setSample(int x, int y, int b,
+                          float s ,
+                          DataBuffer data) {
+        // Bounds check for 'b' will be performed automatically
+        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("Coordinate out of bounds!");
+        }
+        data.setElemFloat(bankIndices[b],
+                          y*scanlineStride + x*pixelStride + bandOffsets[b],
+                          s);
+    }
+
+    /**
+     * Sets a sample in the specified band for the pixel located at (x,y)
+     * in the <code>DataBuffer</code> using a double for input.
+     * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if
+     * the coordinates are not in bounds.
+     * @param x         The X coordinate of the pixel location
+     * @param y         The Y coordinate of the pixel location
+     * @param b         The band to set
+     * @param s         The input sample as a double
+     * @param data      The DataBuffer containing the image data
+     * @see #getSample(int, int, int, DataBuffer)
+     */
+    public void setSample(int x, int y, int b,
+                          double s,
+                          DataBuffer data) {
+        // Bounds check for 'b' will be performed automatically
+        if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("Coordinate out of bounds!");
+        }
+        data.setElemDouble(bankIndices[b],
+                          y*scanlineStride + x*pixelStride + bandOffsets[b],
+                          s);
+    }
+
+    /**
+     * Sets the samples in the specified band for the specified rectangle
+     * of pixels from an int array containing one sample per data array element.
+     * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if the
+     * coordinates are not in bounds.
+     * @param x         The X coordinate of the upper left pixel location
+     * @param y         The Y coordinate of the upper left pixel location
+     * @param w         The width of the pixel rectangle
+     * @param h         The height of the pixel rectangle
+     * @param b         The band to set
+     * @param iArray    The input samples in an int array
+     * @param data      The DataBuffer containing the image data
+     * @see #getSamples(int, int, int, int, int, int[], DataBuffer)
+     */
+    public void setSamples(int x, int y, int w, int h, int b,
+                           int iArray[], DataBuffer data) {
+        // Bounds check for 'b' will be performed automatically
+        if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) {
+            throw new ArrayIndexOutOfBoundsException
+                ("Coordinate out of bounds!");
+        }
+        int lineOffset = y*scanlineStride + x*pixelStride + bandOffsets[b];
+        int srcOffset = 0;
+
+        for (int i = 0; i < h; i++) {
+           int sampleOffset = lineOffset;
+           for (int j = 0; j < w; j++) {
+              data.setElem(bankIndices[b], sampleOffset, iArray[srcOffset++]);
+              sampleOffset += pixelStride;
+           }
+           lineOffset += scanlineStride;
+        }
+    }
+
+    public boolean equals(Object o) {
+        if ((o == null) || !(o instanceof ComponentSampleModel)) {
+            return false;
+        }
+
+        ComponentSampleModel that = (ComponentSampleModel)o;
+        return this.width == that.width &&
+            this.height == that.height &&
+            this.numBands == that.numBands &&
+            this.dataType == that.dataType &&
+            Arrays.equals(this.bandOffsets, that.bandOffsets) &&
+            Arrays.equals(this.bankIndices, that.bankIndices) &&
+            this.numBands == that.numBands &&
+            this.numBanks == that.numBanks &&
+            this.scanlineStride == that.scanlineStride &&
+            this.pixelStride == that.pixelStride;
+    }
+
+    // If we implement equals() we must also implement hashCode
+    public int hashCode() {
+        int hash = 0;
+        hash = width;
+        hash <<= 8;
+        hash ^= height;
+        hash <<= 8;
+        hash ^= numBands;
+        hash <<= 8;
+        hash ^= dataType;
+        hash <<= 8;
+        for (int i = 0; i < bandOffsets.length; i++) {
+            hash ^= bandOffsets[i];
+            hash <<= 8;
+        }
+        for (int i = 0; i < bankIndices.length; i++) {
+            hash ^= bankIndices[i];
+            hash <<= 8;
+        }
+        hash ^= numBands;
+        hash <<= 8;
+        hash ^= numBanks;
+        hash <<= 8;
+        hash ^= scanlineStride;
+        hash <<= 8;
+        hash ^= pixelStride;
+        return hash;
+    }
+}