in pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/SampledImageReader.java [554:703]
private static BufferedImage fromAny(PDImage pdImage, WritableRaster raster, COSArray colorKey, Rectangle clipped,
final int subsampling, final int width, final int height)
throws IOException
{
int currentSubsampling = subsampling;
final PDColorSpace colorSpace = pdImage.getColorSpace();
final int numComponents = colorSpace.getNumberOfComponents();
final int bitsPerComponent = pdImage.getBitsPerComponent();
final float[] decode = getDecodeArray(pdImage);
DecodeOptions options = new DecodeOptions(currentSubsampling);
options.setSourceRegion(clipped);
// MemoryCacheImageInputStream doesn't close the wrapped stream
try (InputStream imageStream = pdImage.createInputStream(options))
{
// read bit stream
try (ImageInputStream iis = new MemoryCacheImageInputStream(imageStream))
{
final int inputWidth;
final int startx;
final int starty;
final int scanWidth;
final int scanHeight;
if (options.isFilterSubsampled())
{
// Decode options were honored, and so there is no need for additional clipping or subsampling
inputWidth = width;
startx = 0;
starty = 0;
scanWidth = width;
scanHeight = height;
currentSubsampling = 1;
}
else
{
// Decode options not honored, so we need to clip and subsample ourselves.
inputWidth = pdImage.getWidth();
startx = clipped.x;
starty = clipped.y;
scanWidth = clipped.width;
scanHeight = clipped.height;
}
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)
{
if (colorKey.size() >= numComponents * 2)
{
colorKeyRanges = colorKey.toFloatArray();
colorKeyMask = new BufferedImage(width, height,
BufferedImage.TYPE_BYTE_GRAY);
}
else
{
LOG.warn("colorKey mask size is {}, should be {}, ignored", colorKey.size(),
numComponents * 2);
}
}
// calculate row padding
int padding = 0;
if (inputWidth * numComponents * bitsPerComponent % 8 > 0)
{
padding = 8 - (inputWidth * numComponents * bitsPerComponent % 8);
}
// read stream
byte[] srcColorValues = new byte[numComponents];
byte[] alpha = new byte[1];
for (int y = 0; y < starty + scanHeight; y++)
{
for (int x = 0; x < startx + scanWidth; 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;
}
}
// only write to output if within requested region and subsample.
if (x >= startx && y >= starty && x % currentSubsampling == 0
&& y % currentSubsampling == 0)
{
raster.setDataElements((x - startx) / currentSubsampling,
(y - starty) / currentSubsampling, srcColorValues);
// set alpha channel in color key mask, if any
if (colorKeyMask != null)
{
alpha[0] = (byte) (isMasked ? 255 : 0);
colorKeyMask.getRaster().setDataElements(
(x - startx) / currentSubsampling,
(y - starty) / currentSubsampling, 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);
}
else
{
return rgbImage;
}
}
}
}