我已经遇到了ConvolveOp的一些问题,可以通过将BufferedImage我的TYPE_INT_ARGB_PRE的imageType设置为see related SO answer here来解决BufferedImageOps Graphics 3}})。
不幸的是,我不完全理解选择这个不同的imageType的所有含义,我似乎也找不到好的参考,所以让我试试这里:
将BufferedImage的imageType从TYPE_INT_ARGB更改为TYPE_INT_ARGB_PRE会影响哪些绘图操作?只是{{3}}吗?或者它是否会影响图像的{{3}}对象上的任何绘制命令,或者它是否被绘制到另一个图形对象上时渲染图像的方式?
答案 0 :(得分:1)
这主要取决于绘画算法是否考虑了图像是否使用预乘alpha的信息。
正如评论中已经指出的那样:在大多数情况下,结果将是相同的 - 至少对于基本的绘图操作:是否正在绘制"非预乘的"将图像转换为预乘的图像,反之亦然,不会影响结果,因为差异是在内部处理的。
特殊情况是BufferedImageOp
。 JavaDoc注释明确说明如何处理alpha通道,并且传入错误类型的图像可能会导致您链接到的问题中描述的不良结果。
很难确定" "他们决定以这种方式实施BufferedImageOp
的原因。但是这里的一个(有点模糊)陈述是:当对单个源的像素进行操作(和组合),并且这些像素具有不同的α值时,对α通道的处理可能变得繁琐。使用alpha通道发生并不总是非常明显。
例如,想象一下这些像素的堆栈(此处为ARGB,具有浮点值):
[1.00, 1.00, 0.00, 0.00] // 100% red, 100% alpha
[0.00, 0.00, 0.00, 0.00] // black, 0% alpha
[0.00, 0.00, 0.00, 0.00] // black, 0% alpha
现在,您想对这些像素进行卷积(如您链接的问题)。然后内核可以
[0.33...]
[0.33...],
[0.33...]
这意味着结果的中心像素应该是"平均值"所有像素(忽略边框 - 与ConvolveOp#EDGE_ZERO_FILL
大致相同)。
然后卷积将平等对待所有频道。对于非预乘图像,这意味着结果像素是深红色,低不透明度:
[0.33, 0.33, 0.00, 0.00]
对于预乘图像,假设组件与其alpha值相乘。在这种情况下,结果像素将是完全红色,具有相同的不透明度:
[0.33, 1.00, 0.00, 0.00]
在这背后做数学是乏味的。事实上,我手动操作太繁琐了 - 所以这是一个例子:
和相应的代码
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.Locale;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PremultipliedAlphaTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new PremultipliedAlphaTestPanel());
f.setSize(550,500);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class PremultipliedAlphaTestPanel extends JPanel
{
@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
BufferedImage imageS = createImage(BufferedImage.TYPE_INT_ARGB);
BufferedImage imageP = createImage(BufferedImage.TYPE_INT_ARGB_PRE);
Kernel kernel = new Kernel(1, 3,
new float[]{ 1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f });
ConvolveOp op = new ConvolveOp(kernel, ConvolveOp.EDGE_ZERO_FILL, null);
BufferedImage resultS = op.filter(imageS, null);
BufferedImage resultP = op.filter(imageP, null);
g.setColor(Color.BLACK);
g.setFont(new Font("Monospaced", Font.PLAIN, 12));
g.drawString("Straight:", 10, 40);
print(g, 2, 1, imageS.getRGB(0, 0));
print(g, 2, 2, imageS.getRGB(0, 1));
print(g, 2, 3, imageS.getRGB(0, 2));
print(g, 7, 2, resultS.getRGB(0, 1));
g.drawString("Premultiplied:", 10, 240);
print(g, 2, 5, imageP.getRGB(0, 0));
print(g, 2, 6, imageP.getRGB(0, 1));
print(g, 2, 7, imageP.getRGB(0, 2));
print(g, 7, 6, resultP.getRGB(0, 1));
g.scale(50, 50);
g.drawImage(imageS, 1, 1, null);
g.drawImage(resultS, 6, 1, null);
g.drawImage(imageP, 1, 5, null);
g.drawImage(resultP, 6, 5, null);
}
private static void print(Graphics2D g, int px, int py, int argb)
{
g.drawString(stringFor(argb), px*50+5, py*50+25);
}
private static String stringFor(int argb)
{
int a = (argb >> 24) & 0xFF;
int r = (argb >> 16) & 0xFF;
int g = (argb >> 8) & 0xFF;
int b = (argb ) & 0xFF;
float fa = a / 255.0f;
float fr = r / 255.0f;
float fg = g / 255.0f;
float fb = b / 255.0f;
return String.format(Locale.ENGLISH,
"%4.2f %4.2f %4.2f %4.2f", fa, fr, fg, fb);
}
private static BufferedImage createImage(int type)
{
BufferedImage b = new BufferedImage(1,3, type);
Graphics2D g = b.createGraphics();
g.setColor(new Color(1.0f,0.0f,0.0f,1.0f));
g.fillRect(0, 0, 1, 1);
g.setColor(new Color(0.0f,0.0f,0.0f,0.0f));
g.fillRect(0, 1, 1, 1);
g.setColor(new Color(0.0f,0.0f,0.0f,0.0f));
g.fillRect(0, 2, 1, 1);
g.dispose();
return b;
}
}