尝试将 javafx WebView 渲染到离屏缓冲区或 FBO

2024-02-13

最终目标是能够以 30fps 或更高的速度记录 WebView 的输出,也许可以通过为 javafx 设置 FBO 来实现?然后我可以以我想要的任何帧速率拉出帧。

我查了一下,在 ViewScene 中发现了 UploadingPainter,这让我觉得这是可能的。困难在于,这似乎是在幕后,对我来说有些新鲜。

有人知道如何让这样的事情发挥作用吗?

这是我在调试过程中遇到的代码:

@Override
public void setStage(GlassStage stage) {
    super.setStage(stage);
    if (stage != null) {
        WindowStage wstage  = (WindowStage)stage;
        if (wstage.needsUpdateWindow() || GraphicsPipeline.getPipeline().isUploading()) {
            if (Pixels.getNativeFormat() != Pixels.Format.BYTE_BGRA_PRE ||
                ByteOrder.nativeOrder() != ByteOrder.LITTLE_ENDIAN) {
                throw new UnsupportedOperationException(UNSUPPORTED_FORMAT);
            }
            painter = new UploadingPainter(this);
        } else {
            painter = new PresentingPainter(this);
        }
        painter.setRoot(getRoot());
        paintRenderJob = new PaintRenderJob(this, PaintCollector.getInstance().getRendered(), painter);
    }
}

下面是在 WebView 中捕获动画的示例。

从网络视图捕获的图像被放置在分页器中以供查看,以便于查看它们。你可以使用SwingFXUtils and ImageIO如果您愿意,可以将它们写到文件中。如果你想将生成的图像放入缓冲区,你可以使用它们PixelReader.

它并不完全按照我想要的方式工作。我想拍摄 WebView 的快照,而不将其放置在可见的舞台上。拍摄不在 Stage 中的节点的快照对于 JavaFX 中的所有其他节点类型都可以正常工作(据我所知),但是,由于某些奇怪的原因,它不适用于 WebView。因此,该示例实际上在显示窗口后面创建了一个新的 Stage,用于显示动画捕获结果的图像序列。我知道这不完全是你想要的,但它就是这样......

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.collections.*;
import javafx.concurrent.Worker;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class WebViewAnimationCaptor extends Application {

    private static final String CAPTURE_URL =
            "https://upload.wikimedia.org/wikipedia/commons/d/dd/Muybridge_race_horse_animated.gif";

    private static final int N_CAPS_PER_SECOND = 10;
    private static final int MAX_CAPTURES = N_CAPS_PER_SECOND * 5;
    private static final int W = 186, H = 124;

    class CaptureResult {
        ObservableList<Image> images = FXCollections.observableArrayList();
        DoubleProperty progress = new SimpleDoubleProperty();
    }

    @Override public void start(Stage stage) {
        CaptureResult captures = captureAnimation(CAPTURE_URL);
        Pane captureViewer = createCaptureViewer(captures);

        stage.setScene(new Scene(captureViewer, W + 40, H + 80));
        stage.show();
    }

    private StackPane createCaptureViewer(CaptureResult captures) {
        ProgressIndicator progressIndicator = new ProgressIndicator();
        progressIndicator.progressProperty().bind(captures.progress);
        progressIndicator.setPrefSize(W, H);

        StackPane stackPane = new StackPane(progressIndicator);
        stackPane.setPadding(new Insets(10));
        if (captures.progress.get() >= 1.0) {
            stackPane.getChildren().setAll(
                createImagePages(captures.images)
            );
        } else {
            captures.progress.addListener((observable, oldValue, newValue) -> {
                if (newValue.doubleValue() >= 1.0) {
                    stackPane.getChildren().setAll(
                            createImagePages(captures.images)
                    );
                }
            });
        }

        return stackPane;
    }

    private Pagination createImagePages(ObservableList<Image> captures) {
        Pagination pagination = new Pagination();
        pagination.setPageFactory(param -> {
            ImageView currentImage = new ImageView();
            currentImage.setImage(
                    param < captures.size()
                            ? captures.get(param)
                            : null
            );

            StackPane pageContent = new StackPane(currentImage);
            pageContent.setPrefSize(W, H);

            return pageContent;
        });

        pagination.setCurrentPageIndex(0);
        pagination.setPageCount(captures.size());
        pagination.setMaxPageIndicatorCount(captures.size());

        return pagination;
    }

    private CaptureResult captureAnimation(final String url) {
        CaptureResult captureResult = new CaptureResult();

        WebView webView = new WebView();
        webView.getEngine().load(url);
        webView.setPrefSize(W, H);

        Stage captureStage = new Stage();
        captureStage.setScene(new Scene(webView, W, H));
        captureStage.show();

        SnapshotParameters snapshotParameters = new SnapshotParameters();
        captureResult.progress.set(0);

        AnimationTimer timer = new AnimationTimer() {
            long last = 0;

            @Override
            public void handle(long now) {
                if (now > last + 1_000_000_000.0 / N_CAPS_PER_SECOND) {
                    last = now;
                    captureResult.images.add(webView.snapshot(snapshotParameters, null));
                    captureResult.progress.setValue(
                            captureResult.images.size() * 1.0 / MAX_CAPTURES
                    );
                }

                if (captureResult.images.size() > MAX_CAPTURES) {
                    captureStage.hide();
                    this.stop();
                }
            }
        };

        webView.getEngine().getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> {
            if (Worker.State.SUCCEEDED.equals(newValue)) {
                timer.start();
            }
        });

        return captureResult;
    }

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

要微调动画序列捕获,您可以查看此JavaFX 中的动画计时器信息 http://svanimpe.be/blog/game-loops-fx.html.

如果你需要让这个东西“无头”,这样就不需要可见的舞台,你可以尝试这个danialfarid 的要点,它执行“Java 图像捕获、HTML 快照、HTML 到图像” https://gist.github.com/danialfarid/2ddbab04803ae4fd2dca(虽然我没有写链接的要点,也没有尝试过)。


对我来说,无头是关键。有问题的(linux)机器在完全无头的服务器场中运行。至于要点,我在那里看到了一个 show() ,但我会仔细看看以确保我没有忽略某些东西。

要点是基于用于 JavaFX 系统的 Monocle 玻璃渲染工具包 https://wiki.openjdk.java.net/display/OpenJFX/Monocle。该工具包支持任何系统上基于软件的无头渲染。

来自单片眼镜文档 https://wiki.openjdk.java.net/download/attachments/17957197/Monocle.pdf?api=v2:

无头端口不执行任何操作。它适用于您想要在没有图形、输入或平台依赖性的情况下运行 JavaFX 的情况。渲染仍然发生,只是没有显示在屏幕上。

无头端口使用 InputDeviceRegistry 的 LinuxInputDeviceRegistry 实现。然而,无头端口根本不访问任何实际的 Linux 设备或任何本机 API;它在设备模拟模式下使用 Linux 输入注册表。这使得即使在非 Linux 平台上也可以模拟 Linux 设备输入。 tests/system/src/test/java/com/sun/glass/ui/monocle/input 中的测试广泛使用了此功能。

如果基于 JavaFX Monocle 的方法最终不适合您,您可以考虑另一个(与 JavaFX 无关)无头 HTML 渲染工具包,例如PhantomJS http://phantomjs.org.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

尝试将 javafx WebView 渲染到离屏缓冲区或 FBO 的相关文章

  • JavaFX GridPane:如果内容被禁用且不可见则缩小

    如果 GridPane 行的内容既禁用又不可见 是否可以缩小该行 当我将节点设置为disable true和visible false时 单元格仍然占用空间 如果我有 8 行 并且只有第一行和最后一行可见 我不希望空行占用太多空间 好像只有
  • 由于 MultiReleaseException,jdeps 无法打印模块 deps

    我们有一个基于 JavaFX 的应用程序 它是not模块化 有原因 涉及遗留库 但我们使用构建自定义运行时jdeps and jlink 我们最近重写了该应用程序并添加了一些新的依赖项 并删除了其他依赖项 现在 正在构建应用程序的脚本突然停
  • JavaFX Span Tableview 通过 MapEntries 合并单元格

    你好 我有以下地图 Map
  • PreLoader 的多线程 - JavaFX

    我正在开发一个 JavaFX 应用程序 需要在启动主应用程序阶段之前从文件中加载资源 我完成此任务的解决方案是使用 PreLoader 以便用户在加载资源之前无法与应用程序交互 非常标准的东西 我有一个扩展 PreLoader 类的类 该类
  • JavaFX 8:拦截应用程序“退出”

    为了验证用户所做的所有更改是否已保存 我想拦截 JavaFX 应用程序的退出 退出 是否有一种通用的方法来实现这一目标 例如覆盖事件 或者还有更多方法吗 正如他们已经说过的 这是通过拦截来完成的WindowEvent WINDOW CLOS
  • 可以将矩形设置为显示边框吗?

    以下应用 public class Temp extends Application Override public void start Stage primaryStage StackPane root new StackPane Re
  • 如何在javafx中通过事件传递参数?

    我有以下示例 我想将参数 文本 与事件一起传递 当单击按钮 bla 时 我该怎么做 EventHandler
  • JavaFX 8 DatePicker 风格

    如何更改JavaFX 8中DatePicker中日历的样式 我查看了 modena 文件中的所有默认样式 但没有找到 DatePicker 的类 有人知道该怎么做吗 例如 将标题颜色更改为蓝色 默认样式如下 您可以找到以下的 cssDate
  • 学习 Java Native Access 时出现 com.sun.glass.ui 包错误

    我正在尝试在 JavaFX 项目中使用 Undecorated 阶段 stage initStyle StageStyle UNDECORATED 它是一个模块化的 Gradle 项目 它也是一个多项目构建 https guides gra
  • JavaFX - 如何检测 Windows 注销/关闭请求?

    我有一个应用程序必须在退出时处理一些方法 但是 当用户在没有先关闭我的应用程序的情况下关闭 Windows 时 Windows 会终止该应用程序并且不会运行关闭方法 如何检测用户何时请求关闭或注销 Windows 我需要运行的方法需要几毫秒
  • 尝试让 GUI 使用 arrayList 在牌组中打印随机卡

    所以我目前正在用java开发一个卡牌战争游戏 我试图让 GUI 屏幕使用 arrayList 从一组卡片图像中打印 2 张随机卡片 必须使用它进行分配 卡片图像文件名为 1 png 2 png 52 png 并存储在 image card
  • JavaFX TabPane - 每个选项卡一个控制器

    我是 Fx 新手 我有一个带有 10 个选项卡的 TabPanel 每个选项卡都有很多控件 图表 按钮等 我想要的是为每个选项卡分配一个控制器 SceneBuilder 只让我为整个视图分配一个控制器 我的意思是 只有顶部面板 根 具有 控
  • JavaFX TextField cancelEdit 未按预期工作

    I have a TextField https docs oracle com javase 8 javafx api javafx scene control TextField html and I would like the co
  • JavaFx ComboBox 绑定混乱

    我有一个 I18N 实现 它通过属性绑定 JavaFX UI 元素 例如 def translateLabel l Label key String args Any Unit l textProperty bind createStrin
  • 从剪贴板获取图像 Awt 与 FX

    最近 我们的 Java FX 应用程序无法再从剪贴板读取图像 例如 用户在 Microsofts Paint 中选择图像的一部分并按复制 我不是在谈论复制的图像文件 它们工作得很好 我很确定它过去已经有效 但我仍然需要验证这一点 尽管如此
  • JavaFx 中装饰且不可移动的舞台

    我想在 JavaFx 中创建一个装饰舞台 它也将不可移动 我正在从另一个控制器类创建这个阶段 我能够创造和展示舞台 但它是自由移动的 我怎样才能创建这个 非常感谢帮助和建议 我把打开新关卡的方法贴出来 private void addRec
  • 将 JavaFX FXML 对象分组在一起

    非常具有描述性和信息性的答案将从我这里获得价值 50 声望的赏金 我正在 JavaFX 中开发一个应用程序 对于视图 我使用 FXML
  • JavaFX 中 WebView 的性能

    我有一个 HTML5 UI 和一个 Java 后端 并且希望避免在纯 java 中重建 HTML ui 所以我的想法是运行本地 Web 服务器并使用 WebView 在 本机 窗口中呈现它 解决方案似乎是使用可以嵌入到 swing 中的 J
  • 即使在轴上进行自动量程调整,我也可以保留积分刻度线吗?

    我 偷 了一些代码here http fxexperience com 2012 01 curve fitting and styling areachart 拥有一个AreaChart我在 FXML 中使用了 平滑线条 它的工作原理如下
  • Javafx 从 TextField 获取输入

    这是我当前的代码 它所做的只是为我制作的计算器设置一个 GUI 界面 我希望用户输入两个值 然后当按下 Sum 按钮时 它将两个值加在一起并将其显示在 Sum 文本字段中 我正在尝试使用 JavaFX 如果您能提供一些帮助 我将不胜感激 i

随机推荐