6、Paint 繪製(1)

1、Flutter 之圖像繪製原理node

2、Widget、Element、RenderObjectweb

3、Flutter UI 更新流程canvas

4、build 流程分析bash

5、layout 流程分析框架

7、Paint 繪製(2)ide

8、composite 流程分析函數

9、Flutter 小實踐post

一、分層繪製

相似web 端 canvas, 咱們常常是採用分層的策略 例如如下游戲場景, 在打鬥的過程當中, 人物動做, 位移,動畫等變化的頻率和幅度是很大的,而背景變化的頻率或幅度則相對較小(基本不變,或者緩慢變化,或者僅在某些時機變化),這個過程須要很頻繁地更新和重繪人物,可是對於背景,咱們也許只須要繪製一次,也許只須要隔一段時間才重繪一次動畫

若是隻在一個畫布裏面繪製,那人物的的頻繁變化也會引發背景的繪製,所以須要須要生成多個畫布獨立繪製,最後再合成ui

在flutter 中, 也是採用分層繪製的概念,例以下圖,是Flutter框架渲染機制的一個示意圖~ 在框架渲染完成以後會輸出 的 一個個的 layer 造成的 layer tree,layer tree被送入engine,engine會把layer tree調度到GPU線程,在GPU線程內合成(compsite)layer tree,而後由Skia 2D渲染引擎渲染後送入GPU顯示~

二、Layer 類

(1) Layer

abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
 
  @override
  ContainerLayer get parent => super.parent; 
  Layer get previousSibling => _previousSibling;
  Layer _previousSibling;
}
複製代碼

類Layer是一個樹形結構, 屬性parent表明其父節點, nextSibling和previousSibling表示同一圖層的前一個和後一個兄弟節點,即圖層孩子節點們是用雙向鏈表存儲的

(2)AbstractNode

class AbstractNode {
  int get depth => _depth;
  int _depth = 0;
  void redepthChildren() { }
  Object get owner => _owner;
  Object _owner;

  /// The parent of this node in the tree.
  AbstractNode get parent => _parent;
  AbstractNode _parent;
  void adoptChild(covariant AbstractNode child) {}
  void dropChild(covariant AbstractNode child) {}
}
複製代碼

Layer 繼承 AbstractNode,所以ayer 也是一個個普通的節點,這個節點能夠是葉子節點,也能夠擁有子節點

三、Layer 分類

Layer 有不一樣的實現類,只有ContainerLayer類型及其子類的圖層能夠擁有孩子,其餘類型的Layer子類都是葉子圖層。

四、繪製流程 layer 管理

分層繪製的核心思想是實現多個 canvas, 那在 flutter 裏面,何時會建立新的 canvas? needsCompositing isRepaintBoundary

咱們先看 needsCompositing 在監聽 Vsync 信號調用 drawFrame, (1) drawFrame

@protected
void drawFrame() {
  pipelineOwner.flushLayout();
  pipelineOwner.flushCompositingBits();
  pipelineOwner.flushPaint();
  renderView.compositeFrame(); // this sends the bits to the GPU
  pipelineOwner.flushSemantics(); // this also sends the semantics to
}
複製代碼

在調用 flushPaint 進行繪製以前,會先調用 flushCompositingBits

(2) flushCompositingBits

void flushCompositingBits() {
  _nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
  for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
    if (node._needsCompositingBitsUpdate && node.owner == this)
      node._updateCompositingBits();
  }
  _nodesNeedingCompositingBitsUpdate.clear();
}
複製代碼

這個函數中,主要是遍歷 _nodesNeedingCompositingBitsUpdate 節點集合,調用 _updateCompositingBits

(3) _updateCompositingBits

void _updateCompositingBits() {
  if (!_needsCompositingBitsUpdate)
    return;
  final bool oldNeedsCompositing = _needsCompositing;
  _needsCompositing = false;
  visitChildren((RenderObject child) {
    child._updateCompositingBits();
    if (child.needsCompositing)
      _needsCompositing = true;
  });
  if (isRepaintBoundary || alwaysNeedsCompositing)
    _needsCompositing = true;
  if (oldNeedsCompositing != _needsCompositing) {
    // 若是 oldNeedsCompositing != _needsCompositing, 則說明節點所在的圖層發生了改變,則須要從新繪製
    markNeedsPaint();
  }
  _needsCompositingBitsUpdate = false;
}
複製代碼

在這個函數中,遍歷子節點,若是子節點 的 needsCompositing 爲true, 則須要將該節點的 needsCompositing 設置爲 true

上述代碼 (2) 中,_nodesNeedingCompositingBitsUpdate 節點集合怎麼來的? 追蹤代碼、則發如今 markNeedsCompositingBitsUpdate 函數中有相關處理

(4) markNeedsCompositingBitsUpdate

void markNeedsCompositingBitsUpdate() {
  if (_needsCompositingBitsUpdate)
    return;
  _needsCompositingBitsUpdate = true;
  if (parent is RenderObject) {
    final RenderObject parent = this.parent;
    if (parent._needsCompositingBitsUpdate)
      return;
    if (!isRepaintBoundary && !parent.isRepaintBoundary) {
      parent.markNeedsCompositingBitsUpdate();
      return;
    }
  }
  if (owner != null)
    owner._nodesNeedingCompositingBitsUpdate.add(this);
}
複製代碼

在該代碼段中會將該 renderObject 加入 _nodesNeedingCompositingBitsUpdate 集合中,同時向上遍歷父節點的 markNeedsCompositingBitsUpdate 方法,

(5)markNeedsCompositingBitsUpdate 什麼時候調用? renderObject 通常在 添加,刪除孩子時調用 markNeedsCompositingBitsUpdate

//添加孩子
@override
void adoptChild(RenderObject child) {
  markNeedsCompositingBitsUpdate();
}

//刪除孩子
@override
void dropChild(RenderObject child) {
  super.dropChild(child);
  markNeedsCompositingBitsUpdate();
}
複製代碼

needsCompositing有哪些應用場景

(6) needsCompositing

在繪製時,若是 needsCompositing 這個屬性值 爲 true, 則意味着須要新增一個 layer

ClipRectLayer pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter, { Clip clipBehavior = Clip.hardEdge, ClipRectLayer oldLayer }) {
  final Rect offsetClipRect = clipRect.shift(offset);
  if (needsCompositing) {
    final ClipRectLayer layer = oldLayer ?? ClipRectLayer();
    layer
      ..clipRect = offsetClipRect
      ..clipBehavior = clipBehavior;
    pushLayer(layer, painter, offset, childPaintBounds: offsetClipRect);
    return layer;
  } else {
    clipRectAndPaint(offsetClipRect, clipBehavior, offsetClipRect, () => painter(this, offset));
    return null;
  }
}
複製代碼
相關文章
相關標籤/搜索