in pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawer.java [1075:1262]
public void drawImage(PDImage pdImage) throws IOException
{
if (pdImage instanceof PDImageXObject &&
isHiddenOCG(((PDImageXObject) pdImage).getOptionalContent()))
{
return;
}
if (!isContentRendered())
{
return;
}
Matrix ctm = getGraphicsState().getCurrentTransformationMatrix();
AffineTransform at = ctm.createAffineTransform();
if (!pdImage.getInterpolate())
{
// if the image is scaled down, we use smooth interpolation, eg PDFBOX-2364
// only when scaled up do we use nearest neighbour, eg PDFBOX-2302 / mori-cvpr01.pdf
// PDFBOX-4930: we use the sizes of the ARGB image. These can be different
// than the original sizes of the base image, when the mask is bigger.
// PDFBOX-5091: also consider subsampling, the sizes are different too.
BufferedImage bim;
if (subsamplingAllowed)
{
bim = pdImage.getImage(null, getSubsampling(pdImage, at));
}
else
{
bim = pdImage.getImage();
}
boolean isScaledUp =
bim.getWidth() <= Math.abs(Math.round(ctm.getScalingFactorX() * xformScalingFactorX)) ||
bim.getHeight() <= Math.abs(Math.round(ctm.getScalingFactorY() * xformScalingFactorY));
if (isScaledUp)
{
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
}
}
graphics.setComposite(getGraphicsState().getNonStrokingJavaComposite());
setClip();
if (pdImage.isStencil())
{
if (getGraphicsState().getNonStrokingColor().getColorSpace() instanceof PDPattern)
{
// The earlier code for stencils (see "else") doesn't work with patterns because the
// CTM is not taken into consideration.
// this code is based on the fact that it is easily possible to draw the mask and
// the paint at the correct place with the existing code, but not in one step.
// Thus what we do is to draw both in separate images, then combine the two and draw
// the result.
// Note that the device scale is not used. In theory, some patterns can get better
// at higher resolutions but the stencil would become more and more "blocky".
// If anybody wants to do this, have a look at the code in showTransparencyGroup().
// draw the paint
Paint paint = getNonStrokingPaint();
Rectangle2D unitRect = new Rectangle2D.Float(0, 0, 1, 1);
Rectangle2D bounds = at.createTransformedShape(unitRect).getBounds2D();
int w = (int) Math.ceil(bounds.getWidth());
int h = (int) Math.ceil(bounds.getHeight());
BufferedImage renderedPaint = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) renderedPaint.getGraphics();
g.translate(-bounds.getMinX(), -bounds.getMinY());
g.setPaint(paint);
g.setRenderingHints(graphics.getRenderingHints());
g.fill(bounds);
g.dispose();
// draw the mask
BufferedImage mask = pdImage.getImage();
AffineTransform imageTransform = new AffineTransform(at);
imageTransform.scale(1.0 / mask.getWidth(), -1.0 / mask.getHeight());
imageTransform.translate(0, -mask.getHeight());
AffineTransform full = new AffineTransform(g.getTransform());
full.concatenate(imageTransform);
Matrix m = new Matrix(full);
double scaleX = Math.abs(m.getScalingFactorX());
double scaleY = Math.abs(m.getScalingFactorY());
boolean smallMask = mask.getWidth() <= 8 && mask.getHeight() <= 8;
if (mask.getWidth() == 1 && mask.getHeight() == 1)
{
// PDFBOX-5802: force usage of the lookup table if it is only 1 pixel
// (See the comment for PDFBOX-5403 that it isn't done for some
// cases based purely on the rendering result of one file!)
smallMask = false;
}
if (!smallMask)
{
// PDFBOX-5403:
// The mask is copied to RGB because this supports a smooth scaling, so we
// get a mask with 255 values instead of just 0 and 255.
// Inverting is done because when we don't do it, the getScaledInstance() call
// produces a black line in many masks. With the inversion we have a white line
// which is neutral. Because of the inversion we don't have to substract from 255
// in the "apply the mask" segment when rasterPixel[3] is assigned.
// The inversion is not done for very small ones, because of
// PDFBOX-2171-002-002710-p14.pdf where the "New Harmony Consolidated" and
// "Sailor Springs" patterns became almost invisible.
// (We may have to decide this differently in the future, e.g. on b/w relationship)
BufferedImage tmp = new BufferedImage(mask.getWidth(), mask.getHeight(), BufferedImage.TYPE_INT_RGB);
mask = new LookupOp(getInvLookupTable(), graphics.getRenderingHints()).filter(mask, tmp);
}
BufferedImage renderedMask = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) renderedMask.getGraphics();
g.translate(-bounds.getMinX(), -bounds.getMinY());
g.setRenderingHints(graphics.getRenderingHints());
if (smallMask)
{
g.drawImage(mask, imageTransform, null);
}
else if (scaleX != 0 && scaleY != 0)
{
while (scaleX < 0.25 || Math.round(mask.getWidth() * scaleX) < 1)
{
scaleX *= 2.0;
}
while (scaleY < 0.25 || Math.round(mask.getHeight() * scaleY) < 1)
{
scaleY *= 2.0;
}
int w2 = (int) Math.round(mask.getWidth() * scaleX);
int h2 = (int) Math.round(mask.getHeight() * scaleY);
Image scaledMask = mask.getScaledInstance(w2, h2, Image.SCALE_SMOOTH);
imageTransform.scale(1f / Math.abs(scaleX), 1f / Math.abs(scaleY));
g.drawImage(scaledMask, imageTransform, null);
}
g.dispose();
// apply the mask
int[] alphaPixel = null;
int[] rasterPixel = null;
WritableRaster raster = renderedPaint.getRaster();
WritableRaster alpha = renderedMask.getRaster();
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
alphaPixel = alpha.getPixel(x, y, alphaPixel);
rasterPixel = raster.getPixel(x, y, rasterPixel);
rasterPixel[3] = alphaPixel[0];
raster.setPixel(x, y, rasterPixel);
}
}
// draw the image
graphics.drawImage(renderedPaint,
AffineTransform.getTranslateInstance(bounds.getMinX(), bounds.getMinY()),
null);
}
else
{
// fill the image with stenciled paint
BufferedImage image = pdImage.getStencilImage(getNonStrokingPaint());
// draw the image
drawBufferedImage(pdImage, image, at);
}
}
else
{
if (subsamplingAllowed)
{
int subsampling = getSubsampling(pdImage, at);
// draw the subsampled image
drawBufferedImage(pdImage, pdImage.getImage(null, subsampling), at);
}
else
{
// subsampling not allowed, draw the image
drawBufferedImage(pdImage, pdImage.getImage(), at);
}
}
if (!pdImage.getInterpolate())
{
// JDK 1.7 has a bug where rendering hints are reset by the above call to
// the setRenderingHint method, so we re-set all hints, see PDFBOX-2302
setRenderingHints();
}
}