首页 > 解决方案 > Java Graphics2D drawImage() 和 clip():如何应用抗锯齿?

问题描述

Java Graphics2D的and方法BufferedImage绘制的边缘有锯齿,如何应用抗锯齿?drawImageclip

在此处输入图像描述

编码:

BufferedImage img = ImageIO.read(new File("D:\\Pictures\\U\\U\\3306231465660486.jpg"));

JFrame frame = new JFrame();
frame.add(new JPanel() {
    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g2d.setColor(Color.RED);
        g2d.drawLine(10, 10, 300, 100);


        g2d.translate(50, 200);
        g2d.rotate(Math.toRadians(30), getWidth() / 2.0, getHeight() / 2.0);
        g2d.drawImage(img, 0, 0, this);
        g2d.clip(new Rectangle(-110, 110, 80, 110));
        g2d.fill(new Rectangle(-100, 100, 100, 100));
    }
});

frame.setSize(600, 600);
frame.setLocationRelativeTo(null);
frame.setVisible(true);

标签: java

解决方案


对于图像,将图像绘制成另一张大 2 像素的图像就足够了,然后使用双线性插值绘制得到的图像。所以你可以通过这样的方法传递你的图像:

private static BufferedImage addBorder(BufferedImage image)
{
    BufferedImage result = new BufferedImage(
        image.getWidth() + 2, image.getHeight() + 2, 
        BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = result.createGraphics();
    g.drawImage(image, 1, 1, null);
    g.dispose();
    return result;
}

结果将是这样的:

旋转图像边框

这是 MCVE,包括设置..._INTERPOLATION_BILINEAR渲染提示的行:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
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 ImageBorderAntialiasing
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui()
    {
        //BufferedImage img = loadUnchecked("7bI1Y.jpg");
        BufferedImage img = addBorder(loadUnchecked("7bI1Y.jpg"));

        JFrame frame = new JFrame();
        frame.add(new JPanel()
        {
            @Override
            protected void paintComponent(Graphics g)
            {
                Graphics2D g2d = (Graphics2D) g;
                g2d.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);

                g2d.setRenderingHint(
                    RenderingHints.KEY_INTERPOLATION,
                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);

                g2d.setColor(Color.RED);
                g2d.drawLine(10, 10, 300, 100);

                g2d.translate(50, 200);
                g2d.rotate(Math.toRadians(30), 
                    getWidth() / 2.0, getHeight() / 2.0);
                g2d.drawImage(img, 0, 0, this);
                g2d.fill(new Rectangle(-100, 100, 100, 100));
            }
        });

        frame.setSize(600, 600);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private static BufferedImage addBorder(BufferedImage image)
    {
        BufferedImage result = new BufferedImage(
            image.getWidth() + 2, image.getHeight() + 2, 
            BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = result.createGraphics();
        g.drawImage(image, 1, 1, null);
        g.dispose();
        return result;
    }

    private static BufferedImage loadUnchecked(String fileName)
    {
        try
        {
            return ImageIO.read(new File(fileName));
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return null;
        }
    }

}

回答此问题后,问题已更新为还询问该clip方法


对操作结果进行抗锯齿clip可能会更加困难。该clip操作“本质上”非常困难(我假设它最终将由硬件中的模板缓冲区之类的东西处理)。

解决此问题的一种方法手动进行剪辑。所以而不是做

g2d.clip(new Rectangle(-110, 110, 80, 110));
g2d.fill(new Rectangle(-100, 100, 100, 100));

你可以做类似的事情

Shape clip = new Rectangle(-110, 110, 80, 110);
Shape rectangleA = new Rectangle(-100, 100, 100, 100);  
g2d.fill(clip(clip, rectangleA));            

clip方法用于手动计算形状的交集。

注意:计算两个形状的交集可能相当昂贵。如果这成为一个问题,人们可能不得不修改方法。但另一方面:我已经大量从事 Swing 编程约 20 年了,根本不记得曾经使用过这种Graphics2D#clip方法......

使用和手动剪辑之间的区别Graphics2D#clip如下所示:

快船

和特写:

ClipperZoom

还有代码:

(它不再包含图像部分,因为这些问题完全不相关......)

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Area;

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

public class ClippedDrawingAntialiasing
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui()
    {
        JFrame frame = new JFrame();
        frame.getContentPane().setLayout(new GridLayout(1,2));
        frame.getContentPane().add(new JPanel()
        {
            @Override
            protected void paintComponent(Graphics g)
            {
                Graphics2D g2d = (Graphics2D) g;
                g2d.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);

                g2d.setColor(Color.RED);
                g2d.drawLine(10, 10, 300, 100);

                g2d.translate(50, 200);
                g2d.rotate(Math.toRadians(30), 
                    getWidth() / 2.0, getHeight() / 2.0);

                g2d.clip(new Rectangle(-110, 110, 80, 110));

                g2d.fill(new Rectangle(-100, 100, 100, 100));            

                g2d.setColor(Color.BLUE);
                g2d.fill(new Rectangle(-60, 120, 60, 170));            
            }
        });
        frame.getContentPane().add(new JPanel()
        {
            @Override
            protected void paintComponent(Graphics g)
            {
                Graphics2D g2d = (Graphics2D) g;
                g2d.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);

                g2d.setColor(Color.RED);
                g2d.drawLine(10, 10, 300, 100);

                g2d.translate(50, 200);
                g2d.rotate(Math.toRadians(30), 
                    getWidth() / 2.0, getHeight() / 2.0);

                Clipper clipper = 
                    new Clipper(new Rectangle(-110, 110, 80, 110));

                g2d.fill(clipper.clip(new Rectangle(-100, 100, 100, 100)));            

                g2d.setColor(Color.BLUE);
                g2d.fill(clipper.clip(new Rectangle(-60, 120, 60, 170)));            
            }
        });

        frame.setSize(1200, 600);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private static class Clipper 
    {
        private final Shape shape;
        Clipper(Shape shape)
        {
            this.shape = shape;
        }

        Shape clip(Shape other)
        {
            Area a = new Area(shape);
            a.intersect(new Area(other));
            return a;
        }
    }

}

推荐阅读