如何在Java中绘制一个看起来不错的圆形

37

我尝试使用相等高度和宽度的drawOval方法,但随着直径增加,圆变得越来越难看。我该怎么做才能让圆无论大小都看起来不错?我该如何在Java中实现反锯齿或其他方法。

7个回答

62

18
看起来常量已经从Graphics2D移动到RenderingHints中。请参见此示例 - Sebastian Paaske Tørholm
4
19年了,还在继续=P - mr5
2
20岁还在活着! :ㅇ - Inyoung Kim 김인영
1
22,我们走吧!XD - KrystosTheOverlord
7
不行。页面未找到。 - clickbait
显示剩余2条评论

33

你可以设置渲染提示:

Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
    RenderingHints.VALUE_ANTIALIAS_ON);

18

两个可能有用的建议:

  1. 使用一个 java.awt.geom.Ellipse2D 实例,结合 Graphics2D.draw(Shape) 方法代替 Graphics.drawOval
  2. 如果结果仍然不理想,尝试使用 Graphics2D.setRenderingHint 启用抗锯齿功能。

示例:

public void paint(Graphics g) {
    Graphics2D g2d = (Graphics2D) g;
    Shape theCircle = new Ellipse2D.Double(centerX - radius, centerY - radius, 2.0 * radius, 2.0 * radius);
    g2d.draw(theCircle);
}

请参考Josef的答案以了解setRenderingHint的示例


7
当然,您可以将半径设置为所需的任何值:
@Override
public void paint(Graphics g) {
    Graphics2D g2d = (Graphics2D) g;
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    Ellipse2D.Double hole = new Ellipse2D.Double();
    hole.width = 28;
    hole.height = 28;
    hole.x = 14;
    hole.y = 14;
    g2d.draw(hole);
}

你还需要设置椭圆的位置,我认为你想要使用g2d.draw而不是g2d.stroke。 - finnw

6

感谢Oleg Estekhin指出的错误报告,因为它解释了如何做到这一点。

这里有一些小圆圈,在之前和之后。放大几倍以查看像素网格。

Circles before and after

向下移动一行,它们会稍微移动子像素量。

第一列没有渲染提示。第二个是仅使用反锯齿。第三个是使用反锯齿和纯模式。

请注意,仅使用反锯齿提示时,前三个圆相同,最后两个圆也相同。似乎发生了一些离散的转换。可能在某个时候进行舍入。

这是代码。为了可读性,它是用Jython编写的,但它驱动Java运行时库,并且可以无损地移植到等效的Java源中,具有完全相同的效果。

from java.lang import *
from java.io import *
from java.awt import *
from java.awt.geom import *
from java.awt.image import *
from javax.imageio import *

bim = BufferedImage(30, 42, BufferedImage.TYPE_INT_ARGB)
g = bim.createGraphics()
g.fillRect(0, 0, 100, 100)
g.setColor(Color.BLACK)
for i in range(5):
    g.draw(Ellipse2D.Double(2+0.2*i, 2+8.2*i, 5, 5))

g.setRenderingHint( RenderingHints.  KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON)

for i in range(5):
    g.draw(Ellipse2D.Double(12+0.2*i, 2+8.2*i, 5, 5))

g.setRenderingHint( RenderingHints.  KEY_STROKE_CONTROL,
                    RenderingHints.VALUE_STROKE_PURE)

for i in range(5):
    g.draw(Ellipse2D.Double(22+0.2*i, 2+8.2*i, 5, 5))

#You'll probably want this too later on:
#g.setRenderingHint( RenderingHints.  KEY_INTERPOLATION,
#                    RenderingHints.VALUE_INTERPOLATION_BICUBIC)
#g.setRenderingHint( RenderingHints.  KEY_RENDERING, 
#                    RenderingHints.VALUE_RENDER_QUALITY)

ImageIO.write(bim, "PNG", File("test.png"))

总结:您需要同时使用VALUE_ANTIALIAS_ONVALUE_STROKE_PURE才能绘制具有亚像素精度的适当圆形。


3
无法绘制“漂亮的圆形”与一个非常古老的错误相关,即6431487
打开抗锯齿功能并不能帮助很多——只需检查当所需的圆大小为16像素(对于图标大小仍然相当普遍)且抗锯齿已开启时,drawOval()或drawShape(Eclipse)所生成的“圆”的类型即可。更大的抗锯齿圆看起来会更好,但如果有人仔细观察它们,它们仍然是不对称的。
似乎要绘制一个“漂亮的圆形”,必须手动绘制一个。没有抗锯齿的话,就是中点圆算法(这个问题有一个带有漂亮Java代码答案)。

-2

编辑日期:2017年9月6日

这是我发明的一种算法,用于在整数矩阵上画圆。相同的思路也可用于在BufferedImage中绘制圆。 如果您正在尝试使用Graphics类绘制该圆形,则这不是您要寻找的答案(除非您希望使用g.drawLine(x,y,x+1,y)修改每个颜色分配,但可能会非常慢)。

protected boolean runOnCircumference(int[][] matrix, int x, int y, int ray, int color) {
    boolean ret;
    int[] rowUpper = null, rowInferior = null, rowCenterUpper = null, rowCenterInferior = null;

    if (ret = ray > 0) {
        if (ray == 1) {
            matrix[y][x + 1] = color;
            rowUpper = matrix[++y];
            rowUpper[x] = color;
            rowUpper[x + 2] = color;
            matrix[y][x] = color;
        } else {
            double rRay = ray + 0.5;
            int r = 0, c = 0, ray2 = ray << 1, ray_1 = ray - 1, halfRay = (ray >> 1) + ray % 2, rInf,
                    ray1 = ray + 1, horizontalSymmetricOldC;
            // draw cardinal points

            rowUpper = matrix[ray + y];
            rowUpper[x] = color;
            rowUpper[x + ray2] = color;
            matrix[y][x + ray] = color;
            matrix[ray2 + y][x + ray] = color;

            horizontalSymmetricOldC = ray1;
            rInf = ray2;
            c = ray_1;
            for (r = 0; r < halfRay; r++, rInf--) {

                rowUpper = matrix[r + y];
                rowInferior = matrix[rInf + y];

                while (c > 0 && (Math.hypot(ray - c, (ray - r)) < rRay)) {

                    rowUpper[x + c] = color;
                    rowUpper[x + horizontalSymmetricOldC] = color;
                    rowInferior[x + c] = color;
                    rowInferior[x + horizontalSymmetricOldC] = color;

                    // get the row pointer to optimize
                    rowCenterUpper = matrix[c + y];
                    rowCenterInferior = matrix[horizontalSymmetricOldC + y];
                    // draw
                    rowCenterUpper[x + r] = color;
                    rowCenterUpper[x + rInf] = color;
                    rowCenterInferior[x + r] = color;
                    rowCenterInferior[x + rInf] = color;
                    horizontalSymmetricOldC++;
                    c--;
                }
            } // end r circle
        }
    }
    return ret;
}

我已经尝试了很多次,手动验证它的正确性,所以我认为它会起作用。我没有进行任何范围检查,只是为了简化代码。 我希望它能帮助你和所有想在矩阵上画圆的人(例如那些试图在纯代码上创建自己的视频游戏并需要管理基于矩阵的游戏地图来存储放置在游戏地图上的对象的程序员[如果您需要帮助,请给我发电子邮件])。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接