如图所示,在OpenGLRenderer类创建的时候,将会创建一个空的Snapshot,也就是FirstSnapshot,也可以称之为根节点,接着每次创建新的节点,都将会保存上一次的节点指针,新节点都用指针mSnapshot,来指向当年的节点,其实这就是用栈的形式来存储hwui的绘制状态,通过设置好Snapshot后,后续的绘图操作都会在mSnapshot(当前快照)上进行操作,当当前的绘制操作完成后,可以返回到上一次的Snapshot中,下一次的绘制操作不会影响上一次的绘制操作,但是上一次的绘制操作,可以通过设置来确定是否可以影响下一次Snapshot的渲染状态。
当OpenGLRenderer构造函数调用的时候将会创建FirstSnapshot,也就是根节点指针,接着当用户调用save时,将创建一个Snapshot,参数为flags,也就是用户在编写是用canvas调用save时输入的参数,默认为全部保存:
int ALL_SAVE_FLAG restore everything when restore() is called int CLIP_SAVE_FLAG restore the current clip when restore() is called int CLIP_TO_LAYER_SAVE_FLAG clip against the layer's bounds int FULL_COLOR_LAYER_SAVE_FLAG the layer needs to 8-bits per color component int HAS_ALPHA_LAYER_SAVE_FLAG the layer needs to per-pixel alpha int MATRIX_SAVE_FLAG restore the current matrix when restore() is called
当Snapshot创建时,将主要用到CLIP_SAVE_FLAG与MATRIX_SAVE_FLAG,也就是用于决定当前的矩阵信息当调用restore的时候是否要作用到上一个节点中:
Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags): flags(0), previous(s), layer(s->layer), fbo(s->fbo), invisible(s->invisible), empty(false), viewport(s->viewport), height(s->height), alpha(s->alpha) { if (saveFlags & SkCanvas::kMatrix_SaveFlag) { mTransformRoot.load(*s->transform); transform = &mTransformRoot; } else { transform = s->transform; } if (saveFlags & SkCanvas::kClip_SaveFlag) { mClipRectRoot.set(*s->clipRect); clipRect = &mClipRectRoot; if (!s->clipRegion->isEmpty()) { mClipRegionRoot.op(*s->clipRegion, SkRegion::kUnion_Op); } clipRegion = &mClipRegionRoot; } else { clipRect = s->clipRect; clipRegion = s->clipRegion; } if (s->flags & Snapshot::kFlagFboTarget) { flags |= Snapshot::kFlagFboTarget; region = s->region; } else { region = NULL; } }
如上代码所示 ,首先将上一次的Snapshot保存到previous中,接着去判断输入的saveFlags,如果是MATRIX_SAVE_FLAG,则将调用load将上一节点的矩阵信息拷贝一份,否则将上一次的矩阵信息的指针保存到当前的变换矩阵transform中,这样后续对transform的操作都将会保存到上一次的Snapshot中,当调用restore的时候,将会将当前的变换矩阵作用到上一次的矩阵中。CLIP_SAVE_FLAG同理。
同时,Snapshot也可以用于保存fbo的layer信息,如当用户调用saveLayer的时候,将会创建一个Layer,Layer也就是一块绘制区域,这块绘制区域单独渲染,即使用fbo的形式。
接着介绍两个Snapshot两个比较重要的变量:
Rect* clipRect; SkRegion* clipRegion;
这两个变量很容易混淆,clipRect就是裁剪矩形,它只有四个值左上右下,它的作用就是保存当前的Snapshot的最大裁剪矩形,它一定是矩形的,而第二个变量clipRegion即裁剪区域,这是个区域,它可以由各种复杂形状组成,clipRect则是由clipRegion计算出最大的bound的矩形,来作为裁剪矩形,用于设置opengl的裁剪的,而clipRegion则是不规则的裁剪区域,用于限定一个绘制指令只有在该区域内才能生效的,这个将在最后绘制时,首先会将它用来生成许多一个像素高度的小矩形来绘制,用来作为模板测试使用,也就是只有通过模板测试的才能绘制,否则就丢弃。