Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typo3/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
JavaFX应用程序在绘制多个画布后无响应_Java_Javafx_Canvas - Fatal编程技术网

JavaFX应用程序在绘制多个画布后无响应

JavaFX应用程序在绘制多个画布后无响应,java,javafx,canvas,Java,Javafx,Canvas,我有一个应用程序,我想显示一个FlowPane,其中PDF页面缩略图被绘制到画布中。我正在使用和渲染页面。 我当前的实现创建了一系列画布作为页面数量,将它们添加到流程窗格,然后旋转一系列或异步任务,将页面内容绘制到画布中 我不确定异步绘图是否是推荐的方法,但我的想法是不要使用JavaFX线程进行PDF解析,以避免冻结应用程序。 现在,我的问题是,我可以从日志中看到,所有渲染任务都已完成,文档已关闭。UI显示一些呈现的页面,但在10秒钟内没有响应。在应用程序恢复之后,所有页面都被呈现,一切都很好地

我有一个应用程序,我想显示一个
FlowPane
,其中PDF页面缩略图被绘制到画布中。我正在使用和渲染页面。 我当前的实现创建了一系列画布作为页面数量,将它们添加到
流程窗格
,然后旋转一系列或异步任务,将页面内容绘制到画布中

我不确定异步绘图是否是推荐的方法,但我的想法是不要使用JavaFX线程进行PDF解析,以避免冻结应用程序。 现在,我的问题是,我可以从日志中看到,所有渲染任务都已完成,文档已关闭。UI显示一些呈现的页面,但在10秒钟内没有响应。在应用程序恢复之后,所有页面都被呈现,一切都很好地工作

我试着描述一下,我认为这是相关的部分: 但我对引擎盖下发生的事情知之甚少,我无法找出我做错了什么。你对我的方法/代码有什么错误以及如何改进有什么想法或提示吗?理想情况下,我希望应用程序在页面缩略图填充时快速响应

我在Linux上有一台相当不错的机器,但我也在Windows上进行了测试,得到了相同的行为。我还试图用
HBox
VBox
替换
FlowPane
,但仍然发生了同样的情况

下面是一些难看的代码来重现这种行为:

public class TestApp extends Application {
    public static void main(String[] args) {
        Application.launch(TestApp.class, args);
    }

    @Override
    public void start(Stage primaryStage) {
        try {
            BorderPane root = new BorderPane();
            Scene scene = new Scene(root, 400, 400);

            var flow = new FlowPane();
            flow.setOnDragOver(e -> {
                if (e.getDragboard().hasFiles()) {
                    e.acceptTransferModes(TransferMode.COPY);
                }
                e.consume();
            });

            flow.setOnDragDropped(e -> {
                var tasks = new ArrayList<TestApp.RenderTask>();
                try {
                    var document = PDDocument.load(e.getDragboard()
                                                    .getFiles().get(0));
                    var renderer = new PDFRenderer(document);
                    for (int i = 1; i <= document.getNumberOfPages(); i++) {
                        var page = document.getPage(i - 1);
                        var cropbox = page.getCropBox();
                        var thumbnailDimension = new Dimension2D(400,
                                                                 400 * (cropbox.getHeight() / cropbox.getWidth()));
                        var thumbnail = new Canvas(thumbnailDimension.getWidth(), thumbnailDimension.getHeight());
                        var gs = thumbnail.getGraphicsContext2D();
                        var pop = gs.getFill();
                        gs.setFill(Color.WHITE);
                        gs.fillRect(0, 0, thumbnailDimension.getWidth(), thumbnailDimension.getHeight());
                        gs.setFill(pop);

                        tasks.add(new TestApp.RenderTask(renderer, thumbnail, i));
                        flow.getChildren().add(new Group(thumbnail));
                    }

                    var exec = Executors.newSingleThreadExecutor();
                    tasks.forEach(exec::submit);
                    exec.submit(()-> {
                        try {
                            document.close();
                            System.out.println("close");
                        } catch (IOException ioException) {
                            ioException.printStackTrace();
                        }
                    });
                } catch (Exception ioException) {
                    ioException.printStackTrace();
                }

                e.setDropCompleted(true);
                e.consume();
            });
            var scroll = new ScrollPane(flow);
            scroll.setFitToHeight(true);
            scroll.setFitToWidth(true);
            root.setCenter(scroll);

            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static record RenderTask(PDFRenderer renderer, Canvas canvas, int page) implements Runnable {

        @Override
        public void run() {
            var gs = new FXGraphics2D(canvas.getGraphicsContext2D());
            gs.setBackground(WHITE);
            try {
                renderer.renderPageToGraphics(page - 1, gs);
            } catch (IOException e) {
                e.printStackTrace();
            }
            gs.dispose();
        }
    }
}
公共类TestApp扩展应用程序{
公共静态void main(字符串[]args){
应用程序启动(TestApp.class,args);
}
@凌驾
公共无效开始(阶段primaryStage){
试一试{
BorderPane根=新的BorderPane();
场景=新场景(根,400400);
var flow=新的FlowPane();
流量设定值(e->{
如果(例如getDragboard().hasFiles()){
e、 acceptTransferModes(TransferMode.COPY);
}
e、 消费();
});
flow.setOnDragDropped(e->{
var tasks=new ArrayList();
试一试{
var document=PDDocument.load(例如getDragboard()
.getFiles().get(0));
var渲染器=新的PDF渲染器(文档);
对于(int i=1;i{
试一试{
document.close();
系统输出打印项次(“关闭”);
}捕获(IOException IOException){
ioException.printStackTrace();
}
});
}捕获(异常ioException){
ioException.printStackTrace();
}
e、 setDropCompleted(真);
e、 消费();
});
var scroll=新滚动窗格(流);
scroll.setFitToHeight(true);
scroll.setFitToWidth(true);
root.setCenter(滚动);
初级阶段。场景(场景);
primaryStage.show();
}捕获(例外e){
e、 printStackTrace();
}
}
私有静态记录RenderTask(PDFRenderer渲染器、画布画布、int页面)实现可运行{
@凌驾
公开募捐{
var gs=newFXGraphics2D(canvas.getGraphicsContext2D());
地面障碍(白色);
试一试{
renderPageToGraphics(第1页,gs);
}捕获(IOE异常){
e、 printStackTrace();
}
gs.dispose();
}
}
}
这是我用来测试它的310页PDF文件。
谢谢

我终于得到了我想要的,应用程序响应了,缩略图已经准备好,并且内存使用有限

问题
我以为我是从FX线程中绘制画布,但是
Canvas
的工作原理是,你填充了一个绘制指令缓冲区,当画布成为场景的一部分时,它们就会被执行(我想)。发生的事情是,我用大量的绘图指令快速填充画布,将所有画布添加到
FlowPane
中,应用程序花费大量时间实际执行310画布的绘图指令,并在约10秒钟内没有响应

在这里和Twitter上阅读了JavaFX社区的一些内容和建议后,我尝试切换到
ImageView
实现,告诉PDFBox创建页面的
BufferedImage
,并使用它创建
ImageView
。它工作得很好,但内存使用率是Canvas impl的10倍:

两全其美
在我的最终解决方案中,我将白色画布添加到FlowPane,创建页面的
BuffereImage
,并将其转换为FX线程的
图像
,将图像绘制到
画布
,然后丢弃
图像
。 这是相同的310页PDF文件的内存使用情况:

这就是应用程序的响应性:

编辑(添加要复制的代码)
这里是我使用的代码(使用SAMBox而不是PDFBox)

公共类TestAppCanvasImageMixed扩展应用程序{
公共静态void main(字符串[]args){
Application.launch(TestAppCanvas.class,args);
}
@凌驾
公共无效开始(阶段primaryStage){
试一试{
BorderPane根=新的BorderPane();
场景=新场景(根,400400);
var按钮=新按钮(“渲染”);
按钮。设置禁用(true);
setTop(新的HBox(按钮));
var flow=新的FlowPane();
流量设定值(e->{
如果(例如getDragboard().hasFiles()){
public class TestAppCanvasImageMixed extends Application {
    public static void main(String[] args) {
        Application.launch(TestAppCanvas.class, args);
    }

    @Override
    public void start(Stage primaryStage) {
        try {
            BorderPane root = new BorderPane();
            Scene scene = new Scene(root, 400, 400);

            var button = new Button("render");
            button.setDisable(true);
            root.setTop(new HBox(button));
            var flow = new FlowPane();
            flow.setOnDragOver(e -> {
                if (e.getDragboard().hasFiles()) {
                    e.acceptTransferModes(TransferMode.COPY);
                }
                e.consume();
            });

            flow.setOnDragDropped(e -> {
                ArrayList<TestAppCanvasImageMixed.RenderTaskToImage> tasks = new ArrayList<>();
                try {
                    var document = PDDocument.load(e.getDragboard()
                                                    .getFiles().get(0));
                    var renderer = new PDFRenderer(document);
                    for (int i = 1; i <= document.getNumberOfPages(); i++) {
                        var page = document.getPage(i - 1);
                        var cropbox = page.getCropBox();
                        var thumbnailDimension = new Dimension2D(400,
                                                                 400 * (cropbox.getHeight() / cropbox.getWidth()));
                        var thumbnail = new Canvas(thumbnailDimension.getWidth(), thumbnailDimension.getHeight());
                        var gs = thumbnail.getGraphicsContext2D();
                        var clip = new Rectangle(thumbnailDimension.getWidth(), thumbnailDimension.getHeight());
                        clip.setArcHeight(15);
                        clip.setArcWidth(15);
                        thumbnail.setClip(clip);
                        var pop = gs.getFill();
                        gs.setFill(WHITE);
                        gs.fillRect(0, 0, thumbnailDimension.getWidth(), thumbnailDimension.getHeight());
                        gs.setFill(pop);
                        var g = new Group();
                        tasks.add(new TestAppCanvasImageMixed.RenderTaskToImage(renderer, 400 / cropbox.getWidth(), i, thumbnail,
                                                                                () -> g.getChildren().setAll(thumbnail)));
                        flow.getChildren().add(new Group());
                    }

                    button.setOnAction(a -> {
                        var exec = Executors.newFixedThreadPool(1);
                        tasks.forEach(exec::submit);
                        exec.submit(() -> {
                            try {
                                document.close();
                                System.out.println("close");
                            } catch (IOException ioException) {
                                ioException.printStackTrace();
                            }
                        });
                    });
                    button.setDisable(false);

                } catch (Exception ioException) {
                    ioException.printStackTrace();
                }

                e.setDropCompleted(true);
                e.consume();
            });
            var scroll = new ScrollPane(new StackPane(flow));
           scroll.setFitToHeight(true);
           scroll.setFitToWidth(true);
            root.setCenter(scroll);

            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static record RenderTaskToImage(PDFRenderer renderer, float scale, int page, Canvas canvas, Runnable r) implements Runnable {

        @Override
        public void run() {
            try {
                var bufferedImage = renderer.renderImage(page - 1, scale, ImageType.ARGB, RenderDestination.VIEW);
                var image = SwingFXUtils.toFXImage(bufferedImage, null);
                var gc = canvas.getGraphicsContext2D();
                gc.drawImage(image, 0, 0);
                Platform.runLater(() -> r.run());
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
}