为什么在没有适当的抗锯齿的情况下渲染此行?

2022-09-02 10:52:48

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

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

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


答案 1

我认为这是算法中的一个错误,与开始画布外有关。(我向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"));
    }
}

答案 2

在我看来,这是在画布外启动行时抗锯齿算法中的一个错误。

在对Redelutionhints进行了大量操作,逐个像素地移动线条并分析了一些值(例如斜率,长度,画布内外的部分)之后,我得出的结论是,如果您不想深入了解手头的抗锯齿算法的工作方式,您可能会对适当的解决方法感到满意:

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

例如:

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

使用精确值时。当然,代码必须适合您的需求,但为了这个例子,它是有效的。

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


推荐