【问题标题】:Impact of TYPE_INT_ARGB_PRETYPE_INT_ARGB_PRE 的影响
【发布时间】:2015-03-11 06:41:59
【问题描述】:

我在使用 ConvolveOp 时遇到了一些问题,可以通过将我正在使用的 BufferedImage 的 imageType 设置为 TYPE_INT_ARGB_PRE (see related SO answer here) 来解决这些问题。

不幸的是,我并不完全理解选择这种不同的 imageType 的所有含义,而且我似乎也找不到好的参考,所以让我在这里尝试一下:

将 BufferedImage 的 imageType 从 TYPE_INT_ARGB 更改为 TYPE_INT_ARGB_PRE 会影响哪些绘图操作?只是BufferedImageOps吗?或者它是否会影响图像的Graphics 对象上的任何绘制命令,或者如果图像被绘制到不同的 Graphics 对象上时的渲染方式?

【问题讨论】:

    标签: java bufferedimage premultiplied-alpha


    【解决方案1】:

    这基本上取决于绘画算法是否考虑到图像是否使用预乘alpha的信息。

    正如评论中已经指出的那样:在大多数情况下,结果是相同的 - 至少对于基本的绘图操作:无论您是将“非预乘”图像绘制成预乘图像,反之亦然,都会不会影响结果,因为差异是在内部处理的。

    BufferedImageOps 是一种特殊情况。 JavaDoc cmets 明确说明了如何处理 alpha 通道,并且传入错误类型的图像可能会导致您链接到的问题中描述的不良结果。

    很难确定他们决定以这种方式实施BufferedImageOp 的“”原因。但这里有一个(有点模糊的)陈述:当对单个源的像素进行操作(和组合)时,并且这些像素具有不同的 alpha 值,对 alpha 通道的处理可能会变得繁琐。 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;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2017-11-21
      • 1970-01-01
      • 1970-01-01
      • 2011-05-04
      • 1970-01-01
      • 2016-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多