JavaFX中Canvas的绘制与Java AWT Graphics2D绘制的比较

4
我想展示一个相对长的图表。之前我使用javafx画布(canvas)工作,但有时候当绘制值过多时,会出现缓冲区溢出异常(buffer overflow exception)。我寻找不同的方法,并发现可以使用java.awt.Graphics2D来绘制图表。Graphics2D的优点是性能提高了,而且我不再遇到异常。缺点是图表的质量不如在javafx的画布中看起来那么平滑。
这里有两个测试应用程序(一个使用javafx画布,另一个使用java.awt.Graphics2D),绘制正弦波。
有什么方法可以改善Graphics2D示例的质量,使其看起来像canvas示例吗? 或者是否有其他解决我的问题的方法,我没有想到?
Canvas 示例
public class CanvasDraw extends Application
{
    private static final int PANEL_WIDTH = 400, PANEL_HEIGHT = 100;


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


    @Override
    public void start(Stage stage)
    {
        stage.setTitle("CanvasDraw");
        StackPane root = new StackPane();
        Scene scene = new Scene(root);
        root.getChildren().add(createCanvas());
        stage.setScene(scene);
        stage.show();
    }


    private Canvas createCanvas()
    {
        Canvas canvas = new Canvas(PANEL_WIDTH, PANEL_HEIGHT);
        GraphicsContext gc = canvas.getGraphicsContext2D();
        drawSinus(gc);
        return canvas;
    }


    private void drawSinus(GraphicsContext gc)
    {
        double height = 50;
        double xFactor = 0.5;
        double yFactor = 50;
        for (int index = 0; index < 720; index++)
        {
            double x = index * xFactor;
            double y = Math.sin(Math.toRadians(index)) * yFactor + height;
            if (index == 0)
            {
                gc.moveTo(x, y);
            }
            else
            {
                gc.lineTo(x, y);
            }
        }
        gc.stroke();
    }
}

新的 Graphics2D 示例

public class Graphics2DDraw extends Application
{
    private static final int PANEL_WIDTH = 400, PANEL_HEIGHT = 100;


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


    @Override
    public void start(Stage stage)
    {
        stage.setTitle("Graphics2DDraw");
        StackPane root = new StackPane();
        Scene scene = new Scene(root);
        root.getChildren().add(createImageView());
        stage.setScene(scene);
        stage.show();
    }


    private ImageView createImageView()
    {
        BufferedImage bi = new BufferedImage(PANEL_WIDTH, PANEL_HEIGHT, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = bi.createGraphics();
        g.setColor(Color.BLACK);
        BasicStroke bs = new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
        g.setStroke(bs);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
        g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        drawSinus(g);
        return new ImageView(SwingFXUtils.toFXImage(bi, null));
    }


    private void drawSinus(Graphics2D g)
    {
        double height = 50;
        double xFactor = 0.5;
        double yFactor = 50;
        int length = 720;
        int lastX = 0;
        int lastY = 0;
        GeneralPath path = new GeneralPath();
        for (int index = 0; index < length; index++)
        {
            double x3 = index * xFactor;
            double y3 = Math.sin(Math.toRadians(index)) * yFactor + height;
            if (index == 0)
            {
                path.moveTo(x3, y3);
            }
            else
            {
                path.lineTo(x3, y3);
            }
        }
        g.draw(path);
        g.dispose();
    }
}

编辑 这就是缓冲区溢出异常的样子:

java.nio.BufferOverflowException
    at com.sun.javafx.sg.prism.GrowableDataBuffer.ensureReadCapacity(GrowableDataBuffer.java:317)
    at com.sun.javafx.sg.prism.GrowableDataBuffer.getInt(GrowableDataBuffer.java:527)
    at com.sun.javafx.sg.prism.GrowableDataBuffer.getFloat(GrowableDataBuffer.java:563)
    at com.sun.javafx.sg.prism.NGCanvas.renderStream(NGCanvas.java:960)
    at com.sun.javafx.sg.prism.NGCanvas.renderContent(NGCanvas.java:609)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.tk.quantum.ViewPainter.doPaint(ViewPainter.java:477)
    at com.sun.javafx.tk.quantum.ViewPainter.paintImpl(ViewPainter.java:330)
    at com.sun.javafx.tk.quantum.UploadingPainter.run(UploadingPainter.java:134)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
    at com.sun.javafx.tk.RenderJob.run(RenderJob.java:58)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125)
    at java.lang.Thread.run(Thread.java:748)
java.lang.IllegalArgumentException: ALPHA color component is out of range
    at com.sun.pisces.PiscesRenderer.checkColorRange(PiscesRenderer.java:113)
    at com.sun.pisces.PiscesRenderer.setColor(PiscesRenderer.java:105)
    at com.sun.prism.sw.SWPaint.setColor(SWPaint.java:76)
    at com.sun.prism.sw.SWPaint.setPaintBeforeDraw(SWPaint.java:118)
    at com.sun.prism.sw.SWPaint.setPaintFromShape(SWPaint.java:86)
    at com.sun.prism.sw.SWGraphics.paintShape(SWGraphics.java:493)
    at com.sun.prism.sw.SWGraphics.drawLine(SWGraphics.java:545)
    at com.sun.javafx.sg.prism.NGCanvas.handleRenderOp(NGCanvas.java:1219)
    at com.sun.javafx.sg.prism.NGCanvas.renderStream(NGCanvas.java:1103)
    at com.sun.javafx.sg.prism.NGCanvas.renderContent(NGCanvas.java:609)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:577)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2053)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1945)
    at com.sun.javafx.tk.quantum.ViewPainter.doPaint(ViewPainter.java:477)
    at com.sun.javafx.tk.quantum.ViewPainter.paintImpl(ViewPainter.java:330)
    at com.sun.javafx.tk.quantum.UploadingPainter.run(UploadingPainter.java:134)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
    at com.sun.javafx.tk.RenderJob.run(RenderJob.java:58)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125)
    at java.lang.Thread.run(Thread.java:748)

编辑2:我找到了一种提高graphics2d绘图质量的方法。通过为graphics2d对象设置渲染提示(感谢Hylian Pickachu的建议),正弦曲线看起来更加平滑。虽然不像画布示例中的正弦曲线那样锐利,但我认为现在可以接受这个结果了。


1
请您提供一个缓冲区溢出异常的堆栈跟踪示例,好吗? - Slaw
1
请勿添加整个代码,而是添加一个 [mre]。您是否从线程更新 GUI? - c0der
是的,当线程成功时,UI 将会被更新。如果我不在一个线程中运行绘制函数,那么直到视图准备好之前需要等待太长时间。 - Yupp
使用问题中发布的代码,无法重现您在JavaFX中遇到的问题。 - fabian
1
你尝试使用RenderingHints了吗?链接在这里:https://docs.oracle.com/javase/tutorial/2d/advanced/quality.html - Fluffy the Togekiss
显示剩余5条评论
1个回答

1

提高Graphics2D质量的一种方法是使用RenderingHints。 RenderingHints允许您设置渲染质量与渲染性能之间的平衡,这是需要考虑的权衡,因为您说过您享受使用Graphics2D而不是Canvas带来的性能提升。然而,这种方法将允许您继续使用Graphics2D并避免崩溃。


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