UIGU源码分析17:Image

源码17:Image

Image可以说是用的最多得UI组件了,相比于RawImage来说也复杂得多

public class Image : MaskableGraphic, ISerializationCallbackReceiver, ILayoutElement, ICanvasRaycastFilter
{
		...
		
		[SerializeField]
        private Sprite m_Sprite;
        
            /// How the Image is drawn.
        [SerializeField] private Type m_Type = Type.Simple;
        
        ...
}

Image使用得图片资源是Sprite格式,这个需要在图片导入得时候在Inspector里吧TextureType 设置为Sprite


Image重写了Graphic里得OnPopulateMesh

    protected override void OnPopulateMesh(VertexHelper toFill)
    {
        if (activeSprite == null)
        {
            base.OnPopulateMesh(toFill);
            return;
        }

        switch (type)
        {
            case Type.Simple:
                if (!useSpriteMesh)
                    GenerateSimpleSprite(toFill, m_PreserveAspect);
                else
                    GenerateSprite(toFill, m_PreserveAspect);
                break;
            case Type.Sliced:
                GenerateSlicedSprite(toFill);
                break;
            case Type.Tiled:
                GenerateTiledSprite(toFill);
                break;
            case Type.Filled:
                GenerateFilledSprite(toFill, m_PreserveAspect);
                break;
        }
    }

再渲染Image得时候,分了四种渲染方法。最终VertexHelper中传入顶点数据 会根据这四种渲染方式来做不同计算

  public enum Type
    {
		//渲染普通图片资源
        Simple,
		//渲染九宫格式图片
        Sliced,
		//平铺模式
        Tiled,
		//填充模式
        Filled
    }

下面就分析下不同得实现:

  • Simple

useSpriteMesh 基本都是False 没看到有设置为True得 所以只考虑GenerateSimpleSprite

    void GenerateSimpleSprite(VertexHelper vh, bool lPreserveAspect)
    {
        Vector4 v = GetDrawingDimensions(lPreserveAspect);
        var uv = (activeSprite != null) ? Sprites.DataUtility.GetOuterUV(activeSprite) : Vector4.zero;

        var color32 = color;
        vh.Clear();
        vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(uv.x, uv.y));
        vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(uv.x, uv.w));
        vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(uv.z, uv.w));
        vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(uv.z, uv.y));

        vh.AddTriangle(0, 1, 2);
        vh.AddTriangle(2, 3, 0);
    }

GetDrawingDimensions 获取图片得左下角 右上角顶点得位置,得到数据后AddVert、AddTriangle。处理方式基本和RawImage的方式一样。

  • Sliced
        private void GenerateSlicedSprite(VertexHelper toFill)
        {
            if (!hasBorder)
            {
                GenerateSimpleSprite(toFill, false);
                return;
            }

            Vector4 outer, inner, padding, border;

            if (activeSprite != null)
            {
                outer = Sprites.DataUtility.GetOuterUV(activeSprite);
                inner = Sprites.DataUtility.GetInnerUV(activeSprite);
                padding = Sprites.DataUtility.GetPadding(activeSprite);
                border = activeSprite.border;
            }
            else
            {
                outer = Vector4.zero;
                inner = Vector4.zero;
                padding = Vector4.zero;
                border = Vector4.zero;
            }

            Rect rect = GetPixelAdjustedRect();

            Vector4 adjustedBorders = GetAdjustedBorders(border / multipliedPixelsPerUnit, rect);
            padding = padding / multipliedPixelsPerUnit;

            s_VertScratch[0] = new Vector2(padding.x, padding.y);
            s_VertScratch[3] = new Vector2(rect.width - padding.z, rect.height - padding.w);

            s_VertScratch[1].x = adjustedBorders.x;
            s_VertScratch[1].y = adjustedBorders.y;

            s_VertScratch[2].x = rect.width - adjustedBorders.z;
            s_VertScratch[2].y = rect.height - adjustedBorders.w;

            for (int i = 0; i < 4; ++i)
            {
                s_VertScratch[i].x += rect.x;
                s_VertScratch[i].y += rect.y;
            }

            s_UVScratch[0] = new Vector2(outer.x, outer.y);
            s_UVScratch[1] = new Vector2(inner.x, inner.y);
            s_UVScratch[2] = new Vector2(inner.z, inner.w);
            s_UVScratch[3] = new Vector2(outer.z, outer.w);

            toFill.Clear();

            for (int x = 0; x < 3; ++x)
            {
                int x2 = x + 1;

                for (int y = 0; y < 3; ++y)
                {
                    if (!m_FillCenter && x == 1 && y == 1)
                        continue;

                    int y2 = y + 1;


                    AddQuad(toFill,
                        new Vector2(s_VertScratch[x].x, s_VertScratch[y].y),
                        new Vector2(s_VertScratch[x2].x, s_VertScratch[y2].y),
                        color,
                        new Vector2(s_UVScratch[x].x, s_UVScratch[y].y),
                        new Vector2(s_UVScratch[x2].x, s_UVScratch[y2].y));
                }
            }
        }

hasBorder : 当没有边框 也就是没有设置成九宫格得时候 就直接和Simple模式下一样

padding 我们通常会设置为0 这里就不考虑他,先计算得outer inner border 基本如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b4c1mYLw-1648651845881)(D:\UnityProjectSpace\BlogRecord\UGUI源码分析\Image\4.png)]

最终两个For其实是生成三角形

  • 生成36个uv点,九宫格里每一个格子对应4个uv点。
  • 生成36个顶点,九宫格里每一个格子对应4个顶点。
  • 中心区域被拉大,四角部分不变。当取消Image上的Fill Center的时候,中间区域变透明,这是因为中间区域顶点、UV等信息都没有。

  • Tiled

    源码有点长就不贴了。

    如果sprite有边界,那么便会生成跟GenerateSlicedSprite一样的结果,如果没有边界,那么就会在该区域内填充多个sprite,GenerateTiledSprite会计算该区域里可以放下多少个精灵单元(横纵分别向上取整),假设为格子数N,便会有4N个顶点,如果一个小格子可以完整的放下一个精灵单元,uv值便是x从0到1,y从0到1的完整纹理坐标。而如果只能放下一部分,那边根据百分比计算uv值。


  • Filled

    区分了不同的填充方法(Horizontal,Vertical,Radial 90,Radial 180,Radial 360)

    Horizontal和Vertical填充方法

    根据m_FillOrigin(填充的起点)和m_FillAmount(填充的值),设置顶点和UV。

    Radial系列方法

    调用RadialCut方法,调整指定的四边形,使其径向填充。然后调用AddQuad方法,设置顶点,颜色和UV。

猜你喜欢

转载自blog.csdn.net/NippyLi/article/details/123859689