using System; using UnityEngine; using UnityEngine.Sprites; using UnityEngine.UI; namespace Cryville.Common.Unity.UI { /// /// An image that is sliced into three parts, with the middle part stretched and the two borders preserved their aspect ratio. /// [ExecuteInEditMode] public class ImageSliced3 : MaskableGraphic { [SerializeField] [Tooltip("The sliced sprite.")] private Sprite m_sprite; /// /// The sliced sprite. /// public Sprite Sprite { get { return m_sprite; } set { m_sprite = value; } } /// /// The mode how a sliced image is generated when it is too compact. /// public enum CompactMode { /// /// Squeezes both the left border and the right border. /// SqueezeBoth = 0, /// /// Squeezes the left border and preserves the width of the right border. /// SqueezeLeft = 2, /// /// Squeezes the right border and preserves the width of the left border. /// SqueezeRight = 3, /// /// Squeezes the lower edge of the left border and the upper edge of the right border. /// DiagonalLeft = 4, /// /// Squeezes the upper edge of the left border and the lower edge of the right border. /// DiagonalRight = 5, } [SerializeField] [Tooltip("The mode how a sliced image is generated when it is too compact.")] private CompactMode m_compact; /// /// The mode how a sliced image is generated when it is too compact. /// public CompactMode Compact { get { return m_compact; } set { m_compact = value; } } [SerializeField] [Tooltip("Whether the sprite is vertical.")] private bool m_isVertical; /// /// Whether the sprite is vertical. /// public bool IsVertical { get { return m_isVertical; } set { m_isVertical = value; } } public override Texture mainTexture { get { return Sprite == null ? s_WhiteTexture : Sprite.texture; } } void GetCornerLength(int axis, int corner, Vector4 uv, out float length, out float uvLength) { float border = Sprite.border[(corner << 1) | axis]; float sizeRatio = border / (axis == 0 ? Sprite.rect.height : Sprite.rect.width); length = sizeRatio * (axis == 0 ? rectTransform.rect.height : rectTransform.rect.width); uvLength = (uv[(axis ^ 1) | 2] - uv[axis ^ 1]) * (border / (axis == 0 ? Sprite.rect.width : Sprite.rect.height)); } protected override void OnPopulateMesh(VertexHelper vh) { if (Sprite == null) { throw new UnityException("No sprite"); } else if (Sprite.border == Vector4.zero) { throw new UnityException("No sprite border"); } int axis = IsVertical ? 1 : 0; float actualLength = axis == 0 ? rectTransform.rect.width : rectTransform.rect.height; Vector4 uv = DataUtility.GetOuterUV(Sprite); GetCornerLength(axis, 0, uv, out var actualStartCornerLength, out var actualStartUVWidth); GetCornerLength(axis, 1, uv, out var actualEndCornerLength, out var actualEndUVLength); float actualTotalCornerLength = actualStartCornerLength + actualEndCornerLength; float w3, w4, w5, w6; if (actualTotalCornerLength > actualLength) { switch (Compact) { case CompactMode.SqueezeBoth: w3 = w4 = actualStartCornerLength / actualTotalCornerLength * actualLength; w5 = w6 = actualEndCornerLength / actualTotalCornerLength * actualLength; break; case CompactMode.SqueezeLeft: w3 = w4 = actualLength - actualEndCornerLength; w5 = w6 = actualEndCornerLength; break; case CompactMode.SqueezeRight: w3 = w4 = actualStartCornerLength; w5 = w6 = actualLength - actualStartCornerLength; break; case CompactMode.DiagonalLeft: w3 = actualStartCornerLength; w4 = actualLength - actualEndCornerLength; w5 = actualLength - actualStartCornerLength; w6 = actualEndCornerLength; break; case CompactMode.DiagonalRight: w3 = actualLength - actualEndCornerLength; w4 = actualStartCornerLength; w5 = actualEndCornerLength; w6 = actualLength - actualStartCornerLength; break; default: throw new ArgumentOutOfRangeException("Compact"); } } else { w3 = w4 = actualStartCornerLength; w5 = w6 = actualEndCornerLength; } Vector2 corner1 = Vector2.zero; Vector2 corner2 = Vector2.one; corner1.x -= rectTransform.pivot[axis]; corner1.y -= rectTransform.pivot[axis ^ 1]; corner2.x -= rectTransform.pivot[axis]; corner2.y -= rectTransform.pivot[axis ^ 1]; corner1.x *= actualLength; corner1.y *= axis == 0 ? rectTransform.rect.height : rectTransform.rect.width; corner2.x *= actualLength; corner2.y *= axis == 0 ? rectTransform.rect.height : rectTransform.rect.width; vh.Clear(); if (axis == 0) { vh.AddVert(new Vector3(corner1.x, corner2.y), color, new Vector2(uv.x, uv.w)); vh.AddVert(new Vector3(corner1.x, corner1.y), color, new Vector2(uv.x, uv.y)); vh.AddVert(new Vector3(corner1.x + w3, corner2.y), color, new Vector2(uv.x + actualStartUVWidth, uv.w)); vh.AddVert(new Vector3(corner1.x + w4, corner1.y), color, new Vector2(uv.x + actualStartUVWidth, uv.y)); vh.AddVert(new Vector3(corner2.x - w5, corner2.y), color, new Vector2(uv.z - actualEndUVLength, uv.w)); vh.AddVert(new Vector3(corner2.x - w6, corner1.y), color, new Vector2(uv.z - actualEndUVLength, uv.y)); vh.AddVert(new Vector3(corner2.x, corner2.y), color, new Vector2(uv.z, uv.w)); vh.AddVert(new Vector3(corner2.x, corner1.y), color, new Vector2(uv.z, uv.y)); } else { vh.AddVert(new Vector3(corner1.y, corner1.x), color, new Vector2(uv.x, uv.w)); vh.AddVert(new Vector3(corner2.y, corner1.x), color, new Vector2(uv.z, uv.w)); vh.AddVert(new Vector3(corner1.y, corner1.x + w4), color, new Vector2(uv.x, uv.w - actualStartUVWidth)); vh.AddVert(new Vector3(corner2.y, corner1.x + w3), color, new Vector2(uv.z, uv.w - actualStartUVWidth)); vh.AddVert(new Vector3(corner1.y, corner2.x - w6), color, new Vector2(uv.x, uv.y + actualEndUVLength)); vh.AddVert(new Vector3(corner2.y, corner2.x - w5), color, new Vector2(uv.z, uv.y + actualEndUVLength)); vh.AddVert(new Vector3(corner1.y, corner2.x), color, new Vector2(uv.x, uv.y)); vh.AddVert(new Vector3(corner2.y, corner2.x), color, new Vector2(uv.z, uv.y)); } if (((int)Compact & 0x1) == 0) { vh.AddTriangle(2, 1, 0); vh.AddTriangle(1, 2, 3); vh.AddTriangle(4, 3, 2); vh.AddTriangle(3, 4, 5); vh.AddTriangle(6, 5, 4); vh.AddTriangle(5, 6, 7); } else { vh.AddTriangle(3, 1, 0); vh.AddTriangle(0, 2, 3); vh.AddTriangle(5, 3, 2); vh.AddTriangle(2, 4, 5); vh.AddTriangle(7, 5, 4); vh.AddTriangle(4, 6, 7); } } } }