【问题标题】:Java Graphics2D drawImage() and clip(): how to apply antialiasing?Java Graphics2D drawImage() 和 clip():如何应用抗锯齿?
【发布时间】:2018-10-17 02:51:24
【问题描述】:

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

代码:

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);

【问题讨论】:

  • 好的,clip 部分是完全不同的故事。解决起来可能没那么简单。 (一种方法是手动进行剪辑,使用Area,但这是否可行取决于clip 应该如何准确地使用)。我已经勾勒出一种方法,但认为最好在此处基本上撤消编辑(以便 this 问题关于 drawImage 案例),并询问一个问题,特别是关于clip 方法的问题。

标签: java


【解决方案1】:

一种解决方案是在加载图像后模糊图像的边界。您还应该使用RenderingHints.KEY_RENDERINGRenderingHints.VALUE_RENDER_QUALITY

这是最终结果:

下面提供了完整的代码。请注意,它使用Marco13 在此答案中描述的模糊方法:https://stackoverflow.com/a/22744303/4289700

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MultipleGradientPaint;
import java.awt.RadialGradientPaint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class Main {

    private static BufferedImage blurImageBorder(BufferedImage input, double border) {
        int w = input.getWidth();
        int h = input.getHeight();
        BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);

        Graphics2D g = output.createGraphics();
        g.drawImage(input, 0, 0, null);

        g.setComposite(AlphaComposite.DstOut);
        Color c0 = new Color(0x000000FF);

        // Left
        g.setPaint(new GradientPaint(new Point2D.Double(0, border), c0, new Point2D.Double(border, border), c0));
        g.fill(new Rectangle2D.Double(0, border, border, h- border - border));

        // Right
        g.setPaint(new GradientPaint(new Point2D.Double(w - border, border), c0, new Point2D.Double(w, border), c0));
        g.fill(new Rectangle2D.Double(w- border, border, border, h- border - border));

        // Top
        g.setPaint(new GradientPaint(new Point2D.Double(border, 0), c0, new Point2D.Double(border, border), c0));
        g.fill(new Rectangle2D.Double(border, 0, w - border - border, border));

        // Bottom
        g.setPaint(new GradientPaint(new Point2D.Double(border, h - border), c0, new Point2D.Double(border, h), c0));
        g.fill(new Rectangle2D.Double(border, h - border, w - border - border, border));

        final float[] floatArray = new float[]{ 0, 1 };
        final Color[] colorArray = new Color[]{ c0, c0 };

        // Top Left
        g.setPaint(new RadialGradientPaint(new Rectangle2D.Double(0, 0, border + border, border + border),
                floatArray, colorArray, MultipleGradientPaint.CycleMethod.NO_CYCLE));
        g.fill(new Rectangle2D.Double(0, 0, border, border));

        // Top Right
        g.setPaint(new RadialGradientPaint(
                new Rectangle2D.Double(w - border - border, 0, border + border, border + border),
                floatArray, colorArray, MultipleGradientPaint.CycleMethod.NO_CYCLE));
        g.fill(new Rectangle2D.Double(w - border, 0, border, border));

        // Bottom Left
        g.setPaint(new RadialGradientPaint(
                new Rectangle2D.Double(0, h - border - border, border + border, border + border),
                floatArray, colorArray, MultipleGradientPaint.CycleMethod.NO_CYCLE));
        g.fill(new Rectangle2D.Double(0, h - border, border, border));

        // Bottom Right
        g.setPaint(new RadialGradientPaint(
                new Rectangle2D.Double(w - border - border, h - border - border, border + border, border + border),
                floatArray, colorArray, MultipleGradientPaint.CycleMethod.NO_CYCLE));
        g.fill(new Rectangle2D.Double(w - border, h - border, border, border));

        g.dispose();

        return output;
    }

    public static void main(String[] args) throws IOException {
        BufferedImage raw = ImageIO.read(new File("/path/to/picture.jpg"));
        BufferedImage img = blurImageBorder(raw, 1);

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

                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.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

                g2d.drawImage(img, 0, 0, this);
                g2d.fill(new Rectangle(-100, 100, 100, 100));
            }
        });

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

【讨论】:

  • 感谢您的回答,虽然这是一种变通方法,但对于clip方法仍然无效,并且在实际应用中可能会造成额外的内存开销。所以我需要找到一个更好的解决方案。
【解决方案2】:

对于图像,将图像绘制成另一张大 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 操作“本质上”是非常困难的(我假设它最终将由硬件中的 Stencil Buffer 之类的东西处理)。

解决此问题的一种方法可以手动进行剪辑。所以不要这样做

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和手动裁剪的区别如下:

还有一个特写:

还有代码:

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

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;
        }
    }

}

【讨论】:

  • 感谢您的回答,虽然这是一种变通方法,但对于clip方法仍然无效,并且在实际应用中可能会造成额外的内存开销。所以我需要找到一个更好的解决方案。
  • @Yii.Guxing 如果有其他要求,请在问题中说明。编辑问题以解释 clip 方法与此处的相关性。
  • 感谢您的补充回复,您的解决方案给了我很多启发,我们可以使用TexturePaint类和fill()方法来解决这个问题。但它带来了一个新问题:渲染出来的图像有点模糊。您对此有什么建议吗?
猜你喜欢
  • 2014-02-18
  • 1970-01-01
  • 2013-04-11
  • 1970-01-01
  • 1970-01-01
  • 2016-06-28
  • 2011-07-05
  • 2014-03-19
  • 1970-01-01
相关资源
最近更新 更多