在 Java 中设置 BufferedImage alpha 掩码
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/221830/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Set BufferedImage alpha mask in Java
提问by Zarkonnen
I have two BufferedImages I loaded in from pngs. The first contains an image, the second an alpha mask for the image.
我有两个从 png 加载的 BufferedImages。第一个包含图像,第二个包含图像的 alpha 蒙版。
I want to create a combined image from the two, by applying the alpha mask. My google-fu fails me.
我想通过应用 alpha 蒙版从两者创建一个组合图像。我的 google-fu 让我失望了。
I know how to load/save the images, I just need the bit where I go from two BufferedImages to one BufferedImage with the right alpha channel.
我知道如何加载/保存图像,我只需要从两个 BufferedImage 到一个具有正确 alpha 通道的 BufferedImage 的位。
采纳答案by Michael Myers
Your solution could be improved by fetching the RGB data more than one pixel at a time(see http://java.sun.com/javase/6/docs/api/java/awt/image/BufferedImage.html), and by not creating three Color objects on every iteration of the inner loop.
您可以通过一次获取多个像素的 RGB 数据来改进您的解决方案(请参阅http://java.sun.com/javase/6/docs/api/java/awt/image/BufferedImage.html),并通过不会在内循环的每次迭代中创建三个 Color 对象。
final int width = image.getWidth();
int[] imgData = new int[width];
int[] maskData = new int[width];
for (int y = 0; y < image.getHeight(); y++) {
// fetch a line of data from each image
image.getRGB(0, y, width, 1, imgData, 0, 1);
mask.getRGB(0, y, width, 1, maskData, 0, 1);
// apply the mask
for (int x = 0; x < width; x++) {
int color = imgData[x] & 0x00FFFFFF; // mask away any alpha present
int maskColor = (maskData[x] & 0x00FF0000) << 8; // shift red into alpha bits
color |= maskColor;
imgData[x] = color;
}
// replace the data
image.setRGB(0, y, width, 1, imgData, 0, 1);
}
回答by Zarkonnen
Actually, I've figured it out. This is probably not a fastway of doing it, but it works:
其实,我已经想通了。这可能不是一种快速的方法,但它有效:
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
Color c = new Color(image.getRGB(x, y));
Color maskC = new Color(mask.getRGB(x, y));
Color maskedColor = new Color(c.getRed(), c.getGreen(), c.getBlue(),
maskC.getRed());
resultImg.setRGB(x, y, maskedColor.getRGB());
}
}
回答by PhiLho
I played recently a bit with this stuff, to display an image over another one, and to fade an image to gray.
Also masking an image with a mask with transparency (my previous version of this message!).
我最近玩了一些这个东西,将图像显示在另一个图像上,并将图像淡化为灰色。
还使用具有透明度的蒙版来屏蔽图像(我之前版本的此消息!)。
I took my little test program and tweaked it a bit to get the wanted result.
我拿了我的小测试程序并稍微调整了一下以获得想要的结果。
Here are the relevant bits:
以下是相关位:
TestMask() throws IOException
{
m_images = new BufferedImage[3];
m_images[0] = ImageIO.read(new File("E:/Documents/images/map.png"));
m_images[1] = ImageIO.read(new File("E:/Documents/images/mapMask3.png"));
Image transpImg = TransformGrayToTransparency(m_images[1]);
m_images[2] = ApplyTransparency(m_images[0], transpImg);
}
private Image TransformGrayToTransparency(BufferedImage image)
{
ImageFilter filter = new RGBImageFilter()
{
public final int filterRGB(int x, int y, int rgb)
{
return (rgb << 8) & 0xFF000000;
}
};
ImageProducer ip = new FilteredImageSource(image.getSource(), filter);
return Toolkit.getDefaultToolkit().createImage(ip);
}
private BufferedImage ApplyTransparency(BufferedImage image, Image mask)
{
BufferedImage dest = new BufferedImage(
image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = dest.createGraphics();
g2.drawImage(image, 0, 0, null);
AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.DST_IN, 1.0F);
g2.setComposite(ac);
g2.drawImage(mask, 0, 0, null);
g2.dispose();
return dest;
}
The remainder just display the images in a little Swing panel.
Note that the mask image is gray levels, black becoming full transparency, white becoming full opaque.
其余的只是在一个小的 Swing 面板中显示图像。
请注意,蒙版图像是灰度级,黑色变为完全透明,白色变为完全不透明。
Although you have resolved your problem, I though I could share my take on it. It uses a slightly more Java-ish method, using standard classes to process/filter images.
Actually, my method uses a bit more memory (making an additional image) and I am not sure it is faster (measuring respective performances could be interesting), but it is slightly more abstract.
At least, you have choice! :-)
虽然你已经解决了你的问题,但我还是可以分享我对它的看法。它使用稍微更像 Java 的方法,使用标准类来处理/过滤图像。
实际上,我的方法使用了更多的内存(制作额外的图像),我不确定它是否更快(测量各自的性能可能很有趣),但它稍微抽象一些。
至少,你还有选择!:-)
回答by Meyer
I'm too late with this answer, but maybe it is of use for someone anyway. This is a simpler and more efficient version of Michael Myers' method:
我对这个答案为时已晚,但无论如何它可能对某人有用。这是迈克尔迈尔斯方法的一个更简单、更有效的版本:
public void applyGrayscaleMaskToAlpha(BufferedImage image, BufferedImage mask)
{
int width = image.getWidth();
int height = image.getHeight();
int[] imagePixels = image.getRGB(0, 0, width, height, null, 0, width);
int[] maskPixels = mask.getRGB(0, 0, width, height, null, 0, width);
for (int i = 0; i < imagePixels.length; i++)
{
int color = imagePixels[i] & 0x00ffffff; // Mask preexisting alpha
int alpha = maskPixels[i] << 24; // Shift blue to alpha
imagePixels[i] = color | alpha;
}
image.setRGB(0, 0, width, height, imagePixels, 0, width);
}
It reads all the pixels into an array at the beginning, thus requiring only one for-loop. Also, it directly shifts the blue byte to the alpha (of the mask color), instead of first masking the red byte and then shifting it.
它在开始时将所有像素读入一个数组,因此只需要一个 for 循环。此外,它直接将蓝色字节移动到 alpha(遮罩颜色),而不是先屏蔽红色字节然后再移动它。
Like the other methods, it assumes both images have the same dimensions.
与其他方法一样,它假设两个图像具有相同的尺寸。
回答by Thiago Medeiros dos Santos
For those who are using alpha in the original image.
对于那些在原始图像中使用 alpha 的人。
I wrote this code in Koltin, the key point here is that if you have the alpha on your original image you need to multiply these channels.
我在 Koltin 中编写了这段代码,这里的关键是如果原始图像上有 alpha,则需要乘以这些通道。
Koltin Version:
科尔廷版:
val width = this.width
val imgData = IntArray(width)
val maskData = IntArray(width)
for(y in 0..(this.height - 1)) {
this.getRGB(0, y, width, 1, imgData, 0, 1)
mask.getRGB(0, y, width, 1, maskData, 0, 1)
for (x in 0..(this.width - 1)) {
val maskAlpha = (maskData[x] and 0x000000FF)/ 255f
val imageAlpha = ((imgData[x] shr 24) and 0x000000FF) / 255f
val rgb = imgData[x] and 0x00FFFFFF
val alpha = ((maskAlpha * imageAlpha) * 255).toInt() shl 24
imgData[x] = rgb or alpha
}
this.setRGB(0, y, width, 1, imgData, 0, 1)
}
Java version (just translated from Kotlin)
Java 版本(刚从 Kotlin 翻译过来)
int width = image.getWidth();
int[] imgData = new int[width];
int[] maskData = new int[width];
for (int y = 0; y < image.getHeight(); y ++) {
image.getRGB(0, y, width, 1, imgData, 0, 1);
mask.getRGB(0, y, width, 1, maskData, 0, 1);
for (int x = 0; x < image.getWidth(); x ++) {
//Normalize (0 - 1)
float maskAlpha = (maskData[x] & 0x000000FF)/ 255f;
float imageAlpha = ((imgData[x] >> 24) & 0x000000FF) / 255f;
//Image without alpha channel
int rgb = imgData[x] & 0x00FFFFFF;
//Multiplied alpha
int alpha = ((int) ((maskAlpha * imageAlpha) * 255)) << 24;
//Add alpha to image
imgData[x] = rgb | alpha;
}
image.setRGB(0, y, width, 1, imgData, 0, 1);
}