/*
* 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;
/**
* The MultiPixelPackedSampleModel
class represents
* one-banded images and can pack multiple one-sample
* pixels into one data element. Pixels are not allowed to span data elements.
* The data type can be DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT,
* or DataBuffer.TYPE_INT. Each pixel must be a power of 2 number of bits
* and a power of 2 number of pixels must fit exactly in one data element.
* Pixel bit stride is equal to the number of bits per pixel. Scanline
* stride is in data elements and the last several data elements might be
* padded with unused pixels. Data bit offset is the offset in bits from
* the beginning of the {@link DataBuffer} to the first pixel and must be
* a multiple of pixel bit stride.
*
* The following code illustrates extracting the bits for pixel
* x, y
from DataBuffer
data
* and storing the pixel data in data elements of type
* dataType
:
*
* int dataElementSize = DataBuffer.getDataTypeSize(dataType); * int bitnum = dataBitOffset + x*pixelBitStride; * int element = data.getElem(y*scanlineStride + bitnum/dataElementSize); * int shift = dataElementSize - (bitnum & (dataElementSize-1)) * - pixelBitStride; * int pixel = (element >> shift) & ((1 << pixelBitStride) - 1); **/ public class MultiPixelPackedSampleModel extends SampleModel { /** The number of bits from one pixel to the next. */ int pixelBitStride; /** Bitmask that extracts the rightmost pixel of a data element. */ int bitMask; /** * The number of pixels that fit in a data element. Also used * as the number of bits per pixel. */ int pixelsPerDataElement; /** The size of a data element in bits. */ int dataElementSize; /** The bit offset into the data array where the first pixel begins. */ int dataBitOffset; /** ScanlineStride of the data buffer described in data array elements. */ int scanlineStride; /** * Constructs a
MultiPixelPackedSampleModel
with the
* specified data type, width, height and number of bits per pixel.
* @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 numberOfBits the number of bits per pixel
* @throws IllegalArgumentException if dataType
is not
* either DataBuffer.TYPE_BYTE
,
* DataBuffer.TYPE_USHORT
, or
* DataBuffer.TYPE_INT
*/
public MultiPixelPackedSampleModel(int dataType,
int w,
int h,
int numberOfBits) {
this(dataType,w,h,
numberOfBits,
(w*numberOfBits+DataBuffer.getDataTypeSize(dataType)-1)/
DataBuffer.getDataTypeSize(dataType),
0);
if (dataType != DataBuffer.TYPE_BYTE &&
dataType != DataBuffer.TYPE_USHORT &&
dataType != DataBuffer.TYPE_INT) {
throw new IllegalArgumentException("Unsupported data type "+
dataType);
}
}
/**
* Constructs a MultiPixelPackedSampleModel
with
* specified data type, width, height, number of bits per pixel,
* scanline stride and data bit offset.
* @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 numberOfBits the number of bits per pixel
* @param scanlineStride the line stride of the image data
* @param dataBitOffset the data bit offset for the region of image
* data described
* @exception RasterFormatException if the number of bits per pixel
* is not a power of 2 or if a power of 2 number of
* pixels do not fit in one data element.
* @throws IllegalArgumentException if w
or
* h
is not greater than 0
* @throws IllegalArgumentException if dataType
is not
* either DataBuffer.TYPE_BYTE
,
* DataBuffer.TYPE_USHORT
, or
* DataBuffer.TYPE_INT
*/
public MultiPixelPackedSampleModel(int dataType, int w, int h,
int numberOfBits,
int scanlineStride,
int dataBitOffset) {
super(dataType, w, h, 1);
if (dataType != DataBuffer.TYPE_BYTE &&
dataType != DataBuffer.TYPE_USHORT &&
dataType != DataBuffer.TYPE_INT) {
throw new IllegalArgumentException("Unsupported data type "+
dataType);
}
this.dataType = dataType;
this.pixelBitStride = numberOfBits;
this.scanlineStride = scanlineStride;
this.dataBitOffset = dataBitOffset;
this.dataElementSize = DataBuffer.getDataTypeSize(dataType);
this.pixelsPerDataElement = dataElementSize/numberOfBits;
if (pixelsPerDataElement*numberOfBits != dataElementSize) {
throw new RasterFormatException("MultiPixelPackedSampleModel " +
"does not allow pixels to " +
"span data element boundaries");
}
this.bitMask = (1 << numberOfBits) - 1;
}
/**
* Creates a new MultiPixelPackedSampleModel
with the
* specified width and height. The new
* MultiPixelPackedSampleModel
has the
* same storage data type and number of bits per pixel as this
* MultiPixelPackedSampleModel
.
* @param w the specified width
* @param h the specified height
* @return a {@link SampleModel} with the specified width and height
* and with the same storage data type and number of bits per pixel
* as this MultiPixelPackedSampleModel
.
* @throws IllegalArgumentException if w
or
* h
is not greater than 0
*/
public SampleModel createCompatibleSampleModel(int w, int h) {
SampleModel sampleModel =
new MultiPixelPackedSampleModel(dataType, w, h, pixelBitStride);
return sampleModel;
}
/**
* Creates a DataBuffer
that corresponds to this
* MultiPixelPackedSampleModel
. The
* DataBuffer
object's data type and size
* is consistent with this MultiPixelPackedSampleModel
.
* The DataBuffer
has a single bank.
* @return a DataBuffer
with the same data type and
* size as this MultiPixelPackedSampleModel
.
*/
public DataBuffer createDataBuffer() {
DataBuffer dataBuffer = null;
int size = (int)scanlineStride*height;
switch (dataType) {
case DataBuffer.TYPE_BYTE:
dataBuffer = new DataBufferByte(size+(dataBitOffset+7)/8);
break;
case DataBuffer.TYPE_USHORT:
dataBuffer = new DataBufferUShort(size+(dataBitOffset+15)/16);
break;
case DataBuffer.TYPE_INT:
dataBuffer = new DataBufferInt(size+(dataBitOffset+31)/32);
break;
}
return dataBuffer;
}
/**
* Returns the number of data elements needed to transfer one pixel
* via the {@link #getDataElements} and {@link #setDataElements}
* methods. For a MultiPixelPackedSampleModel
, this is
* one.
* @return the number of data elements.
*/
public int getNumDataElements() {
return 1;
}
/**
* Returns the number of bits per sample for all bands.
* @return the number of bits per sample.
*/
public int[] getSampleSize() {
int sampleSize[] = {pixelBitStride};
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 int getSampleSize(int band) {
return pixelBitStride;
}
/**
* Returns the offset of pixel (x, y) in data array elements.
* @param x the X coordinate of the specified pixel
* @param y the Y coordinate of the specified pixel
* @return the offset of the specified pixel.
*/
public int getOffset(int x, int y) {
int offset = y * scanlineStride;
offset += (x*pixelBitStride+dataBitOffset)/dataElementSize;
return offset;
}
/**
* Returns the offset, in bits, into the data element in which it is
* stored for the x
th pixel of a scanline.
* This offset is the same for all scanlines.
* @param x the specified pixel
* @return the bit offset of the specified pixel.
*/
public int getBitOffset(int x){
return (x*pixelBitStride+dataBitOffset)%dataElementSize;
}
/**
* Returns the scanline stride.
* @return the scanline stride of this
* MultiPixelPackedSampleModel
.
*/
public int getScanlineStride() {
return scanlineStride;
}
/**
* Returns the pixel bit stride in bits. This value is the same as
* the number of bits per pixel.
* @return the pixelBitStride
of this
* MultiPixelPackedSampleModel
.
*/
public int getPixelBitStride() {
return pixelBitStride;
}
/**
* Returns the data bit offset in bits.
* @return the dataBitOffset
of this
* MultiPixelPackedSampleModel
.
*/
public int getDataBitOffset() {
return dataBitOffset;
}
/**
* Returns the TransferType used to transfer pixels by way of the
* getDataElements
and setDataElements
* methods. The TransferType might or might not be the same as the
* storage DataType. The TransferType is one of
* DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT,
* or DataBuffer.TYPE_INT.
* @return the transfertype.
*/
public int getTransferType() {
if (pixelBitStride > 16)
return DataBuffer.TYPE_INT;
else if (pixelBitStride > 8)
return DataBuffer.TYPE_USHORT;
else
return DataBuffer.TYPE_BYTE;
}
/**
* Creates a new MultiPixelPackedSampleModel
with a
* subset of the bands of this
* MultiPixelPackedSampleModel
. Since a
* MultiPixelPackedSampleModel
only has one band, the
* bands argument must have a length of one and indicate the zeroth
* band.
* @param bands the specified bands
* @return a new SampleModel
with a subset of bands of
* this MultiPixelPackedSampleModel
.
* @exception RasterFormatException if the number of bands requested
* is not one.
* @throws IllegalArgumentException if w
or
* h
is not greater than 0
*/
public SampleModel createSubsetSampleModel(int bands[]) {
if (bands != null) {
if (bands.length != 1)
throw new RasterFormatException("MultiPixelPackedSampleModel has "
+ "only one band.");
}
SampleModel sm = createCompatibleSampleModel(width, height);
return sm;
}
/**
* Returns as int
the sample in a specified band for the
* pixel located at (x, y). An
* ArrayIndexOutOfBoundsException
is thrown if the
* coordinates are not in bounds.
* @param x the X coordinate of the specified pixel
* @param y the Y coordinate of the specified pixel
* @param b the band to return, which is assumed to be 0
* @param data the DataBuffer
containing the image
* data
* @return the specified band containing the sample of the specified
* pixel.
* @exception ArrayIndexOutOfBoundException if the specified
* coordinates are not in bounds.
* @see #setSample(int, int, int, int, DataBuffer)
*/
public int getSample(int x, int y, int b, DataBuffer data) {
// 'b' must be 0
if ((x < 0) || (y < 0) || (x >= width) || (y >= height) ||
(b != 0)) {
throw new ArrayIndexOutOfBoundsException
("Coordinate out of bounds!");
}
int bitnum = dataBitOffset + x*pixelBitStride;
int element = data.getElem(y*scanlineStride + bitnum/dataElementSize);
int shift = dataElementSize - (bitnum & (dataElementSize-1))
- pixelBitStride;
return (element >> shift) & bitMask;
}
/**
* Sets a sample in the specified band for the pixel located at
* (x, y) in the DataBuffer
using an
* int
for input.
* An ArrayIndexOutOfBoundsException
is thrown if the
* coordinates are not in bounds.
* @param x the X coordinate of the specified pixel
* @param y the Y coordinate of the specified pixel
* @param b the band to return, which is assumed to be 0
* @param s the input sample as an int
* @param data the DataBuffer
where image data is stored
* @exception ArrayIndexOutOfBoundsException if the coordinates are
* not in bounds.
* @see #getSample(int, int, int, DataBuffer)
*/
public void setSample(int x, int y, int b, int s,
DataBuffer data) {
// 'b' must be 0
if ((x < 0) || (y < 0) || (x >= width) || (y >= height) ||
(b != 0)) {
throw new ArrayIndexOutOfBoundsException
("Coordinate out of bounds!");
}
int bitnum = dataBitOffset + x * pixelBitStride;
int index = y * scanlineStride + (bitnum / dataElementSize);
int shift = dataElementSize - (bitnum & (dataElementSize-1))
- pixelBitStride;
int element = data.getElem(index);
element &= ~(bitMask << shift);
element |= (s & bitMask) << shift;
data.setElem(index,element);
}
/**
* Returns data for a single pixel in a primitive array of type
* TransferType. For a MultiPixelPackedSampleModel
,
* the array has one element, and the type is the smallest of
* DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, or DataBuffer.TYPE_INT
* that can hold a single pixel. Generally, obj
* should be passed in as null
, so that the
* Object
is created automatically and is the
* correct primitive data type.
*
* The following code illustrates transferring data for one pixel from
* DataBuffer
db1
, whose storage layout is
* described by MultiPixelPackedSampleModel
* mppsm1
, to DataBuffer
db2
,
* whose storage layout is described by
* MultiPixelPackedSampleModel
mppsm2
.
* The transfer is generally more efficient than using
* getPixel
or setPixel
.
*
* MultiPixelPackedSampleModel mppsm1, mppsm2; * DataBufferInt db1, db2; * mppsm2.setDataElements(x, y, mppsm1.getDataElements(x, y, null, * db1), db2); ** Using
getDataElements
or setDataElements
* to transfer between two DataBuffer/SampleModel
pairs
* is legitimate if the SampleModels
have the same number
* of bands, corresponding bands have the same number of
* bits per sample, and the TransferTypes are the same.
*
* If obj
is not null
, it should be a
* primitive array of type TransferType. Otherwise, a
* ClassCastException
is thrown. An
* ArrayIndexOutOfBoundsException
is thrown if the
* coordinates are not in bounds, or if obj
is not
* null
and is not large enough to hold the pixel data.
* @param x the X coordinate of the specified pixel
* @param y the Y coordinate of the specified pixel
* @param obj a primitive array in which to return the pixel data or
* null
.
* @param data the DataBuffer
containing the image data.
* @return an Object
containing data for the specified
* pixel.
* @exception ClassCastException if obj
is not a
* primitive array of type TransferType or is not null
* @exception ArrayIndexOutOfBoundsException if the coordinates are
* not in bounds, or if obj
is not null
or
* not large enough to hold the pixel data
* @see #setDataElements(int, int, Object, DataBuffer)
*/
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 bitnum = dataBitOffset + x*pixelBitStride;
int shift = dataElementSize - (bitnum & (dataElementSize-1))
- pixelBitStride;
int element = 0;
switch(type) {
case DataBuffer.TYPE_BYTE:
byte[] bdata;
if (obj == null)
bdata = new byte[1];
else
bdata = (byte[])obj;
element = data.getElem(y*scanlineStride +
bitnum/dataElementSize);
bdata[0] = (byte)((element >> shift) & bitMask);
obj = (Object)bdata;
break;
case DataBuffer.TYPE_USHORT:
short[] sdata;
if (obj == null)
sdata = new short[1];
else
sdata = (short[])obj;
element = data.getElem(y*scanlineStride +
bitnum/dataElementSize);
sdata[0] = (short)((element >> shift) & bitMask);
obj = (Object)sdata;
break;
case DataBuffer.TYPE_INT:
int[] idata;
if (obj == null)
idata = new int[1];
else
idata = (int[])obj;
element = data.getElem(y*scanlineStride +
bitnum/dataElementSize);
idata[0] = (element >> shift) & bitMask;
obj = (Object)idata;
break;
}
return obj;
}
/**
* Returns the specified single band pixel in the first element
* of an int
array.
* ArrayIndexOutOfBoundsException
is thrown if the
* coordinates are not in bounds.
* @param x the X coordinate of the specified pixel
* @param y the Y coordinate of the specified pixel
* @param iArray the array containing the pixel to be returned or
* null
* @param data the DataBuffer
where image data is stored
* @return an array containing the specified pixel.
* @exception ArrayIndexOutOfBoundsException if the coordinates
* are not in bounds
* @see #setPixel(int, int, int[], DataBuffer)
*/
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 bitnum = dataBitOffset + x*pixelBitStride;
int element = data.getElem(y*scanlineStride + bitnum/dataElementSize);
int shift = dataElementSize - (bitnum & (dataElementSize-1))
- pixelBitStride;
pixels[0] = (element >> shift) & bitMask;
return pixels;
}
/**
* Sets the data for a single pixel in the specified
* DataBuffer
from a primitive array of type
* TransferType. For a MultiPixelPackedSampleModel
,
* only the first element of the array holds valid data,
* and the type must be the smallest of
* DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, or DataBuffer.TYPE_INT
* that can hold a single pixel.
*
* The following code illustrates transferring data for one pixel from
* DataBuffer
db1
, whose storage layout is
* described by MultiPixelPackedSampleModel
* mppsm1
, to DataBuffer
db2
,
* whose storage layout is described by
* MultiPixelPackedSampleModel
mppsm2
.
* The transfer is generally more efficient than using
* getPixel
or setPixel
.
*
* MultiPixelPackedSampleModel mppsm1, mppsm2; * DataBufferInt db1, db2; * mppsm2.setDataElements(x, y, mppsm1.getDataElements(x, y, null, * db1), db2); ** Using
getDataElements
or setDataElements
to
* transfer between two DataBuffer/SampleModel
pairs is
* legitimate if the SampleModel
objects have
* the same number of bands, corresponding bands have the same number of
* bits per sample, and the TransferTypes are the same.
*
* obj
must be a primitive array of type TransferType.
* Otherwise, a ClassCastException
is thrown. An
* ArrayIndexOutOfBoundsException
is thrown if the
* coordinates are not in bounds, or if obj
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 bitnum = dataBitOffset + x * pixelBitStride;
int index = y * scanlineStride + (bitnum / dataElementSize);
int shift = dataElementSize - (bitnum & (dataElementSize-1))
- pixelBitStride;
int element = data.getElem(index);
element &= ~(bitMask << shift);
switch(type) {
case DataBuffer.TYPE_BYTE:
byte[] barray = (byte[])obj;
element |= ( ((int)(barray[0])&0xff) & bitMask) << shift;
data.setElem(index, element);
break;
case DataBuffer.TYPE_USHORT:
short[] sarray = (short[])obj;
element |= ( ((int)(sarray[0])&0xffff) & bitMask) << shift;
data.setElem(index, element);
break;
case DataBuffer.TYPE_INT:
int[] iarray = (int[])obj;
element |= (iarray[0] & bitMask) << shift;
data.setElem(index, element);
break;
}
}
/**
* Sets a pixel in the DataBuffer
using an
* int
array for input.
* ArrayIndexOutOfBoundsException
is 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 pixel 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 bitnum = dataBitOffset + x * pixelBitStride;
int index = y * scanlineStride + (bitnum / dataElementSize);
int shift = dataElementSize - (bitnum & (dataElementSize-1))
- pixelBitStride;
int element = data.getElem(index);
element &= ~(bitMask << shift);
element |= (iArray[0] & bitMask) << shift;
data.setElem(index,element);
}
public boolean equals(Object o) {
if ((o == null) || !(o instanceof MultiPixelPackedSampleModel)) {
return false;
}
MultiPixelPackedSampleModel that = (MultiPixelPackedSampleModel)o;
return this.width == that.width &&
this.height == that.height &&
this.numBands == that.numBands &&
this.dataType == that.dataType &&
this.pixelBitStride == that.pixelBitStride &&
this.bitMask == that.bitMask &&
this.pixelsPerDataElement == that.pixelsPerDataElement &&
this.dataElementSize == that.dataElementSize &&
this.dataBitOffset == that.dataBitOffset &&
this.scanlineStride == that.scanlineStride;
}
// 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;
hash ^= pixelBitStride;
hash <<= 8;
hash ^= bitMask;
hash <<= 8;
hash ^= pixelsPerDataElement;
hash <<= 8;
hash ^= dataElementSize;
hash <<= 8;
hash ^= dataBitOffset;
hash <<= 8;
hash ^= scanlineStride;
return hash;
}
}