GTK 的色彩

一切都用色彩更好。如果它是 HDR,那就更好了

1708 年的七色和十二色色环,归功于克劳德·布特

在这篇文章中,我们将概述 GTK 中色彩处理的现状,而不会深入探讨色彩学和色彩科学的细节。

这项工作的高级目标是实现对 HDR 内容和色彩管理工作流程的正确处理。

最初,是 sRGB

到目前为止,GTK 中的颜色始终被假定为在 sRGB 色彩空间中。GdkRGBA 结构体被定义为指定 sRGB 颜色。

sRGB 在 20 年前是一件很棒的事情,但世界正在转向其他色彩空间,这些色彩空间包括更广泛的色调(例如 Display-P3)或更大的动态范围(例如 BT.2100-PQ)。

在将此问题列入我们的议程相当一段时间后(对该主题的初步调查发生在 2021 年),我们终于开始着手为更加色彩缤纷的未来奠定基础。

即将推出:GdkColorState

今年早些时候,我们合并了 Alice 的一些出色工作,以支持 CSS 引擎中的现代颜色语法和色彩空间。在 CSS 中拥有富有表现力的颜色很棒,但如果我们的所有渲染机制仍然需要以 sRGB 指定颜色,那就无法真正发挥作用。

因此,在过去的一两个月里,我们增加了在定义明确的色彩空间中进行渲染的支持,并引入了一个表示这些色彩空间的对象。它被称为 GdkColorState (不要问我为什么名称从空间更改为状态)。

截至目前,GdkColorState 可以表示 sRGB 和 BT.2100-PQ,以及它们的线性化变体。你还不能用这些对象做太多事情,它们主要由我们的渲染器在内部使用。

不过,你可以做的一件事是,通过设置

GDK_DEBUG=linear

环境变量,尝试线性色彩空间中的渲染效果。

这是我们最终需要做出的改变,以便产生正确且易于理解的结果,尤其是在处理 HDR 内容时。

在线性颜色状态下进行所有合成看起来确实略有不同,因此我们希望将此更改推迟到所有周围工作完成后,并在开发周期的开始进行更改,以便每个人都有时间进行调整。

新协议

Wayland 色彩管理协议已经酝酿了很长时间,但它有望接近离开实验阶段 —— kwin 已经支持它,并且还有一个 mutter 分支。

我们已经添加了对 xx-color-management-v4 协议的支持,因此我们可以从合成器获得首选的颜色状态(如果它支持该协议),并且我们可以告诉它我们生成的帧的颜色状态。

你的合成器今天不太可能首选像 BT.2100-PQ 这样的 HDR 色彩状态(除非你找到隐藏的开关来打开实验性 HDR 支持),但是你可以通过设置

GDK_DEBUG=hdr

环境变量,尝试线性色彩空间中的渲染效果。

尝试在 GTK 以该色彩状态渲染时的效果。请注意,这不会使 HDR 内容显示在你的屏幕上 —— 我们在 HDR 中进行渲染,并在最后一步将最终帧转换回 sRGB。要使 HDR 内容显示在你的屏幕上,需要一个接受此类内容的合成器。

未来:更多的色彩状态和线性合成

我们仍然有一个很长待办事项列表,以便在 GTK 中全面开发色彩支持。

重点包括

  • 更多色彩状态(包括用于更好渐变的 OKLCH 和用于视频内容的 YUV)
  • 色彩状态感知渲染 API(GdkColor 和新的 GtkSnapshot API)
  • 将 CSS 色彩状态信息传递给渲染器
  • 从 gstreamer 传播色彩状态信息(用于 HDR 等)
  • 切换到线性合成

其中一些有望及时赶上 GTK 4.16 版本。

总结

更好的色彩支持即将到来 GTK。

图形卸载的继续

我们去年秋天首次引入了对 dmabufs 和图形卸载的支持,它包含在 GTK 4.14 中。自从我们上次谈论以来,发生了更多的改进,因此现在是进行另一次更新的时候了。

转换

当你旋转显示器时,它的宽高比会从横向(例如,1920 x 1200)变为纵向(1200 x 1920)。但是合成器处理的帧缓冲区仍然是 1920 x 1200。

在某些其他情况下,内容可能需要在显示之前进行一些转换。例如,当显示来自前置摄像头的视频流时。

D4 cycle graph

到目前为止,GTK 将翻转或旋转的纹理视为不可卸载的,但在最近的一些改进之后,我们现在将在卸载内容时将此类转换传递给合成器。

快速绕道

这是一个很好的机会来解释 GTK 转换 API 依赖于 将其分类为有用的类别,例如 2D 平移、缩放、旋转或任意 3D 转换。

因此,最好使用特定的方法,例如 gsk_transform_rotategsk_transform_translate,而不是自己计算转换矩阵并使用 gsk_transform_matrix,后者不会提供任何关于我们正在处理的转换类型的信息。如果没有这些信息,我们就会错过像上面描述的转换卸载之类的优化。

Benjamin 在此处简要概述了我们的转换方法。

纹理

卸载工作的最初目标是启用 dmabuf 内容的直通。通常,我们得到的并不是那样。我们仍然更常见的是在 GL 纹理中接收内容(例如,来自 gstreamer)。值得庆幸的是,mesa 具有 GL 扩展,允许我们将纹理导出为 dmabufs。我们最近使用它来为 GL 纹理启用卸载。

请注意,这些扩展可能并非在所有地方都可用,并且各个驱动程序在 dmabuf 支持方面可能存在问题。

总结

在即将到来的 GTK 4.16 版本中,图形卸载应该会更频繁地工作。

你今天可以尝试这些改进,例如,在新的 Showtime 应用程序的 每夜构建

Showtime

图形卸载回顾

我们去年秋天首次引入了对 dmabufs 和 图形卸载的支持,它包含在 GTK 4.14 中。自那时以来,发生了一些改进,因此现在是更新的时候了。

堆栈上的改进

GStreamer 1.24 版本改进了对显式修饰符的支持,并且 GTK 中的 GStreamer 媒体后端已更新为从 GStreamer 请求 dmabufs。

GStreamer 端发生的另一件事是,dmabufs 有时会带有填充:在这种情况下,GStreamer 会为我们提供带有视口的缓冲区,并期望我们仅显示该缓冲区的一部分。有时,这是为了适应硬件解码器的步幅和大小要求而必需的。

GTK 4.14 在卸载时支持此功能,并且仅显示 dmabuf 的视口指示的部分。

GTK 内部的改进

我们为 GTK 4.14 合并了新的 GSK 渲染器。新的渲染器以与旧的 gl 渲染器相同的方式支持 dmabufs。此外,新的 Vulkan 渲染器在渲染到纹理时会生成 dmabufs。

在 GTK 4.16 中,GtkGLArea 小部件也将提供 dmabuf 纹理(如果可以),因此你可以将其放入 GtkGraphicsOffload 小部件中,以将其输出直接发送到合成器。

你可以在 git main 中的 gtk4-demo 中的 shadertoy 演示中看到这一点。

Shadertoy 演示,其中卸载的图形周围有金色轮廓

改进的合成器交互

图形卸载的一个好处是,合成器可以将 dmabuf 传递到内核的 KMS api,而无需额外的复制或合成。这被称为直接扫描输出,它有助于降低功耗,因为 GPU 的大部分部件都不会使用。

只有当 dmabuf 附加到全屏表面并具有完全覆盖它的正确尺寸时,合成器才能执行此操作。如果它没有完全覆盖,则合成器需要一些保证,即可以将外部部分保留为黑色。

客户端提供这种保证的一种方法是将一个特殊构造的黑色缓冲区附加到具有 dmabuf 附加的表面之下的一个表面。如果 GSK 在渲染节点树中找到黑色颜色节点,它现在会这样做,如果你设置了“black-background”属性,GtkGraphicsOffload 小部件会将该颜色放在那里。这应该会大大增加你在播放全屏视频时享受直接扫描输出的好处的机会。

Developer trying to make sense of graphics offload
具有全屏黑色背景的卸载内容

在为 GTK 4.16 实现此功能时,我们发现 mutter 对单像素缓冲区的支持存在一些问题,但这些问题已得到快速修复。

要查看 GTK4 视频播放器中的图形卸载和直接扫描输出的实际效果,您可以尝试使用轻量级视频播放器

如果您想了解图形卸载是否在您的系统上工作,或者调试它为什么不工作,Benjamin 最近的这篇帖子非常有帮助。

总结

GTK 4 继续改进以实现高效的视频播放,并推动该领域在整个堆栈中的改进。

非常感谢 Robert Mader 为推动这一切向前发展。❤️

关于分数缩放、字体和微调

GTK 4.14 即将发布,其中包含今年早些时候引入的新渲染器。

新渲染器对分数缩放的支持得到了很大改进——在我的系统上,我现在使用 125% 的缩放而不是“大文本”设置,我发现这对于我的需求来说效果很好。

神奇数字

自 4.0 版本以来,GTK 一直在倡导线性布局。

其理念是,我们只需将字形放置在坐标告诉我们的位置,如果这是一个像素之间某个位置的分数位置,那就这样,我们可以很好地在该偏移处渲染轮廓。这种方法有效——如果你的输出设备具有足够高的分辨率(任何高于 240 dpi 的分辨率都应该可以)。可悲的是,我们并没有生活在一个大多数笔记本电脑屏幕都具有这种分辨率的世界中,所以我们不能忽视像素。

因此,我们添加了gtk-hint-font-metrics 设置,该设置强制文本布局将内容舍入为整数位置。这不太适合分数缩放,因为舍入发生在应用程序像素中,而我们真正需要的是整数设备像素位置才能产生清晰的结果。

应用程序像素与设备像素

常见的分数缩放比例为 125%、150%、175%、200% 和 225%。在这些缩放比例下(200% 除外),大多数应用程序像素边界与设备像素边界不对齐。

现在怎么办?

新的渲染器为我们提供了一个机会,重新审视字体渲染的主题,并对微调选项的机制以及它们如何从 GTK 通过 Pango 和 cairo 传递下来,然后最终以渲染目标 + 加载标志的组合形式出现在 freetype 中进行了一些研究

微调样式和抗锯齿选项转换为渲染模式和加载标志

新的渲染器认识到,在处理字形时,有两种基本的操作模式

  • 优化统一间距
  • 优化清晰渲染

前者导致子像素定位和无微调渲染,后者导致微调渲染和放置在整数像素位置的字形(因为这是自动微调器所期望的)。

我们通过查看字体选项来确定我们处于哪种情况。如果它们告诉我们进行微调,我们将字形位置在 y 方向上舍入为整数设备像素。为什么只舍入 y?自动微调器仅在垂直方向上应用微调,而水平方向是子像素位置增加的分辨率最有帮助的地方。如果我们不进行微调,那么我们对 x 和 y 都使用子像素位置,就像旧渲染器一样(值得注意的是,新渲染器在设备像素中使用子像素位置)。

比较

文本渲染差异始终是微妙的,并且在某种程度上是口味和偏好的问题。因此,这些屏幕截图应该持保留态度——最好自己尝试一下新的渲染器。

在 125% 缩放比例下渲染的文本,旧渲染器
在 125% 缩放比例下渲染的文本,新渲染器

这两个渲染都是在 125% 的缩放比例下完成的,启用了微调(但请注意,旧渲染器通过在 200% 下渲染并依赖合成器来缩小内容来处理 125%)。

以下是一些细节:T 和 e 的水平条在各行中保持一致,即使我们仍然允许字形在水平方向上以子像素位置移动。

一致的垂直位置
T 和 e 的实例,旧渲染器
T 和 e 的实例,新渲染器

总结

GTK 4.14 中的新渲染器应该会产生更清晰的字体渲染,尤其是在分数缩放的情况下。

请尝试一下,并告诉我们您的想法。

更新:关于子像素渲染

我应该预料到会提出这个问题,所以这里是一个快速解答

我们没有在 GTK 4 中使用子像素渲染(又名 ClearType 或 rgb 抗锯齿),因为我们的合成没有组件 alpha。我们的字体抗锯齿始终是灰度的。请注意,子像素渲染与子像素定位是不同的。

GTK 黑客马拉松更新

正如我们经常做的那样,GTK 团队的一些成员和更广泛的 GNOME 社区聚集在一起,在 FOSDEM 之前进行了为期两天的黑客马拉松。

今年,我们的目标是在可访问性和输入主题上取得进展。以下是我们所取得的成就的快速总结。

辅助功能

  • 我们同意合并 Georges 编写的 webkit 辅助功能的套接字实现
  • 我们同意 Lukáš 建议的可访问通知api 没问题
  • 我们完成了GtkAccessibleText 接口,并将我们的内部实现迁移到该接口
  • 我们讨论了基于 AccessKit 的 a11y 后端的可能性

输入

  • Carlos 回顾了将未处理的事件传递回系统(在 macOS 上)的合并请求
  • 我们查看了输入 api 中 X11 风格时间戳的残余,并决定提供采用事件的替代方案

Wayland

  • Carlos 开始将私有 gtk-shell 协议转换为单独的协议

感谢 GNOME 基金会对本次活动的支持。❤️

GTK 的新渲染器

最近,GTK 不仅获得了一个,而且获得了两个新的渲染器:一个用于 GL,一个用于 Vulkan。

由于命名很困难,我们重用了现有名称,并将其称为“ngl”和“vulkan”。它们由相同的构建,因此我们也将其称为“统一”渲染器。

但它们令人兴奋的地方是什么?

单个源

如前所述,这两个渲染器由相同的源构建。它被建模为遵循 Vulkan api,并使用一些抽象来涵盖 Vulkan 和 GL 之间的差异(更具体地说,是 GL 3.3+ 和 GLES 3.0+)。这使我们可以共享大部分用于遍历场景图、维护变换和其他状态、缓存纹理和字形的基础设施,并且可以更容易地保持两个渲染器的更新和同步。

这种统一的方法是否可以进一步扩展,以涵盖 macOS 上的基于 Metal 的渲染器或 Windows 上的基于 DirectX 的渲染器?有可能。Vulkan/GL 组合的优势在于它们基本上共享相同的着色器语言(GLSL,有一些变化)。Metal 或 DirectX 则并非如此。对于这些平台,我们需要复制着色器或使用像SPIRV-Cross这样的翻译工具。

如果您对此类事情感到兴奋,欢迎提供帮助。

实现细节

旧的 GL 渲染器为每个渲染节点类型使用简单的着色器,并且经常为更复杂的内容求助于离屏渲染。统一渲染器也具有(更强大的)每个节点的着色器,但它们不会依赖于离屏渲染,而是使用一个复杂的着色器来解释缓冲区中的数据。在游戏编程中,这种方法被称为ubershader

统一渲染器实现不如旧的 GL 渲染器优化,并且编写时侧重于正确性和可维护性。因此,它可以正确处理更多样化的渲染节点树。

这是一个看起来无害的示例

repeat {
  bounds: 0 0 50 50;
  child: border {
    outline: 0 0 4.3 4.3;
    widths: 1.3;
  }
}
gl(左)ngl(右)
近距离视图

新功能

如果没有一些切实的利益,我们就不会完成所有这些工作。当然,有一些新的功能和特性。让我们看看一些

抗锯齿。旧的 GL 渲染器的一个大问题是它会丢失精细的细节。如果某个东西足够小,以至于落在单像素行的边界之间,它就会消失。特别是这会影响下划线,例如助记符。统一渲染器通过进行抗锯齿来更好地处理这种情况。这不仅有助于保留精细细节,还有助于防止基元的锯齿状轮廓。

GL 与 NGL 的近距离对比

分数缩放。抗锯齿也是我们正确处理分数缩放的基础。如果你的 1200 × 800 窗口设置为缩放至 125%,使用统一渲染器,我们将使用 1500 × 1000 大小的帧缓冲区,而不是让合成器缩小 2400 × 1600 的图像。这样像素会少得多,图像也更清晰。

任意渐变。旧的 GL 渲染器处理线性、径向和圆锥渐变时最多支持 6 个颜色停止点。统一渲染器允许无限数量的颜色停止点。新的渲染器还对渐变应用抗锯齿,因此尖锐的边缘将具有平滑的线条。

具有 64 个颜色停止点的线性渐变

Dmabufs。简单偏离一下新渲染器,我们研究了 dmabuf 支持和图形卸载,相关内容在去年秋季发布。新的渲染器支持这一点,并将其扩展到在通过 render_texture API 请求生成纹理时创建 dmabuf(目前仅限于 Vulkan 渲染器)。

有任何尖锐的边缘吗?

通常情况下,新功能会带来新的潜在陷阱。作为应用程序开发者,以下是一些需要注意的事项

不再有 glshader 节点。是的,它们为 4.0 版本带来了一些炫酷的演示,但它们与旧的 GL 渲染器紧密相关,因为它们对该渲染器公开的 GLSL API 做出了假设。因此,新的渲染器不支持它们。

文档中已经警告过您

如果出现问题,此函数将返回 FALSE 并报告错误。在依赖着色器进行渲染之前,您应该使用此函数,如果失败,则使用更简单的着色器或不使用着色器的回退方案。

值得庆幸的是,许多 glshader 节点的使用不再必要,因为自 4.0 版本以来,GTK 已经获得了新功能,例如蒙版节点和对直通 alpha 纹理的支持。

分数位置。旧的 GL 渲染器会对位置进行四舍五入,因此您可以侥幸使用分数位置。新的渲染器会将物体放置在您指定的位置。这有时会产生意想不到的后果,因此您应该留意并确保您的位置在它们应该在的地方。

特别要注意 cairo 样式的绘图,您将线条放置在半像素位置,以便它们精确地填充一行像素。

驱动程序问题。新的渲染器正在以新的和不同的方式使用图形驱动程序,因此有可能触发驱动程序方面的问题。

请将您看到的任何问题提交给 GTK,即使它们看起来像是驱动程序问题,因为这有助于我们了解新代码在各种驱动程序和硬件上的工作情况(或糟糕情况)。

但它更快吗?

不,新的渲染器(目前)并不更快。

旧的 GL 渲染器经过了大量的速度优化。它还使用更简单的着色器,并且不进行抗锯齿等功能所需的数学运算。我们希望最终让新的渲染器更快,但新功能和正确性使其非常令人兴奋,即使在我们达到这个目标之前也是如此。所有基于 GPU 的渲染器都足够快,可以以 60 或 144 fps 的速度渲染当今的 GTK 应用程序。

即便如此,Vulkan 渲染器在一些非科学的基准测试中,接近甚至超过了旧的 GL 渲染器。新的 GL 渲染器出于我们尚未追踪到的某种原因速度较慢。

新的默认设置

在刚刚发布的 4.13.6 快照中,我们已将 ngl 渲染器设为新的默认设置。这是一个试探气球 —— 渲染器需要通过不同的应用程序进行更广泛的测试,以验证它们是否已准备好投入生产。如果出现重大问题,我们可以将 4.14 版本的默认渲染器恢复为 gl 渲染器。

我们决定暂不将 Vulkan 渲染器设为默认设置,因为它在一些应用程序集成方面落后于 GL 渲染器:webkit GTK4 端口适用于 GL,而不适用于 Vulkan,并且 GtkGLArea 和 GtkMediaStream 当前都会生成 Vulkan 渲染器无法直接导入的 GL 纹理。所有这些问题都希望在不久的将来得到解决,届时我们将重新审视默认渲染器的决定。

如果您在非常旧的硬件上使用 GTK,您可能更适合使用旧的 GL 渲染器,因为它对 GPU 的要求更少。您可以使用 GSK_RENDERER 环境变量覆盖渲染器选择

GSK_RENDERER=gl

未来的计划和可能性

新的渲染器是实现我们长期以来一直希望拥有的功能(例如)的良好基础

  • 正确的颜色处理(包括 HDR)
  • GPU 上的路径渲染
  • 可能包括字形渲染
  • 主线程外的渲染
  • 性能(在旧的和性能较低的设备上)

其中一些将是我们近期和中期工作的重点。

总结

新的渲染器具有一些令人兴奋的功能,未来还会推出更多功能。

请尝试一下,并告诉我们哪些对您有效,哪些无效。

图形卸载简介

GTK 团队的一些成员在过去一个月左右的时间里一直在探索 Linux 内核图形 API 的世界,特别是 dmabufs。我们带着一些沮丧和一些成功从这次冒险中归来。

什么是 dmabuf?

dmabuf 是内核空间中的一个内存缓冲区,由文件描述符标识。其理念是您无需到处复制大量像素数据,而只需在内核子系统之间传递文件描述符即可。

当然,现实情况比这个美好的景象要复杂得多:内存可能是设备内存,无法以与“普通”内存相同的方式访问,并且可能存在多个缓冲区(以及多个文件描述符),因为图形数据通常被分成多个平面(例如,RGB 和 A 可能分开,或者 Y 和 UV)。

为什么 dmabufs 有用?

我已经提到我们希望避免复制像素数据并将其馈送到 GTK 合成管道(对于 4K 视频,每个帧可能需要处理相当多的数据)。

这种优化很重要的用例是那些长时间显示频繁变化的内容,例如

  • 视频播放器
  • 虚拟机
  • 流媒体
  • 屏幕录像
  • 游戏

在最佳情况下,如果合成器支持直接扫描输出并且 dmabuf 适合,我们也可以避免将数据馈送到合成器的合成管道。特别是在移动系统中,这可以完全避免使用 GPU,从而降低功耗。

详细信息

自 4.0 版本以来,GTK 已经在使用 dmabufs:当合成帧时,GTK 会将所有渲染节点(通常每个小部件有多个)转换为 GL 命令,将其发送到 GPU,然后 mesa 会将生成的纹理导出为 dmabuf 并将其附加到我们的 Wayland 表面。

但是,如果您的 UI 中唯一更改的内容是已经在 dmabuf 中的视频内容,那么最好避免绕道 GL,而是通过向合成器提供 dmabuf 的文件描述符,直接将数据传递给合成器。

Wayland 具有子表面的概念,允许应用程序将其一些合成需求推迟到合成器:应用程序将缓冲区附加到每个(子)表面,而合成器的任务是将它们组合在一起。

通过当前 git 主分支中的代码,GTK 将根据需要创建子表面,以便将 dmabufs 直接传递给合成器。我们可以通过两种不同的方式来实现:如果没有在 dmabuf 之上绘制任何内容(没有圆角或覆盖控件),那么我们可以将子表面堆叠在主表面之上,而无需更改任何视觉效果。

这是理想的情况,因为它使合成器能够设置直接扫描输出,从而为我们提供从视频解码器到显示器的零拷贝路径。

如果有一些内容绘制在视频之上,我们可能无法获得该内容,但我们仍然可以通过将带有视频的子表面放置在主表面之下并在主表面上戳一个半透明的孔让其透过来获得让合成器进行合成的好处。

圆形的播放按钮是强制将子表面放置在此处的主表面下方的原因。

GTK 会自动且透明地为每个帧选择这些模式,而无需应用程序开发人员执行任何操作。一旦播放按钮出现在帧中,我们就将子表面放置在下方,一旦视频被圆角裁剪,我们将完全停止卸载。当然,卸载的优势也会消失。

GTK 检查器中的图形卸载可视化会显示这些变化

最初,摄像头流未被卸载,因为圆角将其裁剪。洋红色轮廓表示该流被卸载到主表面下方的子表面(因为视频控件位于其顶部)。金色轮廓表示子表面位于主表面上方。

您如何使用它?

GTK 4.14 将引入一个 GtkGraphicsOffload 小部件,它的唯一任务是提示 GTK 尝试卸载其子小部件的内容,方法是将其附加到子表面,而不是像通常那样让 GSK 处理它。

为了创建适合卸载的内容,新的 GdkDmabufTextureBuilder 将 dmabuf 封装在 GdkTexture 对象中。dmabuf 的典型来源包括 pipewire、video4linux 或 gstreamer。gstreamer 中的 dmabuf 支持在即将发布的 1.24 版本中将更加稳定。

在测试此代码时,我们使用了 Georges Basile Stavracas Neto 为 pipewire 编写的 GtkMediaStream 实现,该实现在 pipewire-media-stream 和 Christian Hergert 及 Bilal Elmoussaoui 编写的 libmks 中可以找到。

有哪些限制?

目前,图形卸载仅适用于 Linux 上的 Wayland。我们有一些希望能够在 MacOS 上实现类似的功能,但目前,这仅限于 Wayland。它还取决于内容是否在 dmabuf 中。

想要利用此功能的应用程序需要配合,避免执行干扰子表面使用的操作,例如对视频内容进行圆角处理。GtkGraphicsOffload 文档为开发者提供了关于约束以及如何调试图形卸载问题的更多详细信息。

总结

GTK 4.14 版本将为媒体播放带来一些有趣的新功能。您可以使用刚刚发布的 4.13.3 快照立即尝试。

请尝试一下,并告诉我们哪些功能对您有效,哪些无效。

GTK 中的路径,第二部分

在本系列的第一部分中,我们介绍了路径的概念,并了解了如何创建 GskPath。但路径不仅仅如此。

路径点

路径的许多有趣的属性会随着您沿着路径的轨迹移动而改变。要查询这些属性,我们首先需要一种方法来确定我们感兴趣的路径上的点。

GTK 为此提供了 GskPathPoint 结构体,并提供了许多函数来获取它们,例如 gsk_path_get_closest_point(),它可以让您找到路径上距离给定点最近的点。

一旦您有了 GskPathPoint,您就可以查询该点路径的属性。最基本的属性是位置,但您还可以获得切线、曲率或距路径起点的距离。

输入

在用户界面中使用路径时,另一个有趣的问题是

鼠标指针是否悬停在路径上?

如果您想突出显示指针悬停的路径,或者想响应用户点击路径,则需要此问题的答案。

对于填充的路径,GTK 使用 gsk_path_in_fill() 方法提供答案。

对于描边的路径,提供 100% 准确的答案要复杂得多(特别是当描边使用虚线模式时),但我们可以提供一个通常足够好的近似答案:如果到路径上最近点的距离小于线宽的一半,则该点在描边内。

展望

本系列的下一部分将介绍如何使用路径进行渲染。

GTK 中的路径

我们希望摆脱 cairo 作为 GTK 中的绘图 API,以便可以将更多绘图转移到 GPU 上,这已不是秘密。

虽然人们已经找到了使用渲染节点绘制事物的方法,但它们没有提供像 Skia 或 cairo 那样的全面的绘图 API。这种情况不是很令人满意。

几年前,我们开始研究如何通过使路径在 GTK 中可用作一等对象来改变这种情况。这项工作终于开始取得成果,您可以在 GTK 4.13.0 中看到第一个结果。

路径

那么,什么是路径?一个粗略的定义可以是

一系列线段或曲线,这些线段或曲线的端点可能连接也可能不连接。

当我们说曲线时,我们特指二次或三次 贝塞尔曲线。在 cairo 之上,我们还支持有理二次贝塞尔曲线(或者像 Skia 称呼的那样:圆锥曲线),因为它们可以让我们精确地建模圆形和圆角矩形。

这张图片显示了一个典型的路径,由 4 条曲线和 2 条直线组成,其中一些是连接的。如您所见,路径可以是闭合的(如这里的 4 条曲线)或开放的(如 2 条直线),具有起点和终点。

路径对于绘图有什么用?首先,您可以使用路径定义一个区域(路径内部的部分),并使用颜色、渐变或一些更复杂的内容填充它。

或者,您可以使用各种属性(例如线宽、颜色或虚线模式)描边路径。

GTK 中的路径

我们在 GTK 中用于路径的对象是 GskPath。它是一种紧凑、不可变的表示形式,针对渲染进行了优化。要创建 GskPath,您需要使用 GskPathBuilder,它具有许多用于从单个曲线或预定义形状创建路径的便捷方法。

此示例创建一个闭合的三角形路径

builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, 0, 50);
gsk_path_builder_line_to (builder, 100, 50);
gsk_path_builder_line_to (builder, 50, 0);
gsk_path_builder_close (builder);
path = gsk_path_builder_free_to_path (builder);

此示例创建一个具有给定中心和半径的圆形路径

builder = gsk_path_builder_new ();
gsk_path_builder_add_circle (builder, center, radius);
path = gsk_path_builder_free_to_path (builder);

展望

在下一篇文章中,我们将介绍路径的属性以及如何查询它们。

不断发展的辅助功能

我们上一篇关于 GTK4 中辅助功能的文章是一段时间前了,现在是更新的时候了。

值得庆幸的是,我们现在在 Red Hat 有 Lukáš Tyrychtr 在负责辅助功能。

辅助功能 - 它是如何工作的?

从 GTK3 到 GTK4,辅助功能方面较大的变化之一是,我们有一个新的应用程序 API,它以 Web 上的 ARIA 规范为模型。

需要记住的总体情况是

app → ARIA → AT-SPI → accessibility bus → AT

这里的 AT 代表辅助技术。在实践中,这主要意味着 orca,著名的屏幕阅读器(尽管有一个新的竞争者,即 odilia 项目)。

新的图层提供了诸如

void gtk_accessible_update_state (GtkAccessible *self,
                                  GtkAccessibleState first_state,
                                  ...)

这样的 API,允许应用程序设置其小部件的辅助属性。

ARIA 图层改进

自从我们在 GTK4 中引入它以来,应用程序 API 图层仅经历了适度的更改。这种情况在过去的 9 个月左右开始发生变化,因为 Lukáš 正在处理它。

他开始做的一件事是添加公共接口,以便第三方小部件(例如 libadwaita)可以提供完整的辅助功能支持。第一个这样的接口是 GtkAccessibleRange(在 4.10 中新增),用于像 GtkScale 这样的范围小部件。我们正在考虑添加更多接口,特别是 文本接口。这将使终端可访问。

在 4.12 周期中,我们做了一些工作,使我们的实现更紧密地匹配 ARIA 规范。这涉及到更改某些小部件的角色:我们现在的默认角色是“generic”,而顶级窗口使用“application”角色。我们还重新计算了可访问的名称和描述(即您听到 orca 读取的内容)的方式,以匹配 规范

另一个改进是我们的大多数小部件现在都具有必要的标签和关系,以使 orca 可以读取它们。如果您发现仍然缺少某些内容,请告诉我们!

AT-SPI 转换改进

我们希望看到 AT-SPI D-Bus API 的一些现代化,这已不是秘密。但就目前而言,我们必须使用它。我们的转换层通过仅将 AT 请求的对象延迟放置在总线上来工作,以避免创建过多的总线流量。

我们转换方面的最新改进之一是我们现在正在使用 GtkAccessibleRange,因此第三方范围小部件可以访问。

我们还修复了 GtkNotebookGtkStackSwitcher 的选择实现问题,现在辅助技术 (ATs) 可以在笔记本和堆栈中更改选定的选项卡。

工具

听到这些消息固然不错,但如果您是应用程序开发人员,您可能想知道如何在您的应用程序中查找和修复辅助功能问题。

只需打开屏幕阅读器,看看当您浏览应用程序时它会说什么,就非常有启发意义。但我们也有一些工具可以帮助您评估应用程序的辅助功能支持。

GTK 检查器有一个页面显示辅助功能信息

GTK 检查器中的辅助功能选项卡它最近得到了改进,不仅显示了在每个小部件上设置的属性、状态和关系,还显示了 GTK 计算并传递给 ATs 的名称和描述 - 这就是 orca 读取的文本。

检查器中的另一个工具是全新的:辅助功能覆盖层会显示基于 ARIA 创作指南的警告和建议。

它看起来像这样

一个窗口显示来自辅助功能覆盖层的警告,以及带有启用覆盖层的开关的检查器窗口。它并不完美,但它应该可以快速了解您可以在哪里改进辅助功能。

总结

GTK 4.12 将具有更好的开箱即用的辅助功能和新工具,以帮助您使您的应用程序更易于访问。

请享用!❤️