为什么没有适当的抗锯齿渲染这条线?

时间:2015-11-02 22:49:04

标签: java swing

我正在尝试渲染一条线,但是如果线条在真正的画布边界之外开始,我会遇到奇怪的行为。

例如,我有时会得到这个图像而不是正确的行:

enter image description here

正确的行看起来像这样:

enter image description here

以下是生成此示例的可运行代码:

import java.awt.image.*;
import javax.imageio.ImageIO;
import java.io.File;
import java.awt.*;
import java.awt.geom.*;

public class Render {
    public static void main(String[] args) throws Exception {
        BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = (Graphics2D) image.getGraphics();

        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

        g.setColor(Color.WHITE);
        g.fillRect(0, 0, 100, 100);

        g.setColor(Color.BLACK);
        g.setStroke(new BasicStroke(2));
        g.draw(new Line2D.Double(-92, 37, 88, 39));

        g.dispose();
        ImageIO.write(image, "png", new File("output.png"));
    }
}

我尝试使用许多不同的渲染提示,但没有组合摆脱这个问题。可能是罪魁祸首?

修改

这是RenderingHints.VALUE_STROKE_NORMALIZE的图像:

enter image description here

图像的缩放版本(g.scale(10,10)):

enter image description here

5 个答案:

答案 0 :(得分:6)

我认为算法中的 bug 与开始画布有关。 (我是这样向Oracle提交的(评论ID:JI-9026491,Oracle Bug数据库: JDK-8146312))

请参阅此示例(Ubuntu 14.04,Java 8u66):

enter image description here

如果我反转y坐标:

enter image description here

最终测试:

enter image description here

import java.awt.image.*;
import javax.imageio.ImageIO;
import java.io.File;
import java.awt.*;
import java.awt.geom.*;

public class Testing {
    public static void main(String[] args) throws Exception {

        BufferedImage image = new BufferedImage(400, 400, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = (Graphics2D) image.getGraphics();

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

        g.setColor(Color.WHITE);
        g.fillRect(0, 0, 400, 400);

        g.setColor(Color.BLACK);
        g.setStroke(new BasicStroke(2));
        for (int i=0; i<400; i+=5)
        {
            double dy = 8.0*(i-100.0) / 200.0;
            g.setColor(Color.BLACK);
            g.draw(new Line2D.Double(-100, dy+i, 90, i));
            g.draw(new Line2D.Double(200+-100, dy+i, 200+90, i));
        }

        g.dispose();
        ImageIO.write(image, "png", new File("output.png"));
    }
}

答案 1 :(得分:1)

您观察到的行为与线在画布外部开始的事实无关。如果你在画布内完全使用一条线,你会发现同样的行为。

问题来自于小斜率dy/dx=1/90,小宽度(2)和抗锯齿算法的组合。

没有抗锯齿

enter image description here

    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                       RenderingHints.VALUE_ANTIALIAS_OFF);

使用抗锯齿

enter image description here

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

您观察到的行为完全正常,并且是由于抗锯齿算法。

答案 2 :(得分:1)

使用带有Path2D而不是Line2D的示例可以改善输出。

    BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = (Graphics2D) image.getGraphics();

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

    g.setColor(Color.WHITE);
    g.fillRect(0, 0, 100, 100);

    g.setColor(Color.BLACK);
    g.setStroke(new BasicStroke(2));
    Path2D path = new Path2D.Double();
    path.moveTo(-92, 37);
    path.lineTo(88, 39);
    g.draw(path);
    g.dispose();
    ImageIO.write(image, "png", new File("output.png"));

答案 3 :(得分:1)

在我看来,这是在画布外开始你的行时抗锯齿算法的一个错误。

在使用Renderinghints进行大量游戏后,逐行移动线条并分析画布内外的斜率,长度,部分等值,我得出结论,如果你不想得到非常深入了解抗锯齿算法的工作方式,您可能会对正确的解决方法感到满意:

  1. 从最后画布周围的巨大安全区开始。
  2. 像往常一样绘制图像,只需移动安全区的范围。
  3. 裁掉安全区。
  4. 例如:

    import java.awt.image.*;
    import javax.imageio.ImageIO;
    import java.io.File;
    import java.awt.*;
    import java.awt.geom.*;
    
    public class Render {
        public static void main(String[] args) throws Exception {
    
            // YOUR DESIRED SIZE OF CANVAS
            int size = 100;
            // THE SAFEZONE AROUND THE CANVAS
            int safezone = 1000;
    
            // THE IMAGE GETS INITIALIZED WITH SAFEZONE APPLIED
            BufferedImage image = new BufferedImage(size+safezone, size+safezone, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g = (Graphics2D) image.getGraphics();
    
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
            g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    
            g.setColor(Color.WHITE);
    
            // THE IMAGE GETS FILLED, DON'T FORGET THE SAFEZONE
            g.fillRect(0, 0, size+safezone, size+safezone);
    
            // THE ORIGINAL VALUES WITH THE SAFEZONE APPLIED
            int x1 = -92 + safezone/2;
            int x2 = 88 + safezone/2;
            int y1 = 37 + safezone/2;
            int y2 = 39 + safezone/2;
    
            g.setColor(Color.BLACK);
            g.setStroke(new BasicStroke(2));
            g.draw(new Line2D.Double(x1, y1, x2, y2));
    
            g.dispose();
    
            // CROP THE IMAGE
            image = image.getSubimage(safezone/2, safezone/2, size, size);
            ImageIO.write(image, "png", new File("output.png"));
        }
    }
    

    这会给你:

    cropped

    使用您的确切值。代码当然必须符合您的需求,但为了示例,它可以工作。

    虽然这不是一个漂亮的解决方案,结果是,我希望它有所帮助! :)

答案 4 :(得分:0)

我认为在您的特定情况下,您的采样线在x = -92开始的渲染存在一些问题(线的起点位置和斜率在此比例中是一个例外)。但是您可能已经注意到画布之外的值没有问题,例如x = -12x = -22x = -82或甚至x = -102。您需要建议的唯一提示是:

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

并且我认为如果g.setStroke(new BasicStroke(2));不重要,您可以将其更改为g.setStroke(new BasicStroke(1));或甚至完全省略它。这样可以让你更流畅。