36

我曾尝试使用具有相同高度和宽度的方法 drawOval,但随着直径的增加,圆的外观变得更糟。无论大小,我该怎么做才能拥有一个像样的圆圈。我将如何在 java 或其他方法中实现抗锯齿。

4

7 回答 7

56

事实证明,Java2D(我假设是你正在使用的)在这方面已经相当不错了!这里有一个不错的教程:http ://www.javaworld.com/javaworld/jw-08-1998/jw-08-media.html

重要的一行是:

graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                          RenderingHints.VALUE_ANTIALIAS_ON);
于 2009-07-07T20:10:02.323 回答
32

您可以设置渲染提示:

Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
    RenderingHints.VALUE_ANTIALIAS_ON);
于 2009-07-07T20:09:07.833 回答
17

有两件事可能会有所帮助:

  1. Graphics2D.draw(Shape)与实例一起使用java.awt.geom.Ellipse2D而不是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

于 2009-07-07T20:09:32.950 回答
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);
}
于 2009-07-07T20:09:33.917 回答
6

感谢 Oleg Estekhin 指出错误报告,因为它解释了如何去做。

这是之前和之后的一些小圆圈。放大几倍可以看到像素网格。

之前和之后的圆圈

顺着一排,它们以亚像素量轻微移动。

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

请注意,仅使用抗锯齿提示时,前三个圆圈是相同的,最后两个圆圈也是相同的。似乎发生了一些离散的过渡。可能在某个时候四舍五入。

这是代码。它在 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获得以亚像素精度绘制的外观正确的圆圈。

于 2015-07-04T13:52:13.083 回答
3

无法绘制“看起来像样的圆圈”与非常古老的错误6431487 有关

打开抗锯齿并没有多大帮助 - 当所需的圆形大小为 16 像素(对于图标大小仍然很常见)并且抗锯齿已打开时,只需检查 drawOval() 或 drawShape(Eclipse) 生成的“圆形”类型。更大的抗锯齿圆看起来会更好,但它们仍然是不对称的,如果有人愿意仔细观察的话。

似乎要绘制一个“看起来不错的圆圈”,必须手动绘制一个。如果没有抗锯齿,它将是中点圆算法(这个问题有一个漂亮的 java 代码的答案)。

于 2012-03-08T15:27:30.240 回答
-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;
}

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

于 2015-06-14T15:55:47.920 回答