将GUI渲染到内存中的图像

时间:2014-11-20 19:13:14

标签: java swing javafx

以某种方式可以将GUI呈现给BufferedImage或其他类型的内存图像而不在屏幕上显示它吗?

我知道这会消除各种硬件加速,但对于一个只刷新一次或两次的简单GUI,这应该不是问题。

已经尝试让JavaFX输出图像,但我找不到先在屏幕上省略渲染的方法。有没有人知道用JavaFX或Swing做到这一点的方法?

使用简单的图像处理自己绘制一个简单的GUI是没有问题的,但是我必须手动完成所有操作并使用Swing或FX会更容易。

编辑: 为了使它更清晰,我没有活动显示,但我可以保存图像,然后通过其他方式显示。确切地说,它是一个覆盆子pi,但没有使用GPIO端口连接tft显示器的主显示设备。所以我无法将UI直接渲染到显示设备,但需要创建一个我可以保存在特定位置的图像。到目前为止,我尝试的所有方法都需要主显示设备。

2 个答案:

答案 0 :(得分:3)

是的,可以在屏幕外为图像渲染GUI。

以下是使用JavaFX的示例,示例图像输出如下:

image

该示例的工作原理是将图表渲染到未添加到任何窗口的场景,并且不会显示任何窗口(JavaFX术语中的舞台)。 snapshot方法用于获取节点的快照,然后使用ImageIO实用程序将快照保存到磁盘。

如果底层硬件/软件平台支持,屏幕外场景的渲染将是硬件加速的。

import javafx.application.*;
import javafx.collections.*;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.*;
import javafx.scene.chart.PieChart;
import javafx.scene.image.Image;
import javafx.scene.layout.Region;
import javafx.stage.Stage;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.*;

public class OffscreenImageRecorder extends Application {
    private static final Logger logger = Logger.getLogger(OffscreenImageRecorder.class.getName());

    private static final String IMAGE_TYPE = "png";
    private static final String IMAGE_FILENAME = "image." + IMAGE_TYPE;
    private static final String WORKING_DIR = System.getProperty("user.dir");
    private static final String IMAGE_PATH = new File(WORKING_DIR, IMAGE_FILENAME).getPath();

    private final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
    private final Random random = new Random();

    private final int CHART_SIZE = 400;

    @Override
    public void start(Stage stage) throws IOException {
        Parent chart = createChart();
        Image  image = snapshot(chart);
        exportPng(SwingFXUtils.fromFXImage(image, null), IMAGE_PATH);

        Platform.exit();
    }

    private Parent createChart() {
        // create a chart.
        final PieChart chart = new PieChart();
        ObservableList<PieChart.Data> pieChartData =
                FXCollections.observableArrayList(
                        new PieChart.Data("Grapefruit", random.nextInt(30)),
                        new PieChart.Data("Oranges", random.nextInt(30)),
                        new PieChart.Data("Plums", random.nextInt(30)),
                        new PieChart.Data("Pears", random.nextInt(30)),
                        new PieChart.Data("Apples", random.nextInt(30))
                );
        chart.setData(pieChartData);
        chart.setTitle("Imported Fruits - " + dateFormat.format(new Date()));

        // It is important for snapshots that the chart is not animated
        // otherwise we could get a snapshot of the chart before the
        // data display has been animated in.
        chart.setAnimated(false);

        chart.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
        chart.setPrefSize(CHART_SIZE, CHART_SIZE);
        chart.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);

        return chart;
    }

    private Image snapshot(final Parent sourceNode) {
        // Note: if the source node is not in a scene, css styles will not
        // be applied during a snapshot which may result in incorrect rendering.
        final Scene snapshotScene = new Scene(sourceNode);

        return sourceNode.snapshot(
                new SnapshotParameters(),
                null
        );
    }

    private void exportPng(BufferedImage image, String filename) {
        try {
            ImageIO.write(image, IMAGE_TYPE, new File(filename));

            logger.log(Level.INFO, "Wrote image to: " + filename);
        } catch (IOException ex) {
            logger.log(Level.SEVERE, null, ex);
        }
    }

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

答案 1 :(得分:0)

这有点像黑客,但您可以创建一个框架并将其放置在一个不可见的位置(在本例中使用Swing):

frame = new JFrame("Invisible frame");
frame.setBounds(-1000, 100, 640, 480);