如何使用JavaFX画布绘制1像素线条?

11

我一直在搜索和谷歌,找到了一些相关的问题/帖子,但没有一个解决我的问题。

我正在使用以下方式直接在画布上(JavaFX)绘制线条:

gc.setStroke(color);
gc.setLineWidth(lineWidth);
gc.strokeLine(startX, startY, endX, endY);

我需要1像素宽度的线条,所以我设置了lineWidth=1。 但是我得到了这样的结果: enter image description here

请注意,这些线条是模糊的,并不是1像素宽。 我尝试将lineWidth设置为0.1或0.01等,但结果没有改变。

顺便说一下......我不明白为什么这个参数是double类型。我在某个地方读到它与DPI有关,但我不明白它的单位是什么,以及如何转换为像素。 Oracle的文档没有帮助我。(或者我没有找到帮助我的文档)

我希望得到这个结果:

enter image description here

这是在另一个平台上实现的。请注意,线条很清晰,并且只有1像素宽。


我猜想这与Shape文档中“与坐标系的交互”一节所描述的问题相同;但是我不知道如何在画布的上下文中解决这个问题。 - James_D
以下这篇文章非常有趣:https://dlemmermann.wordpress.com/2014/04/10/javafx-tip-2-sharp-drawing-with-canvas-api/ 但对我来说,这个解决办法并没有起作用,也不知道为什么。 - Chocksmith
我刚刚找到了这个链接:https://community.oracle.com/thread/2465226 它解释了它的工作原理... 我会进行测试,如果它有效,我会回答自己的问题并发布解决方案。 - Chocksmith
James_D,你的解释非常精确。 - Chocksmith
2个回答

36

把每个像素想象成一个(小)矩形(而不是一个点)。 整数坐标是像素之间的边界; 因此,具有整数坐标的(水平或垂直)线“在像素之间”落下。这是通过抗锯齿渲染实现的,它会将线段的一半近似到一个像素上,另一半近似到另一个像素上。将线段向左或向右移动0.5个像素将其移到像素的中心,从而解决了这个问题。

以下是一个示例:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class SharpCanvasTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        Canvas sharpCanvas = createCanvasGrid(600, 300, true);
        Canvas blurryCanvas = createCanvasGrid(600, 300, false);
        VBox root = new VBox(5, sharpCanvas, blurryCanvas);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
    
    private Canvas createCanvasGrid(int width, int height, boolean sharp) {
        Canvas canvas = new Canvas(width, height);
        GraphicsContext gc = canvas.getGraphicsContext2D() ;
        gc.setLineWidth(1.0);
        for (double x = sharp ? 0.5 : 0.0; x < width; x+=10) {
            gc.moveTo(x, 0);
            gc.lineTo(x, height);
            gc.stroke();
        }
        
        for (double y = sharp ? 0.5 : 0.0; y < height; y+=10) {
            gc.moveTo(0, y);
            gc.lineTo(width, y);
            gc.stroke();
        }
        return canvas ;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

结果如下:

enter image description here


4
哇,为什么这不是画布文档中的第一个段落呢?谢谢提供信息,这改变了很多东西。 - Roland
嗨。我知道距离上次回复已经快4年了,但是... 我们可以将这个添加到扩展JPanel的类中吗?例如,我有一个扩展JPanel的类,在这个类中我必须画5条线,一条垂直线,在JPanel的中间宽度上和4条水平线。这样我就可以将JPanel分成10个正方形...我尝试了你的解决方案,但是它给了我一些错误,比如“内部图形尚未初始化”...谢谢 - Vali Maties

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接