理解Chrome Ui Views的两层中间件:Views库和Skia

Views 库在 Chromium/Google Chrome 中的定位与作用

什么都赶不上自己写的文档权威:

Views is Chromium's UI framework. This is a quick primer to help you get started hacking together UIs with Views. Views are lightweight and cross-platform, but they can be difficult to learn and debug because documentation is sparse.

UIs in Chromium are generally implemented using either Views/C++ or HTML/JS/Polymer...

说起来就是几个要点

  • Views就是Chromium 浏览器及 Chrome OS 原生 UI使用的封装了跨平台的UI工具包。注意到的是在之前的博客我就整理了UI Views和浏览器渲染的区别了。Views走的是 “原生” UI 元件的抽象

    Chrome UI Views渲染

  • 在 Chromium 的架构中,Views 位于浏览器 UI 这一层(窗口、控件、菜单、地址栏等)——具体来说,浏览器内的大部分 “浏览器前端界面” 使用 Views 实现(尤其在桌面平台)而不是 HTML/JS。

Views管了什么?

  • 层次结构管理:每个 UI 元件(控件)是一个 View 对象,彼此组成父子树。类似于 HTML DOM 的结构。
  • 布局:父 View 或其 LayoutManager 负责对子 View 的位置/大小进行计算。 View 提供接口(preferred size、minimum/maximum size)供布局系统使用。
  • 绘制:View 提供绘制接口(如 OnPaint、OnPaintBackground、OnPaintBorder 等),最终使用底层绘图库(如 Skia)进行渲染。(Btw,默认的Views的OnPaint,也就是默认的行为总是绘制边框和背景)
  • 事件处理:View 树会接收鼠标/键盘/触控/焦点等事件,并向子 View 分发。 Views 把原生窗口系统的消息转换成 View 树可处理的事件。
  • 平台抽象/统一:Views 封装了不同操作系统/窗口系统(如 Windows、GTK、Cocoa 等)上的差异,提供统一 API,用于构建多平台 UI。

2. views::View 类及其作用

views::View 是什么?

在 Views 中,View 是 UI 元件树中的一个节点。每个 View 代表一个可见(或可交互)元素,类似于 HTML 的 DOM 元素。嗯?有没有感觉有点像QWidget了?Ui::Views这里分的还更细——这一点必须承认的是。Google这里真的把逻辑功能的Widget,绘制的物理实体View和实际实现绘制的Deletgate全部分开了。我说View是绘制的物理实体在于,所有的BoundingRect被放在了View中,定义了其在父 View 坐标系中的位置及大小。 View 可以有子 View,是View而不是Widget通过树结构组织。子 View 在父 View 的 content bounds 内被布局/绘制。 详细的说——

  • 绘制:View 可以绘制自己的背景、边框、内容(子 View、图标、文字等)。绘制通常是在 OnPaint()OnPaintBackground()OnPaintBorder() 等方法中。父 View 提供 Canvas 给子 View 绘制。
  • 布局:View 可选用 LayoutManager 或重写 View::Layout() 来自定义子 View 排布。 View 提供大小建议(preferred/min/max),供父 View 或 layout 管理器参考。
  • 事件响应:View 接收并处理事件(鼠标点击、键盘、触控、焦点等)。当用户交互时,事件通过 View 树分发。 View 可注册为可获取焦点,响应键盘输入等。
  • 层次结构管理:View 提供方法管理父子关系、坐标变换、可见性、对齐、裁剪等。
  • 尺寸管理:View 存在最小/最大/首选大小,供自动布局使用。改变子 View 尺寸/位置应由布局器控制,而不是手动修改 bounds。

3. Delegate 在 Views 中的作用

在 Views 架构中,Delegate 模式被用得比较多。虽然官方文档中没有一个统一「ViewDelegate」章节,但从源码和文档可以看到一些常见用法。其主要目的就是作为一个代理,增强Views 和Widget的交互。

Delegate 的含义与目的

  • Delegate 通常指一个“回调接口”或“委托对象”,其职责是为 View 或 Widget 提供可变行为或事件响应逻辑。
  • 在 Views 中,View 或 Widget 会“委托”某些决策给 Delegate,例如布局决定、按钮点击响应、控件状态切换、窗口尺寸变化等。
  • 这种模式能将“视图本身的绘制/布局逻辑”与“具体业务行为/容器特有逻辑”分离。对于你做 UI 子系统或操作系统桌面系统,这是一种分离视图逻辑与行为逻辑的常用架构。

为什么有 Delegate

从系统/架构视角,使用 Delegate 有以下优点:

  • 定制行为:在 View 库通用部分(绘图、控件、布局、事件分发)之外,商业逻辑或产品特定的行为可通过 Delegate 注入,而无需修改 View 库本身。
  • 分离职责:View 专注于显示、控件树、布局;而业务/容器逻辑(如浏览器窗口:标签栏/工具栏切换)通过 Delegate 实现。
  • 可替换与测试:可以为不同场景传入不同的 delegate 实现,方便复用和单元测试。
  • 简化 View 库:View 库保持较小 API 面,专注于 UI 框架,业务逻辑由外部 delegate 提供。

简单的理解什么是Google Skia

Google Document是这样说它的Skia的。

Skia is an open source 2D graphics library which provides common APIs that work across a variety of hardware and software platforms. It serves as the graphics engine for Google Chrome and ChromeOS, Android, Flutter, Mozilla Firefox and Firefox OS, and many other products.

翻译一下,Skia 是一个开源的 2D 图形库,用 C++ 编写。它提供了一套在各类平台(Windows、macOS、Linux、Android、iOS 等)上统一的“画图”接口。这就有点像Qt中的QPainter集合了。当咱们需要在屏幕上画文字、几何图形、图片、SVG、PDF 等,Skia 提供了“画布”(Canvas)、“笔刷”(Paint)、“路径”(Path)这些抽象,背后隐藏了不同平台的差异。

Skia在浏览器中发挥的作用

我们重复和加深一下Google Chrominum大致的渲染流程。对于Ui Views。我们递归的针对Root控件调用OnPaint,这个OnPaint其实就是在想后端的Skia告知我们具体的绘制指令。后续的流程就是Rasterization光栅化,实际上就是把怎么画(嘿,这就跟矢量图一样啊)变成像素或者。在之后如果是Layer图层的话就进行合并和提交给GPU。

​ Skia 在这个流程中主要承担的是第 3 步(光栅化)以及部分第 2 步(提供绘制 API)——也就是「把画命令变成最终可显示的像素/图形」的关键环节。例如,在 Chromium/Google Chrome 浏览器中,Blink(网页渲染引擎)会将绘制命令(比如“画这个矩形”“画这段文字”)记录下来,然后交给 Skia 去 rasterize(栅格化)和 GPU 绘制。

重新理解光栅化(Rasterization)

“光栅化”这个词其实很好理解:它就是将矢量/几何/路径/文字这些“抽象”的画命令,转换成“像素点”或者传递给 GPU 的绘制命令,从而最终显示在屏幕上的过程。

  • 如果是 CPU 软件渲染:Skia 会走一个栅格管线(raster pipeline),每一个像素可能由色彩、抗锯齿、渐变、遮罩、混合等计算得出。
  • 如果是 GPU 渲染:Skia 会把绘制命令转成 GPU 可理解的操作(例如纹理上传、着色器调用、图层合成等)