/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/* ****************************************************************
******************************************************************
******************************************************************
*** 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 <code>MultiPixelPackedSampleModel</code> 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.
* <p>
* The following code illustrates extracting the bits for pixel
* <code>x, y</code> from <code>DataBuffer</code> <code>data</code>
* and storing the pixel data in data elements of type
* <code>dataType</code>:
* <pre>{@code
* 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);
* }</pre>
*/
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 <code>MultiPixelPackedSampleModel</code> 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 <code>dataType</code> is not
* either <code>DataBuffer.TYPE_BYTE</code>,
* <code>DataBuffer.TYPE_USHORT</code>, or
* <code>DataBuffer.TYPE_INT</code>
*/
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 <code>MultiPixelPackedSampleModel</code> 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 <code>w</code> or
* <code>h</code> is not greater than 0
* @throws IllegalArgumentException if <code>dataType</code> is not
* either <code>DataBuffer.TYPE_BYTE</code>,
* <code>DataBuffer.TYPE_USHORT</code>, or
* <code>DataBuffer.TYPE_INT</code>
*/
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 <code>MultiPixelPackedSampleModel</code> with the
* specified width and height. The new
* <code>MultiPixelPackedSampleModel</code> has the
* same storage data type and number of bits per pixel as this
* <code>MultiPixelPackedSampleModel</code>.
* @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 <code>MultiPixelPackedSampleModel</code>.
* @throws IllegalArgumentException if <code>w</code> or
* <code>h</code> is not greater than 0
*/
public
SampleModel createCompatibleSampleModel(int
w, int
h) {
SampleModel sampleModel =
new
MultiPixelPackedSampleModel(
dataType,
w,
h,
pixelBitStride);
return
sampleModel;
}
/**
* Creates a <code>DataBuffer</code> that corresponds to this
* <code>MultiPixelPackedSampleModel</code>. The
* <code>DataBuffer</code> object's data type and size
* is consistent with this <code>MultiPixelPackedSampleModel</code>.
* The <code>DataBuffer</code> has a single bank.
* @return a <code>DataBuffer</code> with the same data type and
* size as this <code>MultiPixelPackedSampleModel</code>.
*/
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 <code>MultiPixelPackedSampleModel</code>, 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 <code>x</code>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
* <code>MultiPixelPackedSampleModel</code>.
*/
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 <code>pixelBitStride</code> of this
* <code>MultiPixelPackedSampleModel</code>.
*/
public int
getPixelBitStride() {
return
pixelBitStride;
}
/**
* Returns the data bit offset in bits.
* @return the <code>dataBitOffset</code> of this
* <code>MultiPixelPackedSampleModel</code>.
*/
public int
getDataBitOffset() {
return
dataBitOffset;
}
/**
* Returns the TransferType used to transfer pixels by way of the
* <code>getDataElements</code> and <code>setDataElements</code>
* 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 <code>MultiPixelPackedSampleModel</code> with a
* subset of the bands of this
* <code>MultiPixelPackedSampleModel</code>. Since a
* <code>MultiPixelPackedSampleModel</code> 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 <code>SampleModel</code> with a subset of bands of
* this <code>MultiPixelPackedSampleModel</code>.
* @exception RasterFormatException if the number of bands requested
* is not one.
* @throws IllegalArgumentException if <code>w</code> or
* <code>h</code> 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 <code>int</code> the sample in a specified band for the
* pixel located at (x, y). An
* <code>ArrayIndexOutOfBoundsException</code> 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 <code>DataBuffer</code> containing the image
* data
* @return the specified band containing the sample of the specified
* pixel.
* @exception ArrayIndexOutOfBoundsException 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 <code>DataBuffer</code> using an
* <code>int</code> for input.
* An <code>ArrayIndexOutOfBoundsException</code> 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 <code>int</code>
* @param data the <code>DataBuffer</code> 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 <code>MultiPixelPackedSampleModel</code>,
* 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, <code>obj</code>
* should be passed in as <code>null</code>, so that the
* <code>Object</code> is created automatically and is the
* correct 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>MultiPixelPackedSampleModel</code>
* <code>mppsm1</code>, to <code>DataBuffer</code> <code>db2</code>,
* whose storage layout is described by
* <code>MultiPixelPackedSampleModel</code> <code>mppsm2</code>.
* The transfer is generally more efficient than using
* <code>getPixel</code> or <code>setPixel</code>.
* <pre>
* MultiPixelPackedSampleModel mppsm1, mppsm2;
* DataBufferInt db1, db2;
* mppsm2.setDataElements(x, y, mppsm1.getDataElements(x, y, null,
* db1), db2);
* </pre>
* Using <code>getDataElements</code> or <code>setDataElements</code>
* to transfer between two <code>DataBuffer/SampleModel</code> pairs
* is legitimate if the <code>SampleModels</code> have the same number
* of bands, corresponding bands have the same number of
* bits per sample, and the TransferTypes are the same.
* <p>
* If <code>obj</code> is not <code>null</code>, it should be a
* primitive array of type TransferType. Otherwise, a
* <code>ClassCastException</code> is thrown. An
* <code>ArrayIndexOutOfBoundsException</code> is 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 specified pixel
* @param y the Y coordinate of the specified pixel
* @param obj a primitive array in which to return the pixel data or
* <code>null</code>.
* @param data the <code>DataBuffer</code> containing the image data.
* @return an <code>Object</code> containing data for the specified
* pixel.
* @exception ClassCastException if <code>obj</code> is not a
* primitive array of type TransferType or is not <code>null</code>
* @exception ArrayIndexOutOfBoundsException if the coordinates are
* not in bounds, or if <code>obj</code> is not <code>null</code> 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 <code>int</code> array.
* <code>ArrayIndexOutOfBoundsException</code> 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
* <code>null</code>
* @param data the <code>DataBuffer</code> 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
* <code>DataBuffer</code> from a primitive array of type
* TransferType. For a <code>MultiPixelPackedSampleModel</code>,
* 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.
* <p>
* The following code illustrates transferring data for one pixel from
* <code>DataBuffer</code> <code>db1</code>, whose storage layout is
* described by <code>MultiPixelPackedSampleModel</code>
* <code>mppsm1</code>, to <code>DataBuffer</code> <code>db2</code>,
* whose storage layout is described by
* <code>MultiPixelPackedSampleModel</code> <code>mppsm2</code>.
* The transfer is generally more efficient than using
* <code>getPixel</code> or <code>setPixel</code>.
* <pre>
* MultiPixelPackedSampleModel mppsm1, mppsm2;
* DataBufferInt db1, db2;
* mppsm2.setDataElements(x, y, mppsm1.getDataElements(x, y, null,
* db1), db2);
* </pre>
* Using <code>getDataElements</code> or <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 TransferTypes are the same.
* <p>
* <code>obj</code> must be a primitive array of type TransferType.
* Otherwise, a <code>ClassCastException</code> is thrown. An
* <code>ArrayIndexOutOfBoundsException</code> is 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 <code>DataBuffer</code> 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 <code>DataBuffer</code> using an
* <code>int</code> array for input.
* <code>ArrayIndexOutOfBoundsException</code> 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 <code>int</code> array
* @param data the <code>DataBuffer</code> 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;
}
}