当前位置:  开发笔记 > 编程语言 > 正文

Java Graphics2D - 使用渐变不透明度绘制图像

如何解决《JavaGraphics2D-使用渐变不透明度绘制图像》经验,为你挑选了1个好方法。

使用Graphics2d,我试图BufferedImage在背景图像上绘制.在这张图片的任意一点,我想在绘制的图像中"切出一个圆孔"让背景显示出来.

我希望这个洞不是一个坚固的形状,而是一个渐变.换句话说,BufferedImage应该具有与其距孔中心的距离成比例的α/不透明度.

我对Graphics2d渐变有点熟悉AlphaComposite,但有没有办法将它们结合起来?

是否有(不是非常昂贵)的方式来实现这种效果?



1> Marco13..:

这可以通过RadialGradientPaint适当的方法解决AlphaComposite.

以下是MCVE,展示了如何做到这一点.它使用与他的答案中使用的user1803551相同的图像,因此屏幕截图看起来(几乎)相同.但是这个添加了一个MouseMotionListener允许您通过将当前鼠标位置传递给updateGradientAt方法来移动孔周围,在该方法中实际创建所需图像:

它首先用原始图像填充图像

然后它创建一个RadialGradientPaint,在中心有一个完全不透明的颜色,在边框(!)有一个完全透明的颜色.这可能是违反直觉的,但目的是从现有图像中"切出"这个洞,这是通过下一步完成的:

一个AlphaComposite.DstOut被分配到Graphics2D.这个导致α值的"反转",如公式中所示

Ar = Ad*(1-As)
Cr = Cd*(1-As)

where r代表"result",s代表"source",d代表"destination"

结果是在所需位置具有径向渐变透明度的图像,在中心处完全透明并且在边界处完全不透明(!).的这种组合PaintComposite然后被用于与尺寸和孔的填充坐标的椭圆形.(人们也可以fillRect打电话,填写整个图像 - 它不会改变结果).

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class TransparentGradientInImage
{
    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);

        TransparentGradientInImagePanel p =
            new TransparentGradientInImagePanel();
        f.getContentPane().add(p);
        f.setSize(800, 600);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

}

class TransparentGradientInImagePanel extends JPanel
{
    private BufferedImage background;
    private BufferedImage originalImage;
    private BufferedImage imageWithGradient;

    TransparentGradientInImagePanel()
    {
        try
        {
            background = ImageIO.read(
                new File("night-sky-astrophotography-1.jpg"));
            originalImage = convertToARGB(ImageIO.read(new File("7bI1Y.jpg")));
            imageWithGradient = convertToARGB(originalImage);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        addMouseMotionListener(new MouseAdapter()
        {
            @Override
            public void mouseMoved(MouseEvent e)
            {
                updateGradientAt(e.getPoint());
            }
        });
    }


    private void updateGradientAt(Point point)
    {
        Graphics2D g = imageWithGradient.createGraphics();
        g.drawImage(originalImage, 0, 0, null);

        int radius = 100;
        float fractions[] = { 0.0f, 1.0f };
        Color colors[] = { new Color(0,0,0,255), new Color(0,0,0,0) };
        RadialGradientPaint paint = 
            new RadialGradientPaint(point, radius, fractions, colors);
        g.setPaint(paint);

        g.setComposite(AlphaComposite.DstOut);
        g.fillOval(point.x - radius, point.y - radius, radius * 2, radius * 2);
        g.dispose();
        repaint();
    }

    private static BufferedImage convertToARGB(BufferedImage image)
    {
        BufferedImage newImage =
            new BufferedImage(image.getWidth(), image.getHeight(),
                BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = newImage.createGraphics();
        g.drawImage(image, 0, 0, null);
        g.dispose();
        return newImage;
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.drawImage(background, 0, 0, null);
        g.drawImage(imageWithGradient, 0, 0, null);
    }
}

您可以与玩fractionscolorsRadialGradientPaint,以达到不同的效果.例如,这些价值......

float fractions[] = { 0.0f, 0.1f, 1.0f };
Color colors[] = { 
    new Color(0,0,0,255), 
    new Color(0,0,0,255), 
    new Color(0,0,0,0) 
};

导致一个小而透明的孔,有一个大而柔软的"电晕":

TransparentGradientInImage02.png

而这些价值观

float fractions[] = { 0.0f, 0.9f, 1.0f };
Color colors[] = { 
    new Color(0,0,0,255), 
    new Color(0,0,0,255), 
    new Color(0,0,0,0) 
};

造成一个大而清晰透明的中心,带有一个小"电晕":

TransparentGradientInImage01.png

RadialGradientPaintJavaDoc中有一些例子,可以帮助找到所需的值.


我发布的一些相关问题(类似)答案:

Java2D Alpha Mapping图像

如何在Java中进行2D阴影投射?


编辑回答有关评论中提出的表现的问题

如何将性能问题Paint/ Composite方法比作getRGB/ setRGB做法的确有趣.根据我以前的经验,我的直觉是第一个比第二个快得多,因为,一般来说,getRGB/ setRGB往往很慢,并且内置机制被高度优化(在某些情况下,可能甚至是硬件加速).

事实上,Paint/ Composite办法比快getRGB/ setRGB方法,但并不如我预期的多.以下当然不是一个非常深刻的"基准"(我没有使用Caliper或JMH),但应该对实际性能给出一个很好的估计:

// NOTE: This is not really a sophisticated "Benchmark", 
// but gives a rough estimate about the performance

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.image.BufferedImage;

public class TransparentGradientInImagePerformance
{
    public static void main(String[] args)
    {
        int w = 1000;
        int h = 1000;
        BufferedImage image0 = new BufferedImage(w, h,
            BufferedImage.TYPE_INT_ARGB);
        BufferedImage image1 = new BufferedImage(w, h,
            BufferedImage.TYPE_INT_ARGB);

        long before = 0;
        long after = 0;
        int runs = 100;
        for (int radius = 100; radius <=400; radius += 10)
        {
            before = System.nanoTime();
            for (int i=0; i r)
                    continue;
                int argb = imgA.getRGB(x, y);
                int a = (argb >> 24) & 255;
                double factor = distance / r;
                argb = (argb - (a << 24) + ((int) (a * factor) << 24));
                imgA.setRGB(x, y, argb);
            }
        }
    }

    private static void updateGradientAt(BufferedImage originalImage,
        BufferedImage imageWithGradient, Point point, int radius)
    {
        Graphics2D g = imageWithGradient.createGraphics();
        g.drawImage(originalImage, 0, 0, null);

        float fractions[] = { 0.0f, 1.0f };
        Color colors[] = { new Color(0, 0, 0, 255), new Color(0, 0, 0, 0) };
        RadialGradientPaint paint = new RadialGradientPaint(point, radius,
            fractions, colors);
        g.setPaint(paint);

        g.setComposite(AlphaComposite.DstOut);
        g.fillOval(point.x - radius, point.y - radius, radius * 2, radius * 2);
        g.dispose();
    }
}

我的电脑上的时间顺序是这样的

...
Radius 390 with getRGB/setRGB: 1518.224404
Radius 390 with paint          764.11017
Radius 400 with getRGB/setRGB: 1612.854049
Radius 400 with paint          794.695199

表明该Paint/ Composite方法是大约两倍一样快getRGB/ setRGB方法.除了性能之外,Paint/ Composite还有一些其他优点,主要RadialGradientPaint是上面描述的可能的参数化,这是我更喜欢这个解决方案的原因.

推荐阅读
依然-狠幸福
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有