如何用Java画一个好看的圆

发布于 2024-07-26 12:40:13 字数 105 浏览 10 评论 0原文

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

I have tried using the method drawOval with equal height and width but as the diameter increases the circle becomes worse looking. What can I do to have a decent looking circle no matter the size. How would I implement anti-aliasing in java or some other method.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

污味仙女 2024-08-02 12:40:14

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

  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的示例

Two things that may help:

  1. Use Graphics2D.draw(Shape) with an instance of java.awt.geom.Ellipse2D instead of Graphics.drawOval
  2. If the result is still not satisfactory, try using Graphics2D.setRenderingHint to enable antialiasing

Example

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

See Josef's answer for an example of setRenderingHint

孤檠 2024-08-02 12:40:14

当然,您可以将半径设置为您需要的值:

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

Of course you set your radius to what ever you need:

@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);
}
哑剧 2024-08-02 12:40:14

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

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

前后的圆圈

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

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

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

这是代码。 它采用 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 才能以亚像素精度绘制看起来正确的圆圈。

Thanks to Oleg Estekhin for pointing out the bug report, because it explains how to do it.

Here are some small circles before and after. Magnified a few times to see the pixel grid.

Circles before and after

Going down a row, they're moving slightly by subpixel amounts.

The first column is without rendering hints. The second is with antialias only. The third is with antialias and pure mode.

Note how with antialias hints only, the first three circles are the same, and the last two are also the same. There seems to be some discrete transition happening. Probably rounding at some point.

Here's the code. It's in Jython for readability, but it drives the Java runtime library underneath and can be losslessly ported to equivalent Java source, with exactly the same effect.

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

Summary: you need both VALUE_ANTIALIAS_ON and VALUE_STROKE_PURE to get proper looking circles drawn with subpixel accuracy.

千仐 2024-08-02 12:40:14

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

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

看来要画一个“好看的圆”就必须手动画一个。 如果没有抗锯齿,它将是中点圆算法(这个问题有一个带有漂亮java代码的答案)。

Inability to draw a "decent looking circle" is related to the very old bug 6431487.

Turning antialiasing on does not help a lot - just check the kind of "circle" produced by the drawOval() or drawShape(Eclipse) when the required circle size is 16 pixels (still pretty common for icon size) and antialiasing is on. Bigger antialiased circles will look better but they are still asymmetric, if somebody will care to look at them closely.

It seems that to draw a "decent looking circle" one has to manually draw one. Without antialiasing it will be midpoint circle algorithm (this question has an answer with a pretty java code for it).

征﹌骨岁月お 2024-08-02 12:40:14

编辑: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;
}

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

EDITED: 06 September 2017

That's an algorithm invented by me to draw a circle over a integer matrix. The same idea could be used to write a circle inside a BufferedImage.
If you are trying to draw that circle using the class Graphics this is not the answare you are looking for (unless you wish to modify each color-assignement with g.drawLine(x, y, x+1, y), but it could be very slow).

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

I tried it so many times, verifying manually it correctness, so I think it will work. I haven't made any range-check just to simplify the code.
I hope it will help you and everyone wish to draw a circle over a matrix (for example, those programmer who tries to create their own videogames on pure code and need to manage a matrix-oriented game-map to store the objects lying on the game-map [if you need help on this, email me]).

烦人精 2024-08-02 12:40:13

事实证明,Java2D(我假设您正在使用它)已经非常擅长于此! 这里有一个不错的教程: http://www.javaworld .com/javaworld/jw-08-1998/jw-08-media.html

重要的一行是:

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

As it turns out, Java2D (which I'm assuming is what you're using) is already pretty good at this! There's a decent tutorial here: http://www.javaworld.com/javaworld/jw-08-1998/jw-08-media.html

The important line is:

graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                          RenderingHints.VALUE_ANTIALIAS_ON);
你如我软肋 2024-08-02 12:40:13

您可以设置渲染提示:

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

you can set rendering hints:

Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
    RenderingHints.VALUE_ANTIALIAS_ON);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文