绘制原理

Catalogue   

相关类

  • Canvas:封装了Flutter Skia各种绘制指令,比如画线、画圆、画矩形等指令。
  • Layer:分为容器类和绘制类两种;暂时可以理解为是绘制产物的载体,比如调用 Canvas 的绘制 API 后,相应的绘制产物被保存在 PictureLayer.picture 对象中。
  • Scene:屏幕上将要要显示的元素。在上屏前,我们需要将Layer中保存的绘制产物关联到 Scene 上。
  • Picture:是一系列的图形绘制操作指令。

  • Container Layer:管理Layers,是唯一可以拥有child layer的Layer;
  • 非Container Layer:真正用于承载渲染结果的layer,在Layer Tree中属于叶子结点。比如PictureLayer承载的是图片的渲染结果,TextureLayer承载的是纹理的渲染结果;

绘制流程

大致流程

  1. 构建一个 Canvas,用于绘制;同时还需要创建一个绘制指令记录器,因为绘制指令最终是要传递给 Skia 的,而 Canvas 可能会连续发起多条绘制指令,指令记录器用于收集 Canvas 在一段时间内所有的绘制指令,因此Canvas 构造函数第一个参数必须传递一个 PictureRecorder 实例。
  2. Canvas 绘制完成后,通过 PictureRecorder 获取绘制产物,然后将其保存在 Layer 中。
  3. renderView.compositeFrame构建 Scene 对象,将 layer 的绘制产物和 Scene 关联起来。
  4. 上屏;调用window.render API 将Scene上的绘制产物发送给GPU。

精细流程

第一次绘制时,从上到下递归绘制子节点,每当遇到一个边界节点,判断如果该节点的layer属性是否为空,是就创建一个新的OffsetLayer并赋值给它;不是则使用。然后将layer传递给子节点,接下来:

  1. 如果子节点是非边界节点,且需要绘制,则:
    • 第一次绘制:创建一个Canvas对象和一个PictureLayer,然后将它们绑定,后续调用Canvas绘制都会落到和其绑定的PictureLayer上,接着这个PictureLayer会加入到边界节点的layer中;
    • 不是第一次绘制:复用已有的边界节点和Canvas对象;
  2. 如果子节点是边界节点,则对子节点递归上述过程。当子树递归完成后,就要将子节点的layer添加到父级layer中。

RenderObject调用markNeedsRepaint来发起重绘:

  1. 从当前节点一直往父级查找,直到找到一个绘制边界点时终止查找,然后会将该绘制边界点添加到其PiplineOwner的_nodesNeedingPaint列表中。
  2. 在查找的过程中,会将自己到绘制边界点路径上所有节点的_needPaint属性设置为true,表示需要重绘。
  3. 请求新的frame,执行重绘流程。下一个frame就会走drawFrame流程,涉及到flushCompositingBits、flushPaint 和 compositeFrame 三个函数。

参考