Java Graphics2D - draw an image with gradient opacity

Using Graphics2d, I am trying to draw a BufferedImageover the background image. At an arbitrary point in this image, I would like to “cut a circular hole” in the drawn image to skip the background.

I would like the hole not to be solid, but rather a gradient. In other words, each pixel in BufferedImagemust have alpha / opacity proportional to its distance from the center of the hole.

I am a little familiar with gradients Graphics2dand AlphaComposite, but is there any way to combine them?

Is there a (not insanely expensive) way to achieve this effect?

+5
source share
2 answers

This can be solved with the help of RadialGradientPaintand appropriate AlphaComposite.

Below is the MCVE , which shows how this can be done. He uses the same images as user1803551 used in his answer , so the screenshot will look (almost) the same. But this adds MouseMotionListener, which allows you to move the hole around, passing the current mouse position to the method updateGradientAtwhere the actual creation of the desired image takes place:

  • First it fills the image with the original image
  • RadialGradientPaint, (!). , , "" , :
  • AlphaComposite.DstOut Graphics2D. "" -,

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

    r "", s "", d ""

, (!). Paint Composite . ( 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);
    }
}

fractions colors RadialGradientPaint . , ...

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

RadialGradientPaint JavaDocs , .


, () :


EDIT ,

, 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<runs; i++)
            {
                transparitize(image0, w/2, h/2, radius);
            }
            after = System.nanoTime();
            System.out.println(
                "Radius "+radius+" with getRGB/setRGB: "+(after-before)/1e6);

            before = System.nanoTime();
            for (int i=0; i<runs; i++)
            {
                updateGradientAt(image0, image1, new Point(w/2, h/2), radius);
            }
            after = System.nanoTime();
            System.out.println(
                "Radius "+radius+" with paint          "+(after-before)/1e6);
        }
    }

    private static void transparitize(
        BufferedImage imgA, int centerX, int centerY, int r)
    {

        for (int x = centerX - r; x < centerX + r; x++)
        {
            for (int y = centerY - r; y < centerY + r; y++)
            {
                double distance = Math.sqrt(
                    Math.pow(Math.abs(centerX - x), 2) +
                    Math.pow(Math.abs(centerY - y), 2));
                if (distance > 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, , , .

+6

, "" . , , , , ( , , , , ).

:

enter image description here

public class Paint extends JPanel {

    BufferedImage imgA;
    BufferedImage bck;

    Paint() {

        BufferedImage img = null;
        try {
            img = ImageIO.read(getClass().getResource("img.jpg")); // images linked below
            bck = ImageIO.read(getClass().getResource("bck.jpg"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        imgA = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = imgA.createGraphics();
        g2d.drawImage(img, 0, 0, null);
        g2d.dispose();

        transparitize(200, 100, 80);
    }

    private void transparitize(int centerX, int centerY, int r) {

        for (int x = centerX - r; x < centerX + r; x++) {
            for (int y = centerY - r; y < centerY + r; y++) {
                double distance = Math.sqrt(Math.pow(Math.abs(centerX - x), 2)
                                            + Math.pow(Math.abs(centerY - y), 2));
                if (distance > 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);
            }
        }
    }

    @Override
    protected void paintComponent(Graphics g) {

        super.paintComponent(g);
        g.drawImage(bck, 0, 0, null);
        g.drawImage(imgA, 0, 0, null);
    }

    @Override
    public Dimension getPreferredSize() {

        return new Dimension(bck.getWidth(), bck.getHeight()); // because bck is larger than imgA, otherwise use Math.max
    }
}

, ARGB getRGB, ( - ) setRGB. , . , , , (: centerX - r , distance > r ).

:

  • , - , , .
  • - int, , 2-3 .
  • .

:

+2

Source: https://habr.com/ru/post/1017185/


All Articles