【问题标题】:Drawing a nice circle in Java用Java画一个漂亮的圆圈
【发布时间】:2013-01-20 08:10:29
【问题描述】:

我正在使用 Java 图形,但我不断得到“丑陋”的圆圈。

这就是我的 Java 程序所做的

这是在 Matlab 中制作的同样的东西

我认为很明显,Java 不像 Matlab 那样“漂亮”,尤其是在圆圈的边缘。请注意,这与分辨率无关......这些图像实际上大小相同。另请注意,我已经在设置渲染提示。

这里有一个独立的 Main 函数,您可以运行它来测试它。

package test;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;

public class SimplePaint02 {

    private static final int LINE_THICKNESS = 4;
    private static final int LINE_GAP = 10;
    private Color lineColor = Color.red;

    public static void main(String[] args) {
        new SimplePaint02();
    }

    public SimplePaint02() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(100, 100);
        }

        @Override
        public void paintComponent(Graphics g) {

            int radius = 50;
            BufferedImage buffer = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = buffer.createGraphics();
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);

            Ellipse2D circle = new Ellipse2D.Float(0, 0, radius,radius);
            Shape clip = g2d.getClip();
            g2d.setClip(circle);
            AffineTransform at = g2d.getTransform();

            g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(45),radius / 2, radius / 2));

            int gap = LINE_GAP;

            g2d.setColor(Color.WHITE);
            g2d.fill(circle);

            g2d.setColor(lineColor);
            //g2d.setStroke(new BasicStroke(LINE_THICKNESS));
            for (int index = 0; index < 10; index++) {
                int x1 = index*gap-(LINE_THICKNESS/2);
                int y1 = 0;
                int x2 = index*gap+(LINE_THICKNESS/2);
                int y2 = radius;
                int width = x2 - x1;
                int height = y2 - y1;

                g2d.fillRect(x1, y1, width, height);
                //g2d.drawLine(index * gap, 0, index * gap, getRadius());
            }

            g2d.setTransform(at);
            g2d.setClip(clip);
            g2d.dispose();
            g.drawImage(buffer, 0, 0, this);
        }

    }

}

【问题讨论】:

标签: java graphics awt geometry java-2d


【解决方案1】:

编辑:请参阅下面的 Code Guy 的答案以获得解决方案。这被标记为正确,因为最初是乔伊·罗汉(Joey Rohan)发现的!


当我尝试同样的事情时,我得到了平滑的边缘:

  g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,    RenderingHints.VALUE_ANTIALIAS_ON);

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;

public class DrawSmoothCircle {
    public static void main(String[] argv) throws Exception {
        BufferedImage bufferedImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = bufferedImage.createGraphics();

        g2d.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setPaint(Color.green);
        g2d.fillOval(10, 10, 50, 50);
        g2d.dispose();

        ImageIO.write(bufferedImage, "png", new File("e:\\newimage.png"));
    }
}

更新:

经过大量搜索:

代码没有错,但是,

不幸的是,Java 2D(或至少 Sun 当前的实现)不支持“软剪辑”。

但也有剪辑的技巧: 关注This link,你可以达到你的要求。

(另外,我有一个平滑的边缘,因为我没有使用剪辑的东西,在我的上图中)

【讨论】:

  • 线路去哪儿了?如果你运行代码,你会得到一个带线条的圆圈。
  • 不,我的意思是不要用中间线。好的,我会试试的。
  • 是的,没有线条也能正常工作。制作线条的过程中的某些东西正在这样做。
  • 问题实际上不是在绘制形状时,而是在填充或绘制到Clip时。
  • @AndrewThompson 我明白了。那么你认为有另一种方法可以在不使用剪辑的情况下绘制这个形状吗?
【解决方案2】:

这就是答案。我改编了this site 的大部分代码。看看:

代码如下:

public void paintComponent(Graphics g) {

        // Create a translucent intermediate image in which we can perform
        // the soft clipping
        GraphicsConfiguration gc = ((Graphics2D) g).getDeviceConfiguration();
        BufferedImage img = gc.createCompatibleImage(getWidth(), getHeight(), Transparency.TRANSLUCENT);
        Graphics2D g2 = img.createGraphics();

        // Clear the image so all pixels have zero alpha
        g2.setComposite(AlphaComposite.Clear);
        g2.fillRect(0, 0, getWidth(), getHeight());

        // Render our clip shape into the image.  Note that we enable
        // antialiasing to achieve the soft clipping effect.  Try
        // commenting out the line that enables antialiasing, and
        // you will see that you end up with the usual hard clipping.
        g2.setComposite(AlphaComposite.Src);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(Color.WHITE);
        g2.fillOval(0, 0, getRadius(), getRadius());

        // Here's the trick... We use SrcAtop, which effectively uses the
        // alpha value as a coverage value for each pixel stored in the
        // destination.  For the areas outside our clip shape, the destination
        // alpha will be zero, so nothing is rendered in those areas.  For
        // the areas inside our clip shape, the destination alpha will be fully
        // opaque, so the full color is rendered.  At the edges, the original
        // antialiasing is carried over to give us the desired soft clipping
        // effect.
        g2.setComposite(AlphaComposite.SrcAtop);
        g2.setColor(lineColor);
        int gap = LINE_GAP;
        AffineTransform at = g2.getTransform();

        g2.setTransform(AffineTransform.getRotateInstance(Math.toRadians(45),getRadius() / 2, getRadius() / 2));

        for (int index = 0; index < 10; index++) {
            int x1 = index*gap-(LINE_THICKNESS/2);
            int y1 = 0;
            int x2 = index*gap+(LINE_THICKNESS/2);
            int y2 = getRadius();
            int width = x2 - x1;
            int height = y2 - y1;

            g2.fillRect(x1, y1, width, height);
        }

        g2.setTransform(at);
        g2.dispose();

        // Copy our intermediate image to the screen
        g.drawImage(img, 0, 0, null);
    }

【讨论】:

  • 很高兴你明白了 :)+1
【解决方案3】:

更新

好的。那么这个想法是根本不使用剪裁,而是通过相互减去面积来制作剪裁的形状。

import java.awt.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;

public class SimplePaint02 {

    private static final int LINE_THICKNESS = 4;
    private static final int LINE_GAP = 10;
    private Color lineColor = Color.red;

    public static void main(String[] args) {
        new SimplePaint02();
    }

    public SimplePaint02() {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        int radius = 75;

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(radius, radius);
        }

        @Override
        public void paintComponent(Graphics g) {


            Ellipse2D circle = new Ellipse2D.Float(0, 0, radius, radius);
            Area lines = new Area();

            int gap = LINE_GAP;

            for (int index = 0; index < 10; index++) {
                int x1 = index * gap - (LINE_THICKNESS / 2);
                int y1 = 0;
                int x2 = index * gap + (LINE_THICKNESS / 2);
                int y2 = radius;
                int width = x2 - x1;
                int height = y2 - y1;

                Shape lineShape = new Rectangle2D.Double(x1, y1, width, height);
                lines.add(new Area(lineShape));

                //g3d.fillRect(x1, y1, width, height);
                //g2d.drawLine(index * gap, 0, index * gap, getRadius());
            }
            //g2d.setClip(circle);

            Area circleNoLines = new Area(circle);
            circleNoLines.subtract(lines);

            Area linesCutToCircle = new Area(circle);
            linesCutToCircle.subtract(circleNoLines);

            //g2d.setTransform(at);
            BufferedImage buffer = new BufferedImage(radius * 2, radius * 2, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = buffer.createGraphics();

            RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(45), radius / 2, radius / 2));
            g2d.setRenderingHints(rh);
            g2d.setColor(Color.ORANGE);
            g2d.fill(linesCutToCircle);
            g2d.setColor(Color.RED);
            g2d.fill(circleNoLines);

            g2d.dispose();
            g.drawImage(buffer, 0, 0, this);
        }
    }
}

旧代码

部分问题是渲染操作通常不适用于Clip,尽管它们会在绘制时应用于Shape。我通常通过(最后)绘制Shape 本身来解决这个问题。例如

这里使用 1.5 像素的 BasicStroke 表示红色圆圈 - 平滑由 Clip 产生的粗糙边缘。

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;

public class SimplePaint02 {

    private static final int LINE_THICKNESS = 4;
    private static final int LINE_GAP = 10;
    private Color lineColor = Color.red;

    public static void main(String[] args) {
        new SimplePaint02();
    }

    public SimplePaint02() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        int radius = 75;
        
        @Override
        public Dimension getPreferredSize() {
            return new Dimension((int)(1.1*radius), (int)(1.1*radius));
        }

        @Override
        public void paintComponent(Graphics g) {

            BufferedImage buffer = new BufferedImage(radius*2, radius*2, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = buffer.createGraphics();
            
            RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            rh.put(RenderingHints.KEY_DITHERING,RenderingHints.VALUE_DITHER_ENABLE);
            rh.put(RenderingHints.KEY_COLOR_RENDERING,RenderingHints.VALUE_COLOR_RENDER_QUALITY);
            g2d.setRenderingHints(rh);

            Ellipse2D circle = new Ellipse2D.Float(0, 0, radius,radius);
            Shape clip = g2d.getClip();
            g2d.setClip(circle);
            AffineTransform at = g2d.getTransform();

            g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(45),radius / 2, radius / 2));

            int gap = LINE_GAP;

            g2d.setColor(Color.WHITE);
            g2d.fill(circle);

            g2d.setColor(lineColor);
            //g2d.setStroke(new BasicStroke(LINE_THICKNESS));
            for (int index = 0; index < 10; index++) {
                int x1 = index*gap-(LINE_THICKNESS/2);
                int y1 = 0;
                int x2 = index*gap+(LINE_THICKNESS/2);
                int y2 = radius;
                int width = x2 - x1;
                int height = y2 - y1;

                g2d.fillRect(x1, y1, width, height);
                //g2d.drawLine(index * gap, 0, index * gap, getRadius());
            }

            g2d.setTransform(at);
            g2d.setClip(clip);
            g2d.setClip(null);
            g2d.setStroke(new BasicStroke(1.5f));
            g2d.draw(circle);
            g2d.dispose();
            g.drawImage(buffer, 0, 0, this);
        }
    }
}

【讨论】:

  • 现在的轮廓确实使它看起来很平滑。但是我不能用线条在我的圆圈中包含轮廓。你有没有画出这个轮廓的解决方案?
  • 啊.. 挑剔,挑剔(我不得不佩服它;)。查看更新。
  • 不客气。 :) SrcAtop 似乎也很好用。
【解决方案4】:

我使用 drawPolygon 方法通过生成具有建议半径的圆的圆周上的大多数点的数组来绘制圆。 代码:

           import java.awt.*;
           import java.applet.*;

            /*<applet code="OnlyCircle" width=500 height=500>
                 </applet>*/

          public class OnlyCircle extends Applet{

            public void paint(Graphics g){


              int r=200;//radius
              int x1=250;//center x coordinate
              int y1=250;//center y coordinate
              double x2,y2;
              double a=0;
              double pi=3.14159;
              int count=0; 
              int i=0;
              int f=0;
              int[] x22=new int[628319];
              int[] y22=new int[628319];

             while(a<=2*pi&&i<628319&&f<628319)
                  {
                   double k=Math.cos(a);
                   double l=Math.sin(a);
                     x2=x1+r*k;
                     y2=y1+r*l;
                  x22[i]=(int)x2;
                  y22[f]=(int)y2;
                   i++;
                   f++;
                   a+=0.00001;
                  }
               int length=x22.length;
               g.drawPolygon(x22,y22,length);
                 }
               }

【讨论】:

    【解决方案5】:

    您可以启用抗锯齿:

    Graphics2D g2 = (Graphics2D) g;
    Map<RenderingHints.Key, Object> hints = new HashMap<RenderingHints.Key, Object>();
    hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
    g2.setRenderingHints(hints);
    

    我还建议您绘制到从 paintComponent 方法获得的 Graphics 对象,而不是创建一个中间 BufferedImage

    【讨论】:

    • 我相信我已经这样做了(请参阅我发布的代码)...我只是没有通过创建地图来做到这一点,仅此而已。我正在使用 g2d.setRenderingHints
    • 您在 BufferedImage Graphics2D 对象上执行此操作,对您在 paintComponent 方法中获得的 Graphics 执行此操作也是如此。
    • 但这不一定会改变输出,会不会?
    猜你喜欢
    • 1970-01-01
    • 2012-11-12
    • 2021-06-24
    • 1970-01-01
    • 2019-09-04
    • 1970-01-01
    • 2021-11-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多