繁体   English   中英

在Java BufferedImage中绘制完全透明的“白色”

[英]Drawing fully transparent “white” in Java BufferedImage

这听起来像是一个奇怪的标题,但是请允许我,这有原因的:

我正在尝试在灰色背景的文本周围产生白光。

为了生成光晕,我创建了一个比文本大的新BufferedImage,然后将白色文本绘制到图像的画布上,并通过ConvolveOp在图像上运行高斯模糊,以期实现以下目的:

在此处输入图片说明

最初,当辉光比文字的灰色背景更暗时,我感到有些惊讶:

在此处输入图片说明

但是经过一番思考,我理解了这个问题:

卷积在每个颜色通道(R,G,B和A)上独立运行以计算模糊图像。 图片的透明背景的颜色值为0x00000000,即完全透明的黑色 因此,当卷积滤镜在图像上运行时,它不仅会混合alpha值,还会将黑色混入白色像素的RGB值。 这就是为什么辉光发暗的原因。

要解决此问题,我需要将图像初始化为0x00FFFFFF,即改为完全透明的白色 ,但是如果我只是设置该颜色并用它填充一个矩形,则它根本不起作用,就像Java所说的“好吧,这是一个完全透明的矩形,你在画画!这不会改变图像的。让我为你优化它。完成。不客气。”

如果我改为将颜色设置为0x01FFFFFF,即几乎完全透明的白色,则它确实绘制了矩形,并且发光看起来很漂亮,除非我最终在其周围出现了一个非常暗的白色框...

有没有一种方法可以在任何地方将图像初始化为0x00FFFFFF?

更新:

我找到了一种方法,但是您可能会发现它不是最优的:

我在图像上绘制了一个不透明的白色矩形,然后在图像上运行RescaleOp ,将所有alpha值都设置为0。这是可行的,但是就性能而言,这可能是一种糟糕的方法。

我可以以某种方式做得更好吗?

PS:我也乐于接受完全不同的建议来创建这种发光效果

初始方法使辉光显得更暗的主要原因是您未使用带有预乘alpha分量的图像。 ConvolveOp的JavaDoc包含一些有关在卷积期间如何处理alpha分量的信息。

您可以使用“几乎完全透明的白色”解决此问题。 但是,或者,您可以简单地使用具有预乘alpha的图像,即类型为TYPE_INT_ARGB_PRE

这是一个MCVE ,它在面板上绘制一些文本,以及一些围绕文本的脉冲发光(删除计时器并设置固定的半径以删除脉冲-我在这里忍不住玩了一点……)。

辉光

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class TextGlowTest
{
    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 TextGlowPanel());
        f.setSize(300,200);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

class TextGlowPanel extends JPanel
{
    private BufferedImage image;
    private int radius = 1;

    TextGlowPanel()
    {
        Timer t = new Timer(50, new ActionListener()
        {
            long startMillis = -1;

            @Override
            public void actionPerformed(ActionEvent e)
            {
                if (startMillis == -1)
                {
                    startMillis = System.currentTimeMillis();
                }
                long d = System.currentTimeMillis() - startMillis;
                double s = d / 1000.0;
                radius = (int)(1 + 15 * (Math.sin(s * 3) * 0.5 + 0.5));
                repaint();
            }
        });
        t.start();
    }

    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        gr.setColor(Color.GRAY);

        int w = getWidth();
        int h = getHeight();
        gr.fillRect(0, 0, w, h);

        if (image == null || image.getWidth() != w || image.getHeight() != h)
        {
            // Must be prmultiplied!
            image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
        }

        Graphics2D g = image.createGraphics();
        Font font = g.getFont().deriveFont(70.0f).deriveFont(Font.BOLD);
        g.setFont(font);

        g.setComposite(AlphaComposite.Src);
        g.setColor(new Color(255,255,255,0));
        g.fillRect(0,0,w,h);

        g.setComposite(AlphaComposite.SrcOver);
        g.setColor(new Color(255,255,255,0));
        g.fillRect(0,0,w,h);

        g.setColor(Color.WHITE);
        g.drawString("Glow!", 50, 100);

        image = getGaussianBlurFilter(radius, true).filter(image, null);
        image = getGaussianBlurFilter(radius, false).filter(image, null);

        g.dispose();

        g = image.createGraphics();
        g.setFont(font);
        g.setColor(Color.BLUE);
        g.drawString("Glow!", 50, 100);
        g.dispose();

        gr.drawImage(image, 0, 0, null);
    }


    // From
    // http://www.java2s.com/Code/Java/Advanced-Graphics/GaussianBlurDemo.htm
    public static ConvolveOp getGaussianBlurFilter(
        int radius, boolean horizontal)
    {
        if (radius < 1)
        {
            throw new IllegalArgumentException("Radius must be >= 1");
        }

        int size = radius * 2 + 1;
        float[] data = new float[size];

        float sigma = radius / 3.0f;
        float twoSigmaSquare = 2.0f * sigma * sigma;
        float sigmaRoot = (float) Math.sqrt(twoSigmaSquare * Math.PI);
        float total = 0.0f;

        for (int i = -radius; i <= radius; i++)
        {
            float distance = i * i;
            int index = i + radius;
            data[index] =
                (float) Math.exp(-distance / twoSigmaSquare) / sigmaRoot;
            total += data[index];
        }

        for (int i = 0; i < data.length; i++)
        {
            data[i] /= total;
        }

        Kernel kernel = null;
        if (horizontal)
        {
            kernel = new Kernel(size, 1, data);
        }
        else
        {
            kernel = new Kernel(1, size, data);
        }
        return new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
    }
}

我发现clearRect应该绘制透明颜色。

g.setBackground(new Color(0x00FFFFFF, true));
g.clearRect(0, 0, img.getWidth(), img.getHeight());

您还应该能够通过直接设置像素数据来强制BufferedImage用透明颜色填充。

public static void forceFill(BufferedImage img, int rgb) {
    for(int x = 0; x < img.getWidth(); x++) {
        for(int y = 0; y < img.getHeight(); y++) {
            img.setRGB(x, y, rgb);
        }
    }
}

它没有明确记录,但我对其进行了测试,并且setRGB似乎接受ARGB值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM