Java 为什么这条线渲染时没有适当的抗锯齿?
我试图渲染一条线,但如果线从实际画布边界之外开始,我会得到奇怪的行为 例如,我有时会得到以下图像,而不是正确的线条: 正确的线路应该是这样的: 以下是生成此示例的可运行代码:Java 为什么这条线渲染时没有适当的抗锯齿?,java,swing,Java,Swing,我试图渲染一条线,但如果线从实际画布边界之外开始,我会得到奇怪的行为 例如,我有时会得到以下图像,而不是正确的线条: 正确的线路应该是这样的: 以下是生成此示例的可运行代码: import java.awt.image.*; import javax.imageio.ImageIO; import java.io.File; import java.awt.*; import java.awt.geom.*; public class Render { public static
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"));
}
}
我尝试过使用很多不同的渲染提示,但没有一种组合可以解决这个问题。罪魁祸首可能是什么
编辑:
以下是带有RenderingHights.VALUE\u STROKE\u NORMALIZE的图像:
图像的缩放版本(g.scale(10,10)
):
您观察到的行为与线条从画布外部开始这一事实无关。如果完全在画布内部使用线条,您将观察到相同的行为 问题来自于小斜率
dy/dx=1/90
、小宽度(2
)和抗锯齿算法的组合
无抗锯齿
带抗锯齿
您观察到的行为完全正常,这是由于抗锯齿算法造成的。我认为在您的特定情况下渲染存在一些问题,您的采样线开始于
x=-92
(线的起点位置和坡度在此比例中是一个例外)。但是正如您可能注意到的,画布之外的值没有问题,比如x=-12
或x=-22
或x=-82
甚至x=-102
。您需要提供建议的唯一提示是:
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
我认为如果g.setStroke(新的基本行程(2))
实际上并不重要,您可以将其更改为g.setStroke(新的BasicStroke(1))
甚至完全忽略它。这将使您的线条更加流畅。我认为这是算法中的一个缺陷,与从画布开始有关。(我向Oracle提交了该文件(审查ID:JI-9026491,Oracle错误数据库:))
请参阅此示例(Ubuntu 14.04,Java 8u66):
如果我反转y坐标:
最终测试:
导入java.awt.image.*;
导入javax.imageio.imageio;
导入java.io.File;
导入java.awt.*;
导入java.awt.geom.*;
公共类测试{
公共静态void main(字符串[]args)引发异常{
BuffereImage=新的BuffereImage(400400,BuffereImage.TYPE_INT_ARGB);
Graphics2D g=(Graphics2D)image.getGraphics();
g、 setRenderingHint(RenderingHits.KEY\u ANTIALIASING,RenderingHits.VALUE\u ANTIALIAS\u ON);
g、 setColor(Color.WHITE);
g、 fillRect(0,040400);
g、 设置颜色(颜色为黑色);
g、 设定行程(新基本行程(2));
对于(int i=0;i,将示例与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"));
在我看来,这是在画布外开始行时抗锯齿算法中的一个错误
在反复使用渲染提示、逐像素移动线条并分析一些值(如坡度、长度、画布内外部分)后,我得出结论,如果您不想深入了解现有抗锯齿算法的工作方式,您可能会对适当的解决方法感到满意:
从最后一块画布周围的巨大安全区域开始
像往常一样绘制图像,只需移动安全区域的范围
切断安全区
例如:
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"));
}
}
这将为您提供:
当然,代码必须适合您的需要,但是为了示例,它是有效的
虽然这不是一个很好的解决方案,但结果是令人满意的,我希望它能有所帮助!:)寻找斜率为1/90的两像素线。@trashgod-我用右边的线附加了一个图像-它是以相同的斜率和位置生成的,但整个宽度都有很好的填充效果。唯一的区别是画布外的长度更短-这条线是使用g.draw(new Line2D.Double(98,38188,39))绘制的
。可能是笔划设置,2.4。可能是要设置的亚像素渲染(这不是纯的,而是默认的)-RGB亚像素的布局可能与渲染所认为的不同。@JoopEggen亚像素渲染的不同设置更改了图片,但问题仍然存在(我将图片附在问题后面)。我尝试使用不同的图像类型而不是INT_ARGB,但结果仍然相同。笔划设置可以做什么?对于亚像素渲染,请参见。对于便携式非亚像素渲染,我只能建议放大100倍并查看内容。也许其他人可以帮上忙。请看我的第一张示例图片-它是d使用抗锯齿绘制,但它看起来与您的第二张图片不一样。如果我在画布内部(甚至外部,但不太远)开始绘制线条,我将观察到非常不同的行为,如我的第二张示例图片所示。我使用与您完全相同的设置修改了图像。唯一的更改是在键上的抗锯齿。如果您在画布外开始线条,您将更改坡度,这可能是您看到的差异的原因。如果我错了,请纠正我,但我认为(8,37)->(188,39)与(98,38)->(188,39)-1/90具有相同的斜率?如果您仍然喜欢分析,可以将此代码粘贴到写入图像的点下方:x1-=safezone/2;x2-=safezone/2;y1-=safezone/2;y2-=safezone/2;double length=Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))双斜率=((双)x2-x1))/(y2-y1);系统输出println(“斜率:+slope”);系统输出println(“长度:+Length”);系统输出println(“第1点:+x1+”|“+y1”);系统输出println(“第2点:+x2+”|“+y2”);系统输出println(“内部零件:+x2/长度);系统输出println(“外部零件:+Math.abs(x1/长度))是的,这个解决方案正在运行。不幸的是,我每秒需要创建大量这样的图像,安全区将不再有效
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"));
}
}
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"));
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"));
}
}