/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pdfbox.pdmodel.graphics.xobject;
import java.awt.image.BufferedImage;
import java.io.IOException;
import org.apache.pdfbox.cos.COSArray;
/**
* This class is responsible for combining a base image with an SMask-based transparency
* image to form a composite image.
* See section 11.5 of the pdf specification for details on Soft Masks.
* <p/>
* Briefly however, an Smask is a supplementary greyscale image whose RGB-values define
* a transparency mask which, when combined appropriately with the base image,
* allows per-pixel transparency to be applied.
* <p/>
* Note that Smasks are not required for any image and if the smask is not present
* in the pdf file, the image will have no transparent pixels.
*
* @author Neil McErlean
*/
public class CompositeImage
{
private BufferedImage baseImage;
private BufferedImage smaskImage;
/**
* Standard constructor.
* @param baseImage the base Image.
* @param smaskImage the transparency image.
*
*/
public CompositeImage(BufferedImage baseImage, BufferedImage smaskImage)
{
this.baseImage = baseImage;
this.smaskImage = smaskImage;
}
/**
* This method applies the specified transparency mask to a given image and returns a new BufferedImage
* whose alpha values are computed from the transparency mask (smask) image.
*/
public BufferedImage createMaskedImage(COSArray decodeArray) throws IOException
{
// The decode array should only be [0 1] or [1 0]. See PDF spec.
// [0 1] means the smask's RGB values give transparency. Default: see PDF spec section 8.9.5.1
// [1 0] means the smask's RGB values give opacity.
boolean isOpaque = false;
if (decodeArray != null)
{
isOpaque = decodeArray.getInt(0) > decodeArray.getInt(1);
}
final int baseImageWidth = baseImage.getWidth();
final int baseImageHeight = baseImage.getHeight();
BufferedImage result = new BufferedImage(baseImageWidth, baseImageHeight, BufferedImage.TYPE_INT_ARGB);
for (int x = 0; x < baseImageWidth; x++)
{
for (int y = 0; y < baseImageHeight; y++)
{
int rgb = baseImage.getRGB(x, y);
int alpha = smaskImage.getRGB(x, y);
// The smask image defines a transparency mask but it has no alpha values itself, instead
// using the greyscale values to indicate transparency.
// 0xAARRGGBB
// We need to remove any alpha value in the main image.
int rgbOnly = 0x00FFFFFF & rgb;
// We need to use one of the rgb values as the new alpha value for the main image.
// It seems the mask is greyscale, so it shouldn't matter whether we use R, G or B
// as the indicator of transparency.
if (isOpaque)
{
alpha = ~alpha;
}
int alphaOnly = alpha << 24;
result.setRGB(x, y, rgbOnly | alphaOnly);
}
}
return result;
}
}