private BufferedImage readImage(PcxHeader pcxHeader, InputStream is, ByteSource byteSource)
throws ImageReadException, IOException
{
int xSize = pcxHeader.xMax - pcxHeader.xMin + 1;
if (xSize < 0)
throw new ImageReadException("Image width is negative");
int ySize = pcxHeader.yMax - pcxHeader.yMin + 1;
if (ySize < 0)
throw new ImageReadException("Image height is negative");
int scanlineLength = pcxHeader.bytesPerLine * pcxHeader.nPlanes;
byte[] scanline = new byte[scanlineLength];
if ((pcxHeader.bitsPerPixel == 1 || pcxHeader.bitsPerPixel == 2
|| pcxHeader.bitsPerPixel == 4 || pcxHeader.bitsPerPixel == 8) &&
pcxHeader.nPlanes == 1)
{
int bytesPerImageRow = (xSize * pcxHeader.bitsPerPixel + 7) / 8;
byte[] image = new byte[ySize * bytesPerImageRow];
for (int y = 0; y < ySize; y++)
{
readScanLine(pcxHeader, is, scanline);
System.arraycopy(scanline, 0, image, y*bytesPerImageRow, bytesPerImageRow);
}
DataBufferByte dataBuffer = new DataBufferByte(image, image.length);
int[] palette;
if (pcxHeader.bitsPerPixel == 1)
palette = new int[] { 0x000000, 0xffffff };
else if (pcxHeader.bitsPerPixel == 8)
{
// Normally the palette is read 769 bytes from the end of the file.
// However DCX files have multiple PCX images in one file, so
// there could be extra data before the end! So try look for the palette
// immediately after the image data first.
palette = read256ColorPalette(is);
if (palette == null)
palette = read256ColorPaletteFromEndOfFile(byteSource);
if (palette == null)
throw new ImageReadException(
"No 256 color palette found in image that needs it");
}
else
palette = pcxHeader.colormap;
WritableRaster raster;
if (pcxHeader.bitsPerPixel == 8)
{
raster = WritableRaster.createInterleavedRaster(dataBuffer,
xSize, ySize, bytesPerImageRow, 1, new int[]{0}, null);
}
else
{
raster = WritableRaster.createPackedRaster(dataBuffer,
xSize, ySize, pcxHeader.bitsPerPixel, null);
}
IndexColorModel colorModel = new IndexColorModel(pcxHeader.bitsPerPixel,
1 << pcxHeader.bitsPerPixel, palette, 0, false, -1, DataBuffer.TYPE_BYTE);
return new BufferedImage(colorModel, raster,
colorModel.isAlphaPremultiplied(), new Properties());
}
else if (pcxHeader.bitsPerPixel == 1 && 2 <= pcxHeader.nPlanes
&& pcxHeader.nPlanes <= 4)
{
IndexColorModel colorModel = new IndexColorModel(pcxHeader.nPlanes,
1 << pcxHeader.nPlanes, pcxHeader.colormap, 0, false, -1, DataBuffer.TYPE_BYTE);
BufferedImage image = new BufferedImage(xSize, ySize, BufferedImage.TYPE_BYTE_BINARY, colorModel);
byte[] unpacked = new byte[xSize];
for (int y = 0; y < ySize; y++)
{
readScanLine(pcxHeader, is, scanline);
int nextByte = 0;
Arrays.fill(unpacked, (byte) 0);
for (int plane = 0; plane < pcxHeader.nPlanes; plane++)
{
for (int i = 0; i < pcxHeader.bytesPerLine; i++)
{
int b = 0xff & scanline[nextByte++];
for (int j = 0; j < 8 && 8*i + j < unpacked.length; j++)
unpacked[8*i + j] |= (byte) (((b >> (7 - j)) & 0x1) << plane);
}
}
image.getRaster().setDataElements(0, y, xSize, 1, unpacked);
}
return image;
}
else if (pcxHeader.bitsPerPixel == 8 && pcxHeader.nPlanes == 3)
{
byte[][] image = new byte[3][];
image[0] = new byte[xSize*ySize];
image[1] = new byte[xSize*ySize];
image[2] = new byte[xSize*ySize];
for (int y = 0; y < ySize; y++)
{
readScanLine(pcxHeader, is, scanline);
System.arraycopy(scanline, 0, image[0], y*xSize, xSize);
System.arraycopy(scanline, pcxHeader.bytesPerLine,
image[1], y*xSize, xSize);
System.arraycopy(scanline, 2*pcxHeader.bytesPerLine,
image[2], y*xSize, xSize);
}
DataBufferByte dataBuffer = new DataBufferByte(image, image[0].length);
WritableRaster raster = WritableRaster.createBandedRaster(dataBuffer,
xSize, ySize, xSize, new int[]{0,1,2},
new int[]{0,0,0}, null);
ColorModel colorModel = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB), false, false,
Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
return new BufferedImage(colorModel, raster,
colorModel.isAlphaPremultiplied(), new Properties());
}
else if ((pcxHeader.bitsPerPixel == 24 && pcxHeader.nPlanes == 1) ||
(pcxHeader.bitsPerPixel == 32 && pcxHeader.nPlanes == 1))
{
int rowLength = 3 * xSize;
byte[] image = new byte[rowLength * ySize];
for (int y = 0; y < ySize; y++)
{
readScanLine(pcxHeader, is, scanline);
if (pcxHeader.bitsPerPixel == 24)
System.arraycopy(scanline, 0, image, y*rowLength, rowLength);
else
{
for (int x = 0; x < xSize; x++)
{
image[y*rowLength + 3*x] = scanline[4*x];
image[y*rowLength + 3*x + 1] = scanline[4*x + 1];
image[y*rowLength + 3*x + 2] = scanline[4*x + 2];
}
}
}
DataBufferByte dataBuffer = new DataBufferByte(image, image.length);
WritableRaster raster = WritableRaster.createInterleavedRaster(
dataBuffer, xSize, ySize, rowLength, 3,
new int[]{2,1,0}, null);
ColorModel colorModel = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB), false, false,
Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
return new BufferedImage(colorModel, raster,
colorModel.isAlphaPremultiplied(), new Properties());
}
else
{
throw new ImageReadException("Invalid/unsupported image with bitsPerPixel "
+ pcxHeader.bitsPerPixel + " and planes " + pcxHeader.nPlanes);
}
}