渲染
本指南描述了不同渲染模式的工作原理,并探讨了它们的局限性。
概述
Fuzio 在 BrowserView 中渲染 Web 内容的方式由渲染模式决定。Fuzio 提供两种渲染模式:HARDWARE_ACCELERATED(硬件加速)和 OFF_SCREEN(离屏)。
你需要在创建 Engine 实例时指定渲染模式,并且之后无法再更改。在同一个 Engine 中创建的所有浏览器都会使用相同的渲染模式。
import static tech.fuzio.engine.RenderingMode.HARDWARE_ACCELERATED;
...
var engine = Engine.newInstance(HARDWARE_ACCELERATED);
import tech.fuzio.engine.RenderingMode.HARDWARE_ACCELERATED
...
val engine = Engine(HARDWARE_ACCELERATED)
选择合适的渲染模式会影响:
- 渲染性能,
- 整体资源消耗,
- 以及 Fuzio 处理用户输入的方式。
当 GPU 可用时,Chromium 会在两种渲染模式下都使用硬件加速。当 GPU 不可用时,Chromium 会切换为基于 CPU 的软件渲染 —— 同样适用于两种渲染模式。
硬件加速模式
在该渲染模式下,Chromium 会将 Web 内容直接渲染到一个特殊的渲染表面上,该表面由库放置在 BrowserView之上。该表面的类型取决于操作系统。
HARDWARE_ACCELERATED 渲染模式的架构。
在 Windows 和 Linux 上,Fuzio 会将一个原生 Chromium 窗口嵌入到 Java 应用中。这样 Chromium 就可以在自己的窗口中渲染内容,性能与独立浏览器相同。这个嵌入窗口对最终用户来说是“不可察觉”的:它没有 Chromium 的 UI 元素、没有任务栏图标,也不具备普通窗口的其他特征。

嵌入窗口看起来与其他 UI 组件无异。
在 macOS 上,Fuzio 创建一个原生的 CARemoteLayer 表面,并在 Java 进程与 Chromium 进程之间共享。该表面会被嵌入到 Java 窗口中,Chromium 直接将内容渲染到该表面上。
在 macOS 上需要采用不同的实现方式,因为该操作系统只允许在同一进程内对原生窗口进行重新挂载(re-parent)。由于 Fuzio 会在单独的进程中启动 Chromium,因此 Chromium 窗口无法像在 Windows 和 Linux 上那样附加到 Java 窗口层级结构中。
输入处理
在 Windows 和 Linux 上,输入由 Chromium 处理。由于用户直接与 Chromium 窗口交互,操作系统将键盘、无障碍功能和其他输入事件发送到那里,绕过了 Java 窗口。这确保了与 Google Chrome 完全相同的输入反应。
在 macOS 上,输入由 Java UI 工具包处理。详情请参见离屏模式中的“输入处理”部分。
你可以在 Java 代码中拦截鼠标、键盘和触摸事件。请参阅事件拦截指南了解如何实现。
局限性
与其他 UI 组件的重叠问题
无论在哪种操作系统上,用于渲染 Web 内容的表面始终位于 Java 窗口之上。因此,普通的 UI 组件无法显示在 BrowserView 之上,因为它们的区域会被渲染表面遮挡。
特别是在 Swing 中将 BrowserView 放入 JInternalFrame 或 JLayeredPane,或在 JavaFX 中放入 StackPane 时,应避免使用 HARDWARE_ACCELERATED 渲染模式。

两个 BrowserView 组件——处于不同模式——与 JPanel 重叠。
JavaFX 中的透明窗口
在 Windows 上的 JavaFX 中,HARDWARE_ACCELERATED 渲染模式与 StageStyle.TRANSPARENT 样式不兼容。要显示透明的 Stage,JavaFX 会使用重叠窗口,但这种方式无法与嵌入的 Chromium 窗口一起工作。
离屏模式
在该渲染模式下,Chromium 会使用 GPU 渲染内容,然后库会将像素复制到 Java 进程内存中的离屏缓冲区。BrowserView 作为一个轻量级组件,从该缓冲区读取像素,并使用标准 UI 工具包的绘图接口(例如 Swing 中的 Graphics2D)进行显示。
OFF_SCREEN 渲染模式的架构。
输入处理
默认情况下,输入事件由 Java 窗口处理。当发生输入事件时,操作系统会将事件分发给 Java UI 工具包,随后传递给 BrowserView,再由 BrowserView 转发给 Chromium,最终送达网页。
局限性
原生输入处理
在事件处理的每一个阶段,事件都会从一种数据结构转换为另一种。由于不同子系统中的数据结构并不能完全匹配,部分数据可能会丢失或被误解释。这会导致在极少数情况下,Fuzio 中的用户交互产生的 JavaScript 事件与 Chromium 中相同交互产生的事件不一致。
我们引入了一个实验性功能,可以将输入事件直接从操作系统转发给 Chromium。这样一来,Fuzio 中生成的 JavaScript 事件将始终与 Chromium 中保持一致。
要启用该功能,请使用以下系统属性:
System.setProperty("fuzio.native.input.enabled", "true");
触摸事件
Swing、JavaFX、SWT 和 Compose 对触摸的支持并不完整。因此,一些 Chromium 的手势(例如双指缩放)在 Fuzio 中无法使用 —— 在 Windows 和 Linux 的 OFF_SCREEN 模式下,以及在 macOS 的两种模式下都是如此。
性能
在 HARDWARE_ACCELERATED 模式下,GPU 的使用情况会直接影响渲染性能,因为 Chromium 是直接将内容渲染到表面上的。该模式提供与独立版 Google Chrome 相同的渲染性能。在大多数平台上,它可以以 60 FPS 播放 4K 视频。
在 OFF_SCREEN 模式下,使用 GPU 可以改善整体资源消耗,但对渲染性能的影响不大。在该模式下,性能瓶颈在于跨进程复制像素,这是一个受 CPU 限制的操作。该模式在大多数情况下都能提供良好的渲染性能。对于对渲染性能要求极高的应用,建议使用 HARDWARE_ACCELERATED 模式。
无头模式
当你不需要在 UI 中显示浏览器时,建议使用 HARDWARE_ACCELERATED 渲染模式。原因是即使浏览器没有显示,OFF_SCREEN 模式也可能会消耗额外的 CPU 和内存。
无论使用哪种渲染模式,Fuzio 在 BrowserView 之外都能保持完整功能。例如,你可以在任意渲染模式下对一个独立的 Browser 进行截图:
var engine = Engine.newInstance(HARDWARE_ACCELERATED);
var browser = engine.newBrowser();
browser.resize(1920, 1080);
browser.navigation().loadUrlAndWait("https://html5test.jiku.co");
// 获取网页的位图,尺寸为 1920x1080。
var bitmap = browser.bitmap();
import tech.fuzio.dsl.Engine
...
val engine = Engine(HARDWARE_ACCELERATED)
val browser = engine.newBrowser()
browser.resize(1920, 1080)
browser.navigation().loadUrlAndWait("https://html5test.jiku.co")
// 获取网页的位图,尺寸为 1920x1080。
val bitmap = browser.bitmap()

