in src/main/java/org/apache/xmlgraphics/image/codec/tiff/TIFFImage.java [747:1551]
public synchronized Raster getTile(int tileX, int tileY) {
if ((tileX < 0) || (tileX >= tilesX)
|| (tileY < 0) || (tileY >= tilesY)) {
throw new IllegalArgumentException(PropertyUtil.getString("TIFFImage12"));
}
// System.out.println("Called TIFF getTile:" + tileX + "," + tileY);
// Get the data array out of the DataBuffer
byte[] bdata = null;
short[] sdata = null;
int[] idata = null;
SampleModel sampleModel = getSampleModel();
WritableRaster tile = makeTile(tileX, tileY);
DataBuffer buffer = tile.getDataBuffer();
int dataType = sampleModel.getDataType();
if (dataType == DataBuffer.TYPE_BYTE) {
bdata = ((DataBufferByte)buffer).getData();
} else if (dataType == DataBuffer.TYPE_USHORT) {
sdata = ((DataBufferUShort)buffer).getData();
} else if (dataType == DataBuffer.TYPE_SHORT) {
sdata = ((DataBufferShort)buffer).getData();
} else if (dataType == DataBuffer.TYPE_INT) {
idata = ((DataBufferInt)buffer).getData();
}
// Variables used for swapping when converting from RGB to BGR
byte bswap;
short sswap;
int iswap;
// Save original file pointer position and seek to tile data location.
long saveOffset = 0;
try {
saveOffset = stream.getFilePointer();
stream.seek(tileOffsets[tileY * tilesX + tileX]);
} catch (IOException ioe) {
throw new RuntimeException(PropertyUtil.getString("TIFFImage13") + ": "
+ ioe.getMessage());
}
// Number of bytes in this tile (strip) after compression.
int byteCount = (int)tileByteCounts[tileY * tilesX + tileX];
// Find out the number of bytes in the current tile
Rectangle newRect;
if (!tiled) {
newRect = tile.getBounds();
} else {
newRect = new Rectangle(tile.getMinX(), tile.getMinY(),
tileWidth, tileHeight);
}
int unitsInThisTile = newRect.width * newRect.height * numBands;
// Allocate read buffer if needed.
byte[] data = compression != COMP_NONE || imageType == TYPE_PALETTE
? new byte[byteCount] : null;
// Read the data, uncompressing as needed. There are four cases:
// bilevel, palette-RGB, 4-bit grayscale, and everything else.
if (imageType == TYPE_BILEVEL) { // bilevel
try {
if (compression == COMP_PACKBITS) {
stream.readFully(data, 0, byteCount);
// Since the decompressed data will still be packed
// 8 pixels into 1 byte, calculate bytesInThisTile
int bytesInThisTile;
if ((newRect.width % 8) == 0) {
bytesInThisTile = (newRect.width / 8) * newRect.height;
} else {
bytesInThisTile =
(newRect.width / 8 + 1) * newRect.height;
}
decodePackbits(data, bytesInThisTile, bdata);
} else if (compression == COMP_LZW) {
stream.readFully(data, 0, byteCount);
lzwDecoder.decode(data, bdata, newRect.height);
} else if (compression == COMP_FAX_G3_1D) {
stream.readFully(data, 0, byteCount);
decoder.decode1D(bdata, data, 0, newRect.height);
} else if (compression == COMP_FAX_G3_2D) {
stream.readFully(data, 0, byteCount);
decoder.decode2D(bdata, data, 0, newRect.height,
tiffT4Options);
} else if (compression == COMP_FAX_G4_2D) {
stream.readFully(data, 0, byteCount);
decoder.decodeT6(bdata, data, 0, newRect.height,
tiffT6Options);
} else if (compression == COMP_DEFLATE) {
stream.readFully(data, 0, byteCount);
inflate(data, bdata);
} else if (compression == COMP_NONE) {
stream.readFully(bdata, 0, byteCount);
}
stream.seek(saveOffset);
} catch (IOException ioe) {
throw new RuntimeException(PropertyUtil.getString("TIFFImage13") + ": "
+ ioe.getMessage());
}
} else if (imageType == TYPE_PALETTE) { // palette-RGB
if (sampleSize == 16) {
if (decodePaletteAsShorts) {
short[] tempData = null;
// At this point the data is 1 banded and will
// become 3 banded only after we've done the palette
// lookup, since unitsInThisTile was calculated with
// 3 bands, we need to divide this by 3.
int unitsBeforeLookup = unitsInThisTile / 3;
// Since unitsBeforeLookup is the number of shorts,
// but we do our decompression in terms of bytes, we
// need to multiply it by 2 in order to figure out
// how many bytes we'll get after decompression.
int entries = unitsBeforeLookup * 2;
// Read the data, if compressed, decode it, reset the pointer
try {
if (compression == COMP_PACKBITS) {
stream.readFully(data, 0, byteCount);
byte[] byteArray = new byte[entries];
decodePackbits(data, entries, byteArray);
tempData = new short[unitsBeforeLookup];
interpretBytesAsShorts(byteArray, tempData,
unitsBeforeLookup);
} else if (compression == COMP_LZW) {
// Read in all the compressed data for this tile
stream.readFully(data, 0, byteCount);
byte[] byteArray = new byte[entries];
lzwDecoder.decode(data, byteArray, newRect.height);
tempData = new short[unitsBeforeLookup];
interpretBytesAsShorts(byteArray, tempData,
unitsBeforeLookup);
} else if (compression == COMP_DEFLATE) {
stream.readFully(data, 0, byteCount);
byte[] byteArray = new byte[entries];
inflate(data, byteArray);
tempData = new short[unitsBeforeLookup];
interpretBytesAsShorts(byteArray, tempData,
unitsBeforeLookup);
} else if (compression == COMP_NONE) {
// byteCount tells us how many bytes are there
// in this tile, but we need to read in shorts,
// which will take half the space, so while
// allocating we divide byteCount by 2.
tempData = new short[byteCount / 2];
readShorts(byteCount / 2, tempData);
}
stream.seek(saveOffset);
} catch (IOException ioe) {
throw new RuntimeException(PropertyUtil.getString("TIFFImage13") + ": "
+ ioe.getMessage());
}
if (dataType == DataBuffer.TYPE_USHORT) {
// Expand the palette image into an rgb image with ushort
// data type.
int cmapValue;
int count = 0;
int lookup;
int len = colormap.length / 3;
int len2 = len * 2;
for (int i = 0; i < unitsBeforeLookup; i++) {
// Get the index into the colormap
lookup = tempData[i] & 0xffff;
// Get the blue value
cmapValue = colormap[lookup + len2];
sdata[count++] = (short)(cmapValue & 0xffff);
// Get the green value
cmapValue = colormap[lookup + len];
sdata[count++] = (short)(cmapValue & 0xffff);
// Get the red value
cmapValue = colormap[lookup];
sdata[count++] = (short)(cmapValue & 0xffff);
}
} else if (dataType == DataBuffer.TYPE_SHORT) {
// Expand the palette image into an rgb image with
// short data type.
int cmapValue;
int count = 0;
int lookup;
int len = colormap.length / 3;
int len2 = len * 2;
for (int i = 0; i < unitsBeforeLookup; i++) {
// Get the index into the colormap
lookup = tempData[i] & 0xffff;
// Get the blue value
cmapValue = colormap[lookup + len2];
sdata[count++] = (short)cmapValue;
// Get the green value
cmapValue = colormap[lookup + len];
sdata[count++] = (short)cmapValue;
// Get the red value
cmapValue = colormap[lookup];
sdata[count++] = (short)cmapValue;
}
}
} else {
// No lookup being done here, when RGB values are needed,
// the associated IndexColorModel can be used to get them.
try {
if (compression == COMP_PACKBITS) {
stream.readFully(data, 0, byteCount);
// Since unitsInThisTile is the number of shorts,
// but we do our decompression in terms of bytes, we
// need to multiply unitsInThisTile by 2 in order to
// figure out how many bytes we'll get after
// decompression.
int bytesInThisTile = unitsInThisTile * 2;
byte[] byteArray = new byte[bytesInThisTile];
decodePackbits(data, bytesInThisTile, byteArray);
interpretBytesAsShorts(byteArray, sdata,
unitsInThisTile);
} else if (compression == COMP_LZW) {
stream.readFully(data, 0, byteCount);
// Since unitsInThisTile is the number of shorts,
// but we do our decompression in terms of bytes, we
// need to multiply unitsInThisTile by 2 in order to
// figure out how many bytes we'll get after
// decompression.
byte[] byteArray = new byte[unitsInThisTile * 2];
lzwDecoder.decode(data, byteArray, newRect.height);
interpretBytesAsShorts(byteArray, sdata,
unitsInThisTile);
} else if (compression == COMP_DEFLATE) {
stream.readFully(data, 0, byteCount);
byte[] byteArray = new byte[unitsInThisTile * 2];
inflate(data, byteArray);
interpretBytesAsShorts(byteArray, sdata,
unitsInThisTile);
} else if (compression == COMP_NONE) {
readShorts(byteCount / 2, sdata);
}
stream.seek(saveOffset);
} catch (IOException ioe) {
throw new RuntimeException(PropertyUtil.getString("TIFFImage13") + ": "
+ ioe.getMessage());
}
}
} else if (sampleSize == 8) {
if (decodePaletteAsShorts) {
byte[] tempData = null;
// At this point the data is 1 banded and will
// become 3 banded only after we've done the palette
// lookup, since unitsInThisTile was calculated with
// 3 bands, we need to divide this by 3.
int unitsBeforeLookup = unitsInThisTile / 3;
// Read the data, if compressed, decode it, reset the pointer
try {
if (compression == COMP_PACKBITS) {
stream.readFully(data, 0, byteCount);
tempData = new byte[unitsBeforeLookup];
decodePackbits(data, unitsBeforeLookup, tempData);
} else if (compression == COMP_LZW) {
stream.readFully(data, 0, byteCount);
tempData = new byte[unitsBeforeLookup];
lzwDecoder.decode(data, tempData, newRect.height);
} else if (compression == COMP_DEFLATE) {
stream.readFully(data, 0, byteCount);
tempData = new byte[unitsBeforeLookup];
inflate(data, tempData);
} else if (compression == COMP_NONE) {
tempData = new byte[byteCount];
stream.readFully(tempData, 0, byteCount);
} else {
throw new RuntimeException(PropertyUtil.getString("IFFImage10") + ": "
+ compression);
}
stream.seek(saveOffset);
} catch (IOException ioe) {
throw new RuntimeException(PropertyUtil.getString("TIFFImage13") + ": "
+ ioe.getMessage());
}
// Expand the palette image into an rgb image with ushort
// data type.
int cmapValue;
int count = 0;
int lookup;
int len = colormap.length / 3;
int len2 = len * 2;
for (int i = 0; i < unitsBeforeLookup; i++) {
// Get the index into the colormap
lookup = tempData[i] & 0xff;
// Get the blue value
cmapValue = colormap[lookup + len2];
sdata[count++] = (short)(cmapValue & 0xffff);
// Get the green value
cmapValue = colormap[lookup + len];
sdata[count++] = (short)(cmapValue & 0xffff);
// Get the red value
cmapValue = colormap[lookup];
sdata[count++] = (short)(cmapValue & 0xffff);
}
} else {
// No lookup being done here, when RGB values are needed,
// the associated IndexColorModel can be used to get them.
try {
if (compression == COMP_PACKBITS) {
stream.readFully(data, 0, byteCount);
decodePackbits(data, unitsInThisTile, bdata);
} else if (compression == COMP_LZW) {
stream.readFully(data, 0, byteCount);
lzwDecoder.decode(data, bdata, newRect.height);
} else if (compression == COMP_DEFLATE) {
stream.readFully(data, 0, byteCount);
inflate(data, bdata);
} else if (compression == COMP_NONE) {
stream.readFully(bdata, 0, byteCount);
} else {
throw new RuntimeException(PropertyUtil.getString("TIFFImage10")
+ ": " + compression);
}
stream.seek(saveOffset);
} catch (IOException ioe) {
throw new RuntimeException(PropertyUtil.getString("TIFFImage13") + ": "
+ ioe.getMessage());
}
}
} else if (sampleSize == 4) {
int padding = (newRect.width % 2 == 0) ? 0 : 1;
int bytesPostDecoding = ((newRect.width / 2 + padding) * newRect.height);
// Output short images
if (decodePaletteAsShorts) {
byte[] tempData = null;
try {
stream.readFully(data, 0, byteCount);
stream.seek(saveOffset);
} catch (IOException ioe) {
throw new RuntimeException(PropertyUtil.getString("TIFFImage13") + ": "
+ ioe.getMessage());
}
// If compressed, decode the data.
if (compression == COMP_PACKBITS) {
tempData = new byte[bytesPostDecoding];
decodePackbits(data, bytesPostDecoding, tempData);
} else if (compression == COMP_LZW) {
tempData = new byte[bytesPostDecoding];
lzwDecoder.decode(data, tempData, newRect.height);
} else if (compression == COMP_DEFLATE) {
tempData = new byte[bytesPostDecoding];
inflate(data, tempData);
} else if (compression == COMP_NONE) {
tempData = data;
}
int bytes = unitsInThisTile / 3;
// Unpack the 2 pixels packed into each byte.
data = new byte[bytes];
int srcCount = 0;
int dstCount = 0;
for (int j = 0; j < newRect.height; j++) {
for (int i = 0; i < newRect.width / 2; i++) {
data[dstCount++] =
(byte)((tempData[srcCount] & 0xf0) >> 4);
data[dstCount++] =
(byte)(tempData[srcCount++] & 0x0f);
}
if (padding == 1) {
data[dstCount++] =
(byte)((tempData[srcCount++] & 0xf0) >> 4);
}
}
int len = colormap.length / 3;
int len2 = len * 2;
int cmapValue;
int lookup;
int count = 0;
for (int i = 0; i < bytes; i++) {
lookup = data[i] & 0xff;
cmapValue = colormap[lookup + len2];
sdata[count++] = (short)(cmapValue & 0xffff);
cmapValue = colormap[lookup + len];
sdata[count++] = (short)(cmapValue & 0xffff);
cmapValue = colormap[lookup];
sdata[count++] = (short)(cmapValue & 0xffff);
}
} else {
// Output byte values, use IndexColorModel for unpacking
try {
// If compressed, decode the data.
if (compression == COMP_PACKBITS) {
stream.readFully(data, 0, byteCount);
decodePackbits(data, bytesPostDecoding, bdata);
} else if (compression == COMP_LZW) {
stream.readFully(data, 0, byteCount);
lzwDecoder.decode(data, bdata, newRect.height);
} else if (compression == COMP_DEFLATE) {
stream.readFully(data, 0, byteCount);
inflate(data, bdata);
} else if (compression == COMP_NONE) {
stream.readFully(bdata, 0, byteCount);
}
stream.seek(saveOffset);
} catch (IOException ioe) {
throw new RuntimeException(PropertyUtil.getString("TIFFImage13") + ": "
+ ioe.getMessage());
}
}
}
} else if (imageType == TYPE_GRAY_4BIT) { // 4-bit gray
try {
if (compression == COMP_PACKBITS) {
stream.readFully(data, 0, byteCount);
// Since the decompressed data will still be packed
// 2 pixels into 1 byte, calculate bytesInThisTile
int bytesInThisTile;
if ((newRect.width % 8) == 0) {
bytesInThisTile = (newRect.width / 2) * newRect.height;
} else {
bytesInThisTile = (newRect.width / 2 + 1)
* newRect.height;
}
decodePackbits(data, bytesInThisTile, bdata);
} else if (compression == COMP_LZW) {
stream.readFully(data, 0, byteCount);
lzwDecoder.decode(data, bdata, newRect.height);
} else if (compression == COMP_DEFLATE) {
stream.readFully(data, 0, byteCount);
inflate(data, bdata);
} else {
stream.readFully(bdata, 0, byteCount);
}
stream.seek(saveOffset);
} catch (IOException ioe) {
throw new RuntimeException(PropertyUtil.getString("TIFFImage13") + ": "
+ ioe.getMessage());
}
} else { // everything else
try {
if (sampleSize == 8) {
if (compression == COMP_NONE) {
stream.readFully(bdata, 0, byteCount);
} else if (compression == COMP_LZW) {
stream.readFully(data, 0, byteCount);
lzwDecoder.decode(data, bdata, newRect.height);
} else if (compression == COMP_PACKBITS) {
stream.readFully(data, 0, byteCount);
decodePackbits(data, unitsInThisTile, bdata);
} else if (compression == COMP_DEFLATE) {
stream.readFully(data, 0, byteCount);
inflate(data, bdata);
} else {
throw new RuntimeException(PropertyUtil.getString("TIFFImage10")
+ ": " + compression);
}
} else if (sampleSize == 16) {
if (compression == COMP_NONE) {
readShorts(byteCount / 2, sdata);
} else if (compression == COMP_LZW) {
stream.readFully(data, 0, byteCount);
// Since unitsInThisTile is the number of shorts,
// but we do our decompression in terms of bytes, we
// need to multiply unitsInThisTile by 2 in order to
// figure out how many bytes we'll get after
// decompression.
byte[] byteArray = new byte[unitsInThisTile * 2];
lzwDecoder.decode(data, byteArray, newRect.height);
interpretBytesAsShorts(byteArray, sdata,
unitsInThisTile);
} else if (compression == COMP_PACKBITS) {
stream.readFully(data, 0, byteCount);
// Since unitsInThisTile is the number of shorts,
// but we do our decompression in terms of bytes, we
// need to multiply unitsInThisTile by 2 in order to
// figure out how many bytes we'll get after
// decompression.
int bytesInThisTile = unitsInThisTile * 2;
byte[] byteArray = new byte[bytesInThisTile];
decodePackbits(data, bytesInThisTile, byteArray);
interpretBytesAsShorts(byteArray, sdata,
unitsInThisTile);
} else if (compression == COMP_DEFLATE) {
stream.readFully(data, 0, byteCount);
byte[] byteArray = new byte[unitsInThisTile * 2];
inflate(data, byteArray);
interpretBytesAsShorts(byteArray, sdata,
unitsInThisTile);
}
} else if (sampleSize == 32
&& dataType == DataBuffer.TYPE_INT) { // redundant
if (compression == COMP_NONE) {
readInts(byteCount / 4, idata);
} else if (compression == COMP_LZW) {
stream.readFully(data, 0, byteCount);
// Since unitsInThisTile is the number of ints,
// but we do our decompression in terms of bytes, we
// need to multiply unitsInThisTile by 4 in order to
// figure out how many bytes we'll get after
// decompression.
byte[] byteArray = new byte[unitsInThisTile * 4];
lzwDecoder.decode(data, byteArray, newRect.height);
interpretBytesAsInts(byteArray, idata,
unitsInThisTile);
} else if (compression == COMP_PACKBITS) {
stream.readFully(data, 0, byteCount);
// Since unitsInThisTile is the number of ints,
// but we do our decompression in terms of bytes, we
// need to multiply unitsInThisTile by 4 in order to
// figure out how many bytes we'll get after
// decompression.
int bytesInThisTile = unitsInThisTile * 4;
byte[] byteArray = new byte[bytesInThisTile];
decodePackbits(data, bytesInThisTile, byteArray);
interpretBytesAsInts(byteArray, idata,
unitsInThisTile);
} else if (compression == COMP_DEFLATE) {
stream.readFully(data, 0, byteCount);
byte[] byteArray = new byte[unitsInThisTile * 4];
inflate(data, byteArray);
interpretBytesAsInts(byteArray, idata,
unitsInThisTile);
}
}
stream.seek(saveOffset);
} catch (IOException ioe) {
throw new RuntimeException(PropertyUtil.getString("TIFFImage13") + ": "
+ ioe.getMessage());
}
// Modify the data for certain special cases.
switch (imageType) {
case TYPE_GRAY:
case TYPE_GRAY_ALPHA:
if (isWhiteZero) {
// Since we are using a ComponentColorModel with this
// image, we need to change the WhiteIsZero data to
// BlackIsZero data so it will display properly.
if (dataType == DataBuffer.TYPE_BYTE
&& !(getColorModel() instanceof IndexColorModel)) {
for (int l = 0; l < bdata.length; l += numBands) {
bdata[l] = (byte)(255 - bdata[l]);
}
} else if (dataType == DataBuffer.TYPE_USHORT) {
int ushortMax = Short.MAX_VALUE - Short.MIN_VALUE;
for (int l = 0; l < sdata.length; l += numBands) {
sdata[l] = (short)(ushortMax - sdata[l]);
}
} else if (dataType == DataBuffer.TYPE_SHORT) {
for (int l = 0; l < sdata.length; l += numBands) {
sdata[l] = (short)(~sdata[l]);
}
} else if (dataType == DataBuffer.TYPE_INT) {
long uintMax = ((long)Integer.MAX_VALUE
- (long)Integer.MIN_VALUE);
for (int l = 0; l < idata.length; l += numBands) {
idata[l] = (int)(uintMax - idata[l]);
}
}
}
break;
case TYPE_RGB:
// Change RGB to BGR order, as Java2D displays that faster.
// Unnecessary for JPEG-in-TIFF as the decoder handles it.
if (sampleSize == 8 && compression != COMP_JPEG_TTN2) {
for (int i = 0; i < unitsInThisTile; i += 3) {
bswap = bdata[i];
bdata[i] = bdata[i + 2];
bdata[i + 2] = bswap;
}
} else if (sampleSize == 16) {
for (int i = 0; i < unitsInThisTile; i += 3) {
sswap = sdata[i];
sdata[i] = sdata[i + 2];
sdata[i + 2] = sswap;
}
} else if (sampleSize == 32) {
if (dataType == DataBuffer.TYPE_INT) {
for (int i = 0; i < unitsInThisTile; i += 3) {
iswap = idata[i];
idata[i] = idata[i + 2];
idata[i + 2] = iswap;
}
}
}
break;
case TYPE_RGB_ALPHA:
// Convert from RGBA to ABGR for Java2D
if (sampleSize == 8) {
for (int i = 0; i < unitsInThisTile; i += 4) {
// Swap R and A
bswap = bdata[i];
bdata[i] = bdata[i + 3];
bdata[i + 3] = bswap;
// Swap G and B
bswap = bdata[i + 1];
bdata[i + 1] = bdata[i + 2];
bdata[i + 2] = bswap;
}
} else if (sampleSize == 16) {
for (int i = 0; i < unitsInThisTile; i += 4) {
// Swap R and A
sswap = sdata[i];
sdata[i] = sdata[i + 3];
sdata[i + 3] = sswap;
// Swap G and B
sswap = sdata[i + 1];
sdata[i + 1] = sdata[i + 2];
sdata[i + 2] = sswap;
}
} else if (sampleSize == 32) {
if (dataType == DataBuffer.TYPE_INT) {
for (int i = 0; i < unitsInThisTile; i += 4) {
// Swap R and A
iswap = idata[i];
idata[i] = idata[i + 3];
idata[i + 3] = iswap;
// Swap G and B
iswap = idata[i + 1];
idata[i + 1] = idata[i + 2];
idata[i + 2] = iswap;
}
}
}
break;
case TYPE_YCBCR_SUB:
// Post-processing for YCbCr with subsampled chrominance:
// simply replicate the chroma channels for displayability.
int pixelsPerDataUnit = chromaSubH * chromaSubV;
int numH = newRect.width / chromaSubH;
int numV = newRect.height / chromaSubV;
byte[] tempData = new byte[numH * numV * (pixelsPerDataUnit + 2)];
System.arraycopy(bdata, 0, tempData, 0, tempData.length);
int samplesPerDataUnit = pixelsPerDataUnit * 3;
int[] pixels = new int[samplesPerDataUnit];
int bOffset = 0;
int offsetCb = pixelsPerDataUnit;
int offsetCr = offsetCb + 1;
int y = newRect.y;
for (int j = 0; j < numV; j++) {
int x = newRect.x;
for (int i = 0; i < numH; i++) {
int cb = tempData[bOffset + offsetCb];
int cr = tempData[bOffset + offsetCr];
int k = 0;
while (k < samplesPerDataUnit) {
pixels[k++] = tempData[bOffset++];
pixels[k++] = cb;
pixels[k++] = cr;
}
bOffset += 2;
tile.setPixels(x, y, chromaSubH, chromaSubV, pixels);
x += chromaSubH;
}
y += chromaSubV;
}
break;
}
}
return tile;
}