// slower, general-purpose image conversion from any image format
private static BufferedImage fromAny(PDImage pdImage, WritableRaster raster, COSArray colorKey)
throws IOException
{
final PDColorSpace colorSpace = pdImage.getColorSpace();
final int numComponents = colorSpace.getNumberOfComponents();
final int width = pdImage.getWidth();
final int height = pdImage.getHeight();
final int bitsPerComponent = pdImage.getBitsPerComponent();
final float[] decode = getDecodeArray(pdImage);
// read bit stream
ImageInputStream iis = null;
try
{
// create stream
iis = new MemoryCacheImageInputStream(pdImage.getStream().createInputStream());
final float sampleMax = (float)Math.pow(2, bitsPerComponent) - 1f;
final boolean isIndexed = colorSpace instanceof PDIndexed;
// init color key mask
float[] colorKeyRanges = null;
BufferedImage colorKeyMask = null;
if (colorKey != null)
{
colorKeyRanges = colorKey.toFloatArray();
colorKeyMask = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
}
// calculate row padding
int padding = 0;
if (width * numComponents * bitsPerComponent % 8 > 0)
{
padding = 8 - (width * numComponents * bitsPerComponent % 8);
}
// read stream
byte[] srcColorValues = new byte[numComponents];
byte[] alpha = new byte[1];
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
boolean isMasked = true;
for (int c = 0; c < numComponents; c++)
{
int value = (int)iis.readBits(bitsPerComponent);
// color key mask requires values before they are decoded
if (colorKeyRanges != null)
{
isMasked &= value >= colorKeyRanges[c * 2] &&
value <= colorKeyRanges[c * 2 + 1];
}
// decode array
final float dMin = decode[c * 2];
final float dMax = decode[(c * 2) + 1];
// interpolate to domain
float output = dMin + (value * ((dMax - dMin) / sampleMax));
if (isIndexed)
{
// indexed color spaces get the raw value, because the TYPE_BYTE
// below cannot be reversed by the color space without it having
// knowledge of the number of bits per component
srcColorValues[c] = (byte)Math.round(output);
}
else
{
// interpolate to TYPE_BYTE
int outputByte = Math.round(((output - Math.min(dMin, dMax)) /
Math.abs(dMax - dMin)) * 255f);
srcColorValues[c] = (byte)outputByte;
}
}
raster.setDataElements(x, y, srcColorValues);
// set alpha channel in color key mask, if any
if (colorKeyMask != null)
{
alpha[0] = (byte)(isMasked ? 255 : 0);
colorKeyMask.getRaster().setDataElements(x, y, alpha);
}
}
// rows are padded to the nearest byte
iis.readBits(padding);
}
// use the color space to convert the image to RGB
BufferedImage rgbImage = colorSpace.toRGBImage(raster);
// apply color mask, if any
if (colorKeyMask != null)
{
return applyColorKeyMask(rgbImage, colorKeyMask);