源码13:MaskGraphic
public abstract class MaskableGraphic : Graphic, IClippable, IMaskable, IMaterialModifier
{
...
}
继承MaskGraphic 的有Image Text RawImage 基本可以使用显示的UI组件都继承了MaskGraphic
MaskableGraphic除了继承Graphic外 还继承了三个接口
IClippable
IClippable 被裁剪者 通常和裁剪者IClipper配套使用 看IClippable接口的源码注释
public interface IClippable
{
/// <summary>
///可被裁剪的对象
/// GameObject of the IClippable object
/// </summary>
GameObject gameObject { get; }
/// <summary>
///可被裁剪的父对象状态发生变化的时候使用
/// Will be called when the state of a parent IClippable changed.
/// </summary>
void RecalculateClipping();
/// <summary>
/// 可被裁剪的对象RectTransform
/// The RectTransform of the clippable.
/// </summary>
RectTransform rectTransform { get; }
/// <summary>
/// 在给给定的裁剪范围中进行裁剪计算
/// Clip and cull the IClippable given a specific clipping rect
/// </summary>
/// <param name="clipRect">The Rectangle in which to clip against.</param>
/// <param name="validRect">Is the Rect valid. If not then the rect has 0 size.</param>
void Cull(Rect clipRect, bool validRect);
/// <summary>
/// 设置可被裁剪的对象的裁剪范围
/// Set the clip rect for the IClippable.
/// </summary>
/// <param name="value">The Rectangle for the clipping</param>
/// <param name="validRect">Is the rect valid.</param>
void SetClipRect(Rect value, bool validRect);
/// <summary>
/// Set the clip softness for the IClippable.
///
/// The softness is a linear alpha falloff over clipSoftness pixels.
/// </summary>
/// <param name="clipSoftness">The number of pixels to apply the softness to </param>
void SetClipSoftness(Vector2 clipSoftness);
IClipper 目前只有RectMask2D组件继承实现
也就是说通常裁剪的时候 是裁减者RectMask2D设置一个裁减矩形给被裁减者MaskableGraphic
这里先分析下MaskGraphic 是如何设置应用裁剪相关内容 如何裁剪在RectMask2D在分析
SetClipRect MaskableGraphic将裁减矩形发送到canvasRenderer渲染器中
public virtual void SetClipRect(Rect clipRect, bool validRect)
{
if (validRect)
canvasRenderer.EnableRectClipping(clipRect);
else
canvasRenderer.DisableRectClipping();
}
RecalculateClipping 重新计算获取父对象中的RectMask2D组件 (m_ParentMask)
/// <summary>
/// See IClippable.RecalculateClipping
/// </summary>
public virtual void RecalculateClipping()
{
UpdateClipParent();
}
private void UpdateClipParent()
{
var newParent = (maskable && IsActive()) ? MaskUtilities.GetRectMaskForClippable(this) : null;
// if the new parent is different OR is now inactive
if (m_ParentMask != null && (newParent != m_ParentMask || !newParent.IsActive()))
{
m_ParentMask.RemoveClippable(this);
UpdateCull(false);
}
// don't re-add it if the newparent is inactive
if (newParent != null && newParent.IsActive())
newParent.AddClippable(this);
m_ParentMask = newParent;
}
Cull 剔除方法 如果validRect为false,或者输入的clipRect与所属Canvas的矩形区域不重合,调用UpdateCull方法,设置cull为true,把cull赋值给canvasRenderer.cull。如果canvasRenderer.cull发生变化时,发送事件m_OnCullStateChanged,m_OnCullStateChanged.Invoke(cull),并调用SetVerticesDirty,设置顶点的Dirty,等待重绘。
public virtual void Cull(Rect clipRect, bool validRect)
{
var cull = !validRect || !clipRect.Overlaps(rootCanvasRect, true);
UpdateCull(cull);
}
private void UpdateCull(bool cull)
{
if (canvasRenderer.cull != cull)
{
canvasRenderer.cull = cull;
UISystemProfilerApi.AddMarker("MaskableGraphic.cullingChanged", this);
m_OnCullStateChanged.Invoke(cull);
OnCullingChanged();
}
}
IMaskable
IMaskable 被遮罩者 同IClippable类似也有个遮罩者 Mask组件(必须有一个MaskGraphic组件)
这里只分析作为IMaskable Mask后面再分析
IMaskable 只有一个实现方法RecalculateMasking
public virtual void RecalculateMasking()
{
// Remove the material reference as either the graphic of the mask has been enable/ disabled.
// This will cause the material to be repopulated from the original if need be. (case 994413)
StencilMaterial.Remove(m_MaskMaterial);
m_MaskMaterial = null;
m_ShouldRecalculateStencil = true;
SetMaterialDirty();
}
设置m_ShouldRecalculateStencil为true,调用SetMaterialDirty 也就是启用Mask的时候,会让所有IMaskable子物体的StencilValue发生变化,并标记重建。
IMaterialModifier
MaskGraphic 也实现了IMaterialModifier接口,主要就是配合实现遮罩效果,因为遮罩效果正是通过材质实现的
public virtual Material GetModifiedMaterial(Material baseMaterial)
{
var toUse = baseMaterial;
if (m_ShouldRecalculateStencil)
{
if (maskable)
{
var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);
m_StencilValue = MaskUtilities.GetStencilDepth(transform, rootCanvas);
}
else
m_StencilValue = 0;
m_ShouldRecalculateStencil = false;
}
// if we have a enabled Mask component then it will
// generate the mask material. This is an optimization
// it adds some coupling between components though :(
if (m_StencilValue > 0 && !isMaskingGraphic)
{
var maskMat = StencilMaterial.Add(toUse, (1 << m_StencilValue) - 1, StencilOp.Keep, CompareFunction.Equal, ColorWriteMask.All, (1 << m_StencilValue) - 1, 0);
StencilMaterial.Remove(m_MaskMaterial);
m_MaskMaterial = maskMat;
toUse = m_MaskMaterial;
}
return toUse;
}
GetModifiedMaterial方法是再被标记重建后 在Rebulid中调用
如果需要重新计算模板,便从父RectTransform中获取overrideSorting为true的Canvas,赋值给rootCanvas,然后通过MaskUtilities.GetStencilDepth从rootCanvas获取模板深度。
如果模板深度大于0,且没有Mask组件或Mask组件没有激活,就把baseMaterial,stencilID,operation等参数添加到StencilMaterial中,并把之前旧的m_MaskMaterial从StencilMaterial中移除,用新的baseMaterial替换m_MaskMaterial,并返回
其他补充
protected override void OnEnable()
{
base.OnEnable();
m_ShouldRecalculateStencil = true;
UpdateClipParent();
SetMaterialDirty();
if (isMaskingGraphic)
{
MaskUtilities.NotifyStencilStateChanged(this);
}
}
组件刚激活时 设置重新计算模板标记为True,更新被裁剪得父对象 设置Material为Dirty,SetMaterialDirty。从StencilMaterial移除了m_MaskMaterial,并设置m_MaskMaterial为空,如果Makk组件不为空,调用MaskUtilities.NotifyStencilStateChanged重新计算Mask。
protected override void OnDisable()
{
base.OnDisable();
m_ShouldRecalculateStencil = true;
SetMaterialDirty();
UpdateClipParent();
StencilMaterial.Remove(m_MaskMaterial);
m_MaskMaterial = null;
if (isMaskingGraphic)
{
MaskUtilities.NotifyStencilStateChanged(this);
}
}
设置重新计算模板m_ShouldRecalculateStencil为true,更新裁剪的父对象UpdateClipParent,设置Material为Dirty,SetMaterialDirty。从StencilMaterial移除了m_MaskMaterial,并设置m_MaskMaterial为空,如果Mesh组件不为空,调用MaskUtilities.NotifyStencilStateChanged重新计算Mask。
StencilMaterial 类
/// <summary>
/// Dynamic material class makes it possible to create custom materials on the fly on a per-Graphic basis,
/// and still have them get cleaned up correctly.
/// </summary>
public static class StencilMaterial
{
private static List<MatEntry> m_List = new List<MatEntry>();
...
}
StencilMaterial是一个静态类,负责管理模板材质。维护了一个MatEntry类型的列表:
private static List m_List = new List();
外部可以调用Add、Remove和ClearAll方法来对这个List进行操作。
Add方法,会创建一个MatEntry,并将输入的baseMat以及其他参数赋值给MatEntry,并创建了赋值baseMat的customMat,并将stencilID,operation等参数赋值给customMat,实际上赋值customMat的shader参数。