Search Unity

Sliced Image: Keep border aspect.

Discussion in 'Immediate Mode GUI (IMGUI)' started by benbenmushi, Feb 17, 2017.

  1. benbenmushi

    benbenmushi

    Joined:
    Jan 29, 2013
    Posts:
    34
    Hello guys,

    I did some modifications to the original UnityEngine.UI.Image component and I wanted to share the result.
    I added an option in sliced mode only to keep your border aspect when you mess around with the sprite's size.

    The downside of this is that it ignores the pixelPerUnits of your sprite.


    What you can see above is a simple circle 64x64 sliced on every corner (L:32 R:32 T:32 B:32)

    See line 354 and 595 of UIImage.cs
    Also added a function line 1043 so you can easly change your old Image to UIImage. Be carefull with this because you still loses all references to your previous Image component in your hierarchy.

    Code (CSharp):
    1.  
    2. using System;
    3. using UnityEngine.Serialization;
    4.  
    5. /// <summary>
    6. /// Image is a textured element in the UI hierarchy.
    7. /// </summary>
    8. namespace UnityEngine.UI
    9. {
    10.  
    11.     public static class SetPropertyUtility
    12.     {
    13.         public static bool SetStruct<T>(ref T s, T val)
    14.         {
    15.             if (s.Equals(val))
    16.             {
    17.                 s = val;
    18.                 return true;
    19.             }
    20.             return false;
    21.         }
    22.         public static bool SetClass<T>(ref T s, T val)
    23.         {
    24.             if (s.Equals(val))
    25.             {
    26.                 s = val;
    27.                 return true;
    28.             }
    29.             return false;
    30.         }
    31.     }
    32.  
    33.     [AddComponentMenu("UI/UIImage", 11)]
    34.     public class UIImage : MaskableGraphic, ISerializationCallbackReceiver, ILayoutElement, ICanvasRaycastFilter
    35.     {
    36.         public enum Type
    37.         {
    38.             Simple,
    39.             Sliced,
    40.             Tiled,
    41.             Filled
    42.         }
    43.  
    44.         public enum FillMethod
    45.         {
    46.             Horizontal,
    47.             Vertical,
    48.             Radial90,
    49.             Radial180,
    50.             Radial360,
    51.         }
    52.  
    53.         public enum OriginHorizontal
    54.         {
    55.             Left,
    56.             Right,
    57.         }
    58.  
    59.         public enum OriginVertical
    60.         {
    61.             Bottom,
    62.             Top,
    63.         }
    64.  
    65.         public enum Origin90
    66.         {
    67.             BottomLeft,
    68.             TopLeft,
    69.             TopRight,
    70.             BottomRight,
    71.         }
    72.  
    73.         public enum Origin180
    74.         {
    75.             Bottom,
    76.             Left,
    77.             Top,
    78.             Right,
    79.         }
    80.  
    81.         public enum Origin360
    82.         {
    83.             Bottom,
    84.             Right,
    85.             Top,
    86.             Left,
    87.         }
    88.  
    89.         [FormerlySerializedAs("m_Frame")]
    90.         [SerializeField]
    91.         private Sprite m_Sprite;
    92.         public Sprite sprite { get { return m_Sprite; } set { if (SetPropertyUtility.SetClass(ref m_Sprite, value)) SetAllDirty(); } }
    93.  
    94.         [NonSerialized]
    95.         private Sprite m_OverrideSprite;
    96.         public Sprite overrideSprite { get { return m_OverrideSprite == null ? sprite : m_OverrideSprite; } set { if (SetPropertyUtility.SetClass(ref m_OverrideSprite, value)) SetAllDirty(); } }
    97.  
    98.         /// How the Image is drawn.
    99.         [SerializeField]
    100.         private Type m_Type = Type.Simple;
    101.         public Type type { get { return m_Type; } set { if (SetPropertyUtility.SetStruct(ref m_Type, value)) SetVerticesDirty(); } }
    102.  
    103.         [SerializeField]
    104.         private bool m_PreserveAspect = false;
    105.         public bool preserveAspect { get { return m_PreserveAspect; } set { if (SetPropertyUtility.SetStruct(ref m_PreserveAspect, value)) SetVerticesDirty(); } }
    106.  
    107.         [SerializeField]
    108.         private bool m_PreserveAspectSliced = false;
    109.         public bool PreserveAspectSliced { get { return m_PreserveAspectSliced; } set { if (SetPropertyUtility.SetStruct(ref m_PreserveAspectSliced, value)) SetVerticesDirty(); } }
    110.  
    111.         private bool m_FillCenter = true;
    112.         public bool fillCenter { get { return m_FillCenter; } set { if (SetPropertyUtility.SetStruct(ref m_FillCenter, value)) SetVerticesDirty(); } }
    113.  
    114.         /// Filling method for filled sprites.
    115.         [SerializeField]
    116.         private FillMethod m_FillMethod = FillMethod.Radial360;
    117.         public FillMethod fillMethod { get { return m_FillMethod; } set { if (SetPropertyUtility.SetStruct(ref m_FillMethod, value)) { SetVerticesDirty(); m_FillOrigin = 0; } } }
    118.  
    119.         /// Amount of the Image shown. 0-1 range with 0 being nothing shown, and 1 being the full Image.
    120.         [Range(0, 1)]
    121.         [SerializeField]
    122.         private float m_FillAmount = 1.0f;
    123.         public float fillAmount { get { return m_FillAmount; } set { if (SetPropertyUtility.SetStruct(ref m_FillAmount, Mathf.Clamp01(value))) SetVerticesDirty(); } }
    124.  
    125.         /// Whether the Image should be filled clockwise (true) or counter-clockwise (false).
    126.         [SerializeField]
    127.         private bool m_FillClockwise = true;
    128.         public bool fillClockwise { get { return m_FillClockwise; } set { if (SetPropertyUtility.SetStruct(ref m_FillClockwise, value)) SetVerticesDirty(); } }
    129.  
    130.         /// Controls the origin point of the Fill process. Value means different things with each fill method.
    131.         [SerializeField]
    132.         private int m_FillOrigin;
    133.         public int fillOrigin { get { return m_FillOrigin; } set { if (SetPropertyUtility.SetStruct(ref m_FillOrigin, value)) SetVerticesDirty(); } }
    134.  
    135.         // Not serialized until we support read-enabled sprites better.
    136.         private float m_EventAlphaThreshold = 1;
    137.         public float eventAlphaThreshold { get { return m_EventAlphaThreshold; } set { m_EventAlphaThreshold = value; } }
    138.  
    139.         protected UIImage()
    140.         {
    141.             useLegacyMeshGeneration = false;
    142.         }
    143.  
    144.         /// <summary>
    145.         /// Image's texture comes from the UnityEngine.Image.
    146.         /// </summary>
    147.         public override Texture mainTexture
    148.         {
    149.             get
    150.             {
    151.                 if (overrideSprite == null)
    152.                 {
    153.                     if (material != null && material.mainTexture != null)
    154.                     {
    155.                         return material.mainTexture;
    156.                     }
    157.                     return s_WhiteTexture;
    158.                 }
    159.  
    160.                 return overrideSprite.texture;
    161.             }
    162.         }
    163.  
    164.         /// <summary>
    165.         /// Whether the Image has a border to work with.
    166.         /// </summary>
    167.  
    168.         public bool hasBorder
    169.         {
    170.             get
    171.             {
    172.                 if (overrideSprite != null)
    173.                 {
    174.                     Vector4 v = overrideSprite.border;
    175.                     return v.sqrMagnitude > 0f;
    176.                 }
    177.                 return false;
    178.             }
    179.         }
    180.  
    181.         public float pixelsPerUnit
    182.         {
    183.             get
    184.             {
    185.                 float spritePixelsPerUnit = 100;
    186.                 if (sprite)
    187.                     spritePixelsPerUnit = sprite.pixelsPerUnit;
    188.  
    189.                 float referencePixelsPerUnit = 100;
    190.                 if (canvas)
    191.                     referencePixelsPerUnit = canvas.referencePixelsPerUnit;
    192.  
    193.                 return spritePixelsPerUnit / referencePixelsPerUnit;
    194.             }
    195.         }
    196.  
    197.         public virtual void OnBeforeSerialize() { }
    198.  
    199.         public virtual void OnAfterDeserialize()
    200.         {
    201.             if (m_FillOrigin < 0)
    202.                 m_FillOrigin = 0;
    203.             else if (m_FillMethod == FillMethod.Horizontal && m_FillOrigin > 1)
    204.                 m_FillOrigin = 0;
    205.             else if (m_FillMethod == FillMethod.Vertical && m_FillOrigin > 1)
    206.                 m_FillOrigin = 0;
    207.             else if (m_FillOrigin > 3)
    208.                 m_FillOrigin = 0;
    209.  
    210.             m_FillAmount = Mathf.Clamp(m_FillAmount, 0f, 1f);
    211.         }
    212.  
    213.         /// Image's dimensions used for drawing. X = left, Y = bottom, Z = right, W = top.
    214.         private Vector4 GetDrawingDimensions(bool shouldPreserveAspect)
    215.         {
    216.             var padding = overrideSprite == null ? Vector4.zero : Sprites.DataUtility.GetPadding(overrideSprite);
    217.             var size = overrideSprite == null ? Vector2.zero : new Vector2(overrideSprite.rect.width, overrideSprite.rect.height);
    218.  
    219.             Rect r = GetPixelAdjustedRect();
    220.             // Debug.Log(string.Format("r:{2}, size:{0}, padding:{1}", size, padding, r));
    221.  
    222.             int spriteW = Mathf.RoundToInt(size.x);
    223.             int spriteH = Mathf.RoundToInt(size.y);
    224.  
    225.             var v = new Vector4(
    226.                     padding.x / spriteW,
    227.                     padding.y / spriteH,
    228.                     (spriteW - padding.z) / spriteW,
    229.                     (spriteH - padding.w) / spriteH);
    230.  
    231.             if (shouldPreserveAspect && size.sqrMagnitude > 0.0f)
    232.             {
    233.                 var spriteRatio = size.x / size.y;
    234.                 var rectRatio = r.width / r.height;
    235.  
    236.                 if (spriteRatio > rectRatio)
    237.                 {
    238.                     var oldHeight = r.height;
    239.                     r.height = r.width * (1.0f / spriteRatio);
    240.                     r.y += (oldHeight - r.height) * rectTransform.pivot.y;
    241.                 }
    242.                 else
    243.                 {
    244.                     var oldWidth = r.width;
    245.                     r.width = r.height * spriteRatio;
    246.                     r.x += (oldWidth - r.width) * rectTransform.pivot.x;
    247.                 }
    248.             }
    249.  
    250.             v = new Vector4(
    251.                     r.x + r.width * v.x,
    252.                     r.y + r.height * v.y,
    253.                     r.x + r.width * v.z,
    254.                     r.y + r.height * v.w
    255.                     );
    256.  
    257.             return v;
    258.         }
    259.  
    260.         public override void SetNativeSize()
    261.         {
    262.             if (overrideSprite != null)
    263.             {
    264.                 float w = overrideSprite.rect.width / pixelsPerUnit;
    265.                 float h = overrideSprite.rect.height / pixelsPerUnit;
    266.                 rectTransform.anchorMax = rectTransform.anchorMin;
    267.                 rectTransform.sizeDelta = new Vector2(w, h);
    268.                 SetAllDirty();
    269.             }
    270.         }
    271.  
    272.         /// <summary>
    273.         /// Update the UI renderer mesh.
    274.         /// </summary>
    275.         protected override void OnPopulateMesh(VertexHelper toFill)
    276.         {
    277.             if (overrideSprite == null)
    278.             {
    279.                 base.OnPopulateMesh(toFill);
    280.                 return;
    281.             }
    282.  
    283.             switch (type)
    284.             {
    285.                 case Type.Simple:
    286.                     GenerateSimpleSprite(toFill, m_PreserveAspect);
    287.                     break;
    288.                 case Type.Sliced:
    289.                     GenerateSlicedSprite(toFill);
    290.                     break;
    291.                 case Type.Tiled:
    292.                     GenerateTiledSprite(toFill);
    293.                     break;
    294.                 case Type.Filled:
    295.                     GenerateFilledSprite(toFill, m_PreserveAspect);
    296.                     break;
    297.             }
    298.         }
    299.  
    300.         #region Various fill functions
    301.         /// <summary>
    302.         /// Generate vertices for a simple Image.
    303.         /// </summary>
    304.         void GenerateSimpleSprite(VertexHelper vh, bool lPreserveAspect)
    305.         {
    306.             Vector4 v = GetDrawingDimensions(lPreserveAspect);
    307.             var uv = (overrideSprite != null) ? Sprites.DataUtility.GetOuterUV(overrideSprite) : Vector4.zero;
    308.  
    309.             var color32 = color;
    310.             vh.Clear();
    311.             vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(uv.x, uv.y));
    312.             vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(uv.x, uv.w));
    313.             vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(uv.z, uv.w));
    314.             vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(uv.z, uv.y));
    315.  
    316.             vh.AddTriangle(0, 1, 2);
    317.             vh.AddTriangle(2, 3, 0);
    318.         }
    319.  
    320.         /// <summary>
    321.         /// Generate vertices for a 9-sliced Image.
    322.         /// </summary>
    323.  
    324.         static readonly Vector2[] s_VertScratch = new Vector2[4];
    325.         static readonly Vector2[] s_UVScratch = new Vector2[4];
    326.  
    327.         private void GenerateSlicedSprite(VertexHelper toFill)
    328.         {
    329.             if (!hasBorder)
    330.             {
    331.                 GenerateSimpleSprite(toFill, false);
    332.                 return;
    333.             }
    334.  
    335.             Vector4 outer, inner, padding, border;
    336.  
    337.             if (overrideSprite != null)
    338.             {
    339.                 outer = Sprites.DataUtility.GetOuterUV(overrideSprite);
    340.                 inner = Sprites.DataUtility.GetInnerUV(overrideSprite);
    341.                 padding = Sprites.DataUtility.GetPadding(overrideSprite);
    342.                 border = overrideSprite.border;
    343.             }
    344.             else
    345.             {
    346.                 outer = Vector4.zero;
    347.                 inner = Vector4.zero;
    348.                 padding = Vector4.zero;
    349.                 border = Vector4.zero;
    350.             }
    351.  
    352.             Rect rect = GetPixelAdjustedRect();
    353.             if (PreserveAspectSliced)
    354.                 border = GetAdjustedSlicedBorders(border / pixelsPerUnit, rect, new Vector2(overrideSprite.rect.width, overrideSprite.rect.height) / pixelsPerUnit);
    355.             else
    356.                 border = GetAdjustedBorders(border / pixelsPerUnit, rect);
    357.  
    358.             padding = padding / pixelsPerUnit;
    359.  
    360.             s_VertScratch[0] = new Vector2(padding.x, padding.y);
    361.             s_VertScratch[3] = new Vector2(rect.width - padding.z, rect.height - padding.w);
    362.  
    363.             s_VertScratch[1].x = border.x;
    364.             s_VertScratch[1].y = border.y;
    365.             s_VertScratch[2].x = rect.width - border.z;
    366.             s_VertScratch[2].y = rect.height - border.w;
    367.  
    368.             for (int i = 0; i < 4; ++i)
    369.             {
    370.                 s_VertScratch[i].x += rect.x;
    371.                 s_VertScratch[i].y += rect.y;
    372.             }
    373.  
    374.             s_UVScratch[0] = new Vector2(outer.x, outer.y);
    375.             s_UVScratch[1] = new Vector2(inner.x, inner.y);
    376.             s_UVScratch[2] = new Vector2(inner.z, inner.w);
    377.             s_UVScratch[3] = new Vector2(outer.z, outer.w);
    378.  
    379.             toFill.Clear();
    380.  
    381.             for (int x = 0; x < 3; ++x)
    382.             {
    383.                 int x2 = x + 1;
    384.  
    385.                 for (int y = 0; y < 3; ++y)
    386.                 {
    387.                     if (!m_FillCenter && x == 1 && y == 1)
    388.                         continue;
    389.  
    390.                     int y2 = y + 1;
    391.                     AddQuad(toFill,
    392.                         new Vector2(s_VertScratch[x].x, s_VertScratch[y].y),
    393.                         new Vector2(s_VertScratch[x2].x, s_VertScratch[y2].y),
    394.                         color,
    395.                         new Vector2(s_UVScratch[x].x, s_UVScratch[y].y),
    396.                         new Vector2(s_UVScratch[x2].x, s_UVScratch[y2].y));
    397.                 }
    398.             }
    399.         }
    400.  
    401.         /// <summary>
    402.         /// Generate vertices for a tiled Image.
    403.         /// </summary>
    404.  
    405.         void GenerateTiledSprite(VertexHelper toFill)
    406.         {
    407.             Vector4 outer, inner, border;
    408.             Vector2 spriteSize;
    409.  
    410.             if (overrideSprite != null)
    411.             {
    412.                 outer = Sprites.DataUtility.GetOuterUV(overrideSprite);
    413.                 inner = Sprites.DataUtility.GetInnerUV(overrideSprite);
    414.                 border = overrideSprite.border;
    415.                 spriteSize = overrideSprite.rect.size;
    416.             }
    417.             else
    418.             {
    419.                 outer = Vector4.zero;
    420.                 inner = Vector4.zero;
    421.                 border = Vector4.zero;
    422.                 spriteSize = Vector2.one * 100;
    423.             }
    424.  
    425.             Rect rect = GetPixelAdjustedRect();
    426.             float tileWidth = (spriteSize.x - border.x - border.z) / pixelsPerUnit;
    427.             float tileHeight = (spriteSize.y - border.y - border.w) / pixelsPerUnit;
    428.             border = GetAdjustedBorders(border / pixelsPerUnit, rect);
    429.  
    430.             var uvMin = new Vector2(inner.x, inner.y);
    431.             var uvMax = new Vector2(inner.z, inner.w);
    432.  
    433.             var v = UIVertex.simpleVert;
    434.             v.color = color;
    435.  
    436.             // Min to max max range for tiled region in coordinates relative to lower left corner.
    437.             float xMin = border.x;
    438.             float xMax = rect.width - border.z;
    439.             float yMin = border.y;
    440.             float yMax = rect.height - border.w;
    441.  
    442.             toFill.Clear();
    443.             var clipped = uvMax;
    444.  
    445.             // if either with is zero we cant tile so just assume it was the full width.
    446.             if (tileWidth == 0)
    447.                 tileWidth = xMax - xMin;
    448.  
    449.             if (tileHeight == 0)
    450.                 tileHeight = yMax - yMin;
    451.  
    452.             if (m_FillCenter)
    453.             {
    454.                 for (float y1 = yMin; y1 < yMax; y1 += tileHeight)
    455.                 {
    456.                     float y2 = y1 + tileHeight;
    457.                     if (y2 > yMax)
    458.                     {
    459.                         clipped.y = uvMin.y + (uvMax.y - uvMin.y) * (yMax - y1) / (y2 - y1);
    460.                         y2 = yMax;
    461.                     }
    462.  
    463.                     clipped.x = uvMax.x;
    464.                     for (float x1 = xMin; x1 < xMax; x1 += tileWidth)
    465.                     {
    466.                         float x2 = x1 + tileWidth;
    467.                         if (x2 > xMax)
    468.                         {
    469.                             clipped.x = uvMin.x + (uvMax.x - uvMin.x) * (xMax - x1) / (x2 - x1);
    470.                             x2 = xMax;
    471.                         }
    472.                         AddQuad(toFill, new Vector2(x1, y1) + rect.position, new Vector2(x2, y2) + rect.position, color, uvMin, clipped);
    473.                     }
    474.                 }
    475.             }
    476.  
    477.             if (hasBorder)
    478.             {
    479.                 clipped = uvMax;
    480.                 for (float y1 = yMin; y1 < yMax; y1 += tileHeight)
    481.                 {
    482.                     float y2 = y1 + tileHeight;
    483.                     if (y2 > yMax)
    484.                     {
    485.                         clipped.y = uvMin.y + (uvMax.y - uvMin.y) * (yMax - y1) / (y2 - y1);
    486.                         y2 = yMax;
    487.                     }
    488.                     AddQuad(toFill,
    489.                         new Vector2(0, y1) + rect.position,
    490.                         new Vector2(xMin, y2) + rect.position,
    491.                         color,
    492.                         new Vector2(outer.x, uvMin.y),
    493.                         new Vector2(uvMin.x, clipped.y));
    494.                     AddQuad(toFill,
    495.                         new Vector2(xMax, y1) + rect.position,
    496.                         new Vector2(rect.width, y2) + rect.position,
    497.                         color,
    498.                         new Vector2(uvMax.x, uvMin.y),
    499.                         new Vector2(outer.z, clipped.y));
    500.                 }
    501.  
    502.                 // Bottom and top tiled border
    503.                 clipped = uvMax;
    504.                 for (float x1 = xMin; x1 < xMax; x1 += tileWidth)
    505.                 {
    506.                     float x2 = x1 + tileWidth;
    507.                     if (x2 > xMax)
    508.                     {
    509.                         clipped.x = uvMin.x + (uvMax.x - uvMin.x) * (xMax - x1) / (x2 - x1);
    510.                         x2 = xMax;
    511.                     }
    512.                     AddQuad(toFill,
    513.                         new Vector2(x1, 0) + rect.position,
    514.                         new Vector2(x2, yMin) + rect.position,
    515.                         color,
    516.                         new Vector2(uvMin.x, outer.y),
    517.                         new Vector2(clipped.x, uvMin.y));
    518.                     AddQuad(toFill,
    519.                         new Vector2(x1, yMax) + rect.position,
    520.                         new Vector2(x2, rect.height) + rect.position,
    521.                         color,
    522.                         new Vector2(uvMin.x, uvMax.y),
    523.                         new Vector2(clipped.x, outer.w));
    524.                 }
    525.  
    526.                 // Corners
    527.                 AddQuad(toFill,
    528.                     new Vector2(0, 0) + rect.position,
    529.                     new Vector2(xMin, yMin) + rect.position,
    530.                     color,
    531.                     new Vector2(outer.x, outer.y),
    532.                     new Vector2(uvMin.x, uvMin.y));
    533.                 AddQuad(toFill,
    534.                     new Vector2(xMax, 0) + rect.position,
    535.                     new Vector2(rect.width, yMin) + rect.position,
    536.                     color,
    537.                     new Vector2(uvMax.x, outer.y),
    538.                     new Vector2(outer.z, uvMin.y));
    539.                 AddQuad(toFill,
    540.                     new Vector2(0, yMax) + rect.position,
    541.                     new Vector2(xMin, rect.height) + rect.position,
    542.                     color,
    543.                     new Vector2(outer.x, uvMax.y),
    544.                     new Vector2(uvMin.x, outer.w));
    545.                 AddQuad(toFill,
    546.                     new Vector2(xMax, yMax) + rect.position,
    547.                     new Vector2(rect.width, rect.height) + rect.position,
    548.                     color,
    549.                     new Vector2(uvMax.x, uvMax.y),
    550.                     new Vector2(outer.z, outer.w));
    551.             }
    552.         }
    553.  
    554.         static void AddQuad(VertexHelper vertexHelper, Vector3[] quadPositions, Color32 color, Vector3[] quadUVs)
    555.         {
    556.             int startIndex = vertexHelper.currentVertCount;
    557.  
    558.             for (int i = 0; i < 4; ++i)
    559.                 vertexHelper.AddVert(quadPositions[i], color, quadUVs[i]);
    560.  
    561.             vertexHelper.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
    562.             vertexHelper.AddTriangle(startIndex + 2, startIndex + 3, startIndex);
    563.         }
    564.  
    565.         static void AddQuad(VertexHelper vertexHelper, Vector2 posMin, Vector2 posMax, Color32 color, Vector2 uvMin, Vector2 uvMax)
    566.         {
    567.             int startIndex = vertexHelper.currentVertCount;
    568.  
    569.             vertexHelper.AddVert(new Vector3(posMin.x, posMin.y, 0), color, new Vector2(uvMin.x, uvMin.y));
    570.             vertexHelper.AddVert(new Vector3(posMin.x, posMax.y, 0), color, new Vector2(uvMin.x, uvMax.y));
    571.             vertexHelper.AddVert(new Vector3(posMax.x, posMax.y, 0), color, new Vector2(uvMax.x, uvMax.y));
    572.             vertexHelper.AddVert(new Vector3(posMax.x, posMin.y, 0), color, new Vector2(uvMax.x, uvMin.y));
    573.  
    574.             vertexHelper.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
    575.             vertexHelper.AddTriangle(startIndex + 2, startIndex + 3, startIndex);
    576.         }
    577.  
    578.         Vector4 GetAdjustedBorders(Vector4 border, Rect rect)
    579.         {
    580.             for (int axis = 0; axis <= 1; axis++)
    581.             {
    582.                 // If the rect is smaller than the combined borders, then there's not room for the borders at their normal size.
    583.                 // In order to avoid artefacts with overlapping borders, we scale the borders down to fit.
    584.                 float combinedBorders = border[axis] + border[axis + 2];
    585.                 if (rect.size[axis] < combinedBorders && combinedBorders != 0)
    586.                 {
    587.                     float borderScaleRatio = rect.size[axis] / combinedBorders;
    588.                     border[axis] *= borderScaleRatio;
    589.                     border[axis + 2] *= borderScaleRatio;
    590.                 }
    591.             }
    592.             return border;
    593.         }
    594.  
    595.         Vector4 GetAdjustedSlicedBorders(Vector4 border, Rect rect, Vector2 spriteSize)
    596.         {
    597.             Vector2 combinedBorders = new Vector2(border[0] + border[2], border[1] + border[3]);
    598.  
    599.             if (combinedBorders[0] == 0)
    600.                 combinedBorders[0] = spriteSize[0];
    601.             if (combinedBorders[1] == 0)
    602.                 combinedBorders[1] = spriteSize[1];
    603.             Vector2 borderScaleRatio = new Vector2(rect.size[0] / combinedBorders[0], rect.size[1] / combinedBorders[1]);
    604.  
    605.             if (borderScaleRatio[0] < borderScaleRatio[1])
    606.                 return border * borderScaleRatio[0] * (combinedBorders[0] / spriteSize[0]);
    607.             else
    608.                 return border * borderScaleRatio[1] * (combinedBorders[1] / spriteSize[1]);
    609.         }
    610.  
    611.         /// <summary>
    612.         /// Generate vertices for a filled Image.
    613.         /// </summary>
    614.  
    615.         static readonly Vector3[] s_Xy = new Vector3[4];
    616.         static readonly Vector3[] s_Uv = new Vector3[4];
    617.         void GenerateFilledSprite(VertexHelper toFill, bool preserveAspect)
    618.         {
    619.             toFill.Clear();
    620.  
    621.             if (m_FillAmount < 0.001f)
    622.                 return;
    623.  
    624.             Vector4 v = GetDrawingDimensions(preserveAspect);
    625.             Vector4 outer = overrideSprite != null ? Sprites.DataUtility.GetOuterUV(overrideSprite) : Vector4.zero;
    626.             UIVertex uiv = UIVertex.simpleVert;
    627.             uiv.color = color;
    628.  
    629.             float tx0 = outer.x;
    630.             float ty0 = outer.y;
    631.             float tx1 = outer.z;
    632.             float ty1 = outer.w;
    633.  
    634.             // Horizontal and vertical filled sprites are simple -- just end the Image prematurely
    635.             if (m_FillMethod == FillMethod.Horizontal || m_FillMethod == FillMethod.Vertical)
    636.             {
    637.                 if (fillMethod == FillMethod.Horizontal)
    638.                 {
    639.                     float fill = (tx1 - tx0) * m_FillAmount;
    640.  
    641.                     if (m_FillOrigin == 1)
    642.                     {
    643.                         v.x = v.z - (v.z - v.x) * m_FillAmount;
    644.                         tx0 = tx1 - fill;
    645.                     }
    646.                     else
    647.                     {
    648.                         v.z = v.x + (v.z - v.x) * m_FillAmount;
    649.                         tx1 = tx0 + fill;
    650.                     }
    651.                 }
    652.                 else if (fillMethod == FillMethod.Vertical)
    653.                 {
    654.                     float fill = (ty1 - ty0) * m_FillAmount;
    655.  
    656.                     if (m_FillOrigin == 1)
    657.                     {
    658.                         v.y = v.w - (v.w - v.y) * m_FillAmount;
    659.                         ty0 = ty1 - fill;
    660.                     }
    661.                     else
    662.                     {
    663.                         v.w = v.y + (v.w - v.y) * m_FillAmount;
    664.                         ty1 = ty0 + fill;
    665.                     }
    666.                 }
    667.             }
    668.  
    669.             s_Xy[0] = new Vector2(v.x, v.y);
    670.             s_Xy[1] = new Vector2(v.x, v.w);
    671.             s_Xy[2] = new Vector2(v.z, v.w);
    672.             s_Xy[3] = new Vector2(v.z, v.y);
    673.  
    674.             s_Uv[0] = new Vector2(tx0, ty0);
    675.             s_Uv[1] = new Vector2(tx0, ty1);
    676.             s_Uv[2] = new Vector2(tx1, ty1);
    677.             s_Uv[3] = new Vector2(tx1, ty0);
    678.  
    679.             {
    680.                 if (m_FillAmount < 1f && m_FillMethod != FillMethod.Horizontal && m_FillMethod != FillMethod.Vertical)
    681.                 {
    682.                     if (fillMethod == FillMethod.Radial90)
    683.                     {
    684.                         if (RadialCut(s_Xy, s_Uv, m_FillAmount, m_FillClockwise, m_FillOrigin))
    685.                             AddQuad(toFill, s_Xy, color, s_Uv);
    686.                     }
    687.                     else if (fillMethod == FillMethod.Radial180)
    688.                     {
    689.                         for (int side = 0; side < 2; ++side)
    690.                         {
    691.                             float fx0, fx1, fy0, fy1;
    692.                             int even = m_FillOrigin > 1 ? 1 : 0;
    693.  
    694.                             if (m_FillOrigin == 0 || m_FillOrigin == 2)
    695.                             {
    696.                                 fy0 = 0f;
    697.                                 fy1 = 1f;
    698.                                 if (side == even)
    699.                                 {
    700.                                     fx0 = 0f;
    701.                                     fx1 = 0.5f;
    702.                                 }
    703.                                 else
    704.                                 {
    705.                                     fx0 = 0.5f;
    706.                                     fx1 = 1f;
    707.                                 }
    708.                             }
    709.                             else
    710.                             {
    711.                                 fx0 = 0f;
    712.                                 fx1 = 1f;
    713.                                 if (side == even)
    714.                                 {
    715.                                     fy0 = 0.5f;
    716.                                     fy1 = 1f;
    717.                                 }
    718.                                 else
    719.                                 {
    720.                                     fy0 = 0f;
    721.                                     fy1 = 0.5f;
    722.                                 }
    723.                             }
    724.  
    725.                             s_Xy[0].x = Mathf.Lerp(v.x, v.z, fx0);
    726.                             s_Xy[1].x = s_Xy[0].x;
    727.                             s_Xy[2].x = Mathf.Lerp(v.x, v.z, fx1);
    728.                             s_Xy[3].x = s_Xy[2].x;
    729.  
    730.                             s_Xy[0].y = Mathf.Lerp(v.y, v.w, fy0);
    731.                             s_Xy[1].y = Mathf.Lerp(v.y, v.w, fy1);
    732.                             s_Xy[2].y = s_Xy[1].y;
    733.                             s_Xy[3].y = s_Xy[0].y;
    734.  
    735.                             s_Uv[0].x = Mathf.Lerp(tx0, tx1, fx0);
    736.                             s_Uv[1].x = s_Uv[0].x;
    737.                             s_Uv[2].x = Mathf.Lerp(tx0, tx1, fx1);
    738.                             s_Uv[3].x = s_Uv[2].x;
    739.  
    740.                             s_Uv[0].y = Mathf.Lerp(ty0, ty1, fy0);
    741.                             s_Uv[1].y = Mathf.Lerp(ty0, ty1, fy1);
    742.                             s_Uv[2].y = s_Uv[1].y;
    743.                             s_Uv[3].y = s_Uv[0].y;
    744.  
    745.                             float val = m_FillClockwise ? fillAmount * 2f - side : m_FillAmount * 2f - (1 - side);
    746.  
    747.                             if (RadialCut(s_Xy, s_Uv, Mathf.Clamp01(val), m_FillClockwise, ((side + m_FillOrigin + 3) % 4)))
    748.                             {
    749.                                 AddQuad(toFill, s_Xy, color, s_Uv);
    750.                             }
    751.                         }
    752.                     }
    753.                     else if (fillMethod == FillMethod.Radial360)
    754.                     {
    755.                         for (int corner = 0; corner < 4; ++corner)
    756.                         {
    757.                             float fx0, fx1, fy0, fy1;
    758.  
    759.                             if (corner < 2)
    760.                             {
    761.                                 fx0 = 0f;
    762.                                 fx1 = 0.5f;
    763.                             }
    764.                             else
    765.                             {
    766.                                 fx0 = 0.5f;
    767.                                 fx1 = 1f;
    768.                             }
    769.  
    770.                             if (corner == 0 || corner == 3)
    771.                             {
    772.                                 fy0 = 0f;
    773.                                 fy1 = 0.5f;
    774.                             }
    775.                             else
    776.                             {
    777.                                 fy0 = 0.5f;
    778.                                 fy1 = 1f;
    779.                             }
    780.  
    781.                             s_Xy[0].x = Mathf.Lerp(v.x, v.z, fx0);
    782.                             s_Xy[1].x = s_Xy[0].x;
    783.                             s_Xy[2].x = Mathf.Lerp(v.x, v.z, fx1);
    784.                             s_Xy[3].x = s_Xy[2].x;
    785.  
    786.                             s_Xy[0].y = Mathf.Lerp(v.y, v.w, fy0);
    787.                             s_Xy[1].y = Mathf.Lerp(v.y, v.w, fy1);
    788.                             s_Xy[2].y = s_Xy[1].y;
    789.                             s_Xy[3].y = s_Xy[0].y;
    790.  
    791.                             s_Uv[0].x = Mathf.Lerp(tx0, tx1, fx0);
    792.                             s_Uv[1].x = s_Uv[0].x;
    793.                             s_Uv[2].x = Mathf.Lerp(tx0, tx1, fx1);
    794.                             s_Uv[3].x = s_Uv[2].x;
    795.  
    796.                             s_Uv[0].y = Mathf.Lerp(ty0, ty1, fy0);
    797.                             s_Uv[1].y = Mathf.Lerp(ty0, ty1, fy1);
    798.                             s_Uv[2].y = s_Uv[1].y;
    799.                             s_Uv[3].y = s_Uv[0].y;
    800.  
    801.                             float val = m_FillClockwise ?
    802.                                 m_FillAmount * 4f - ((corner + m_FillOrigin) % 4) :
    803.                                 m_FillAmount * 4f - (3 - ((corner + m_FillOrigin) % 4));
    804.  
    805.                             if (RadialCut(s_Xy, s_Uv, Mathf.Clamp01(val), m_FillClockwise, ((corner + 2) % 4)))
    806.                                 AddQuad(toFill, s_Xy, color, s_Uv);
    807.                         }
    808.                     }
    809.                 }
    810.                 else
    811.                 {
    812.                     AddQuad(toFill, s_Xy, color, s_Uv);
    813.                 }
    814.             }
    815.         }
    816.  
    817.         /// <summary>
    818.         /// Adjust the specified quad, making it be radially filled instead.
    819.         /// </summary>
    820.  
    821.         static bool RadialCut(Vector3[] xy, Vector3[] uv, float fill, bool invert, int corner)
    822.         {
    823.             // Nothing to fill
    824.             if (fill < 0.001f) return false;
    825.  
    826.             // Even corners invert the fill direction
    827.             if ((corner & 1) == 1) invert = !invert;
    828.  
    829.             // Nothing to adjust
    830.             if (!invert && fill > 0.999f) return true;
    831.  
    832.             // Convert 0-1 value into 0 to 90 degrees angle in radians
    833.             float angle = Mathf.Clamp01(fill);
    834.             if (invert) angle = 1f - angle;
    835.             angle *= 90f * Mathf.Deg2Rad;
    836.  
    837.             // Calculate the effective X and Y factors
    838.             float cos = Mathf.Cos(angle);
    839.             float sin = Mathf.Sin(angle);
    840.  
    841.             RadialCut(xy, cos, sin, invert, corner);
    842.             RadialCut(uv, cos, sin, invert, corner);
    843.             return true;
    844.         }
    845.  
    846.         /// <summary>
    847.         /// Adjust the specified quad, making it be radially filled instead.
    848.         /// </summary>
    849.  
    850.         static void RadialCut(Vector3[] xy, float cos, float sin, bool invert, int corner)
    851.         {
    852.             int i0 = corner;
    853.             int i1 = ((corner + 1) % 4);
    854.             int i2 = ((corner + 2) % 4);
    855.             int i3 = ((corner + 3) % 4);
    856.  
    857.             if ((corner & 1) == 1)
    858.             {
    859.                 if (sin > cos)
    860.                 {
    861.                     cos /= sin;
    862.                     sin = 1f;
    863.  
    864.                     if (invert)
    865.                     {
    866.                         xy[i1].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos);
    867.                         xy[i2].x = xy[i1].x;
    868.                     }
    869.                 }
    870.                 else if (cos > sin)
    871.                 {
    872.                     sin /= cos;
    873.                     cos = 1f;
    874.  
    875.                     if (!invert)
    876.                     {
    877.                         xy[i2].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin);
    878.                         xy[i3].y = xy[i2].y;
    879.                     }
    880.                 }
    881.                 else
    882.                 {
    883.                     cos = 1f;
    884.                     sin = 1f;
    885.                 }
    886.  
    887.                 if (!invert) xy[i3].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos);
    888.                 else xy[i1].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin);
    889.             }
    890.             else
    891.             {
    892.                 if (cos > sin)
    893.                 {
    894.                     sin /= cos;
    895.                     cos = 1f;
    896.  
    897.                     if (!invert)
    898.                     {
    899.                         xy[i1].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin);
    900.                         xy[i2].y = xy[i1].y;
    901.                     }
    902.                 }
    903.                 else if (sin > cos)
    904.                 {
    905.                     cos /= sin;
    906.                     sin = 1f;
    907.  
    908.                     if (invert)
    909.                     {
    910.                         xy[i2].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos);
    911.                         xy[i3].x = xy[i2].x;
    912.                     }
    913.                 }
    914.                 else
    915.                 {
    916.                     cos = 1f;
    917.                     sin = 1f;
    918.                 }
    919.  
    920.                 if (invert) xy[i3].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin);
    921.                 else xy[i1].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos);
    922.             }
    923.         }
    924.  
    925.         #endregion
    926.  
    927.         public virtual void CalculateLayoutInputHorizontal() { }
    928.         public virtual void CalculateLayoutInputVertical() { }
    929.  
    930.         public virtual float minWidth { get { return 0; } }
    931.  
    932.         public virtual float preferredWidth
    933.         {
    934.             get
    935.             {
    936.                 if (overrideSprite == null)
    937.                     return 0;
    938.                 if (type == Type.Sliced || type == Type.Tiled)
    939.                     return Sprites.DataUtility.GetMinSize(overrideSprite).x / pixelsPerUnit;
    940.                 return overrideSprite.rect.size.x / pixelsPerUnit;
    941.             }
    942.         }
    943.  
    944.         public virtual float flexibleWidth { get { return -1; } }
    945.  
    946.         public virtual float minHeight { get { return 0; } }
    947.  
    948.         public virtual float preferredHeight
    949.         {
    950.             get
    951.             {
    952.                 if (overrideSprite == null)
    953.                     return 0;
    954.                 if (type == Type.Sliced || type == Type.Tiled)
    955.                     return Sprites.DataUtility.GetMinSize(overrideSprite).y / pixelsPerUnit;
    956.                 return overrideSprite.rect.size.y / pixelsPerUnit;
    957.             }
    958.         }
    959.  
    960.         public virtual float flexibleHeight { get { return -1; } }
    961.  
    962.         public virtual int layoutPriority { get { return 0; } }
    963.  
    964.         public virtual bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
    965.         {
    966.             if (m_EventAlphaThreshold >= 1)
    967.                 return true;
    968.  
    969.             Sprite sprite = overrideSprite;
    970.             if (sprite == null)
    971.                 return true;
    972.  
    973.             Vector2 local;
    974.             RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out local);
    975.  
    976.             Rect rect = GetPixelAdjustedRect();
    977.  
    978.             // Convert to have lower left corner as reference point.
    979.             local.x += rectTransform.pivot.x * rect.width;
    980.             local.y += rectTransform.pivot.y * rect.height;
    981.  
    982.             local = MapCoordinate(local, rect);
    983.  
    984.             // Normalize local coordinates.
    985.             Rect spriteRect = sprite.textureRect;
    986.             Vector2 normalized = new Vector2(local.x / spriteRect.width, local.y / spriteRect.height);
    987.  
    988.             // Convert to texture space.
    989.             float x = Mathf.Lerp(spriteRect.x, spriteRect.xMax, normalized.x) / sprite.texture.width;
    990.             float y = Mathf.Lerp(spriteRect.y, spriteRect.yMax, normalized.y) / sprite.texture.height;
    991.  
    992.             try
    993.             {
    994.                 return sprite.texture.GetPixelBilinear(x, y).a >= m_EventAlphaThreshold;
    995.             }
    996.             catch (UnityException e)
    997.             {
    998.                 Debug.LogError("Using clickAlphaThreshold lower than 1 on Image whose sprite texture cannot be read. " + e.Message + " Also make sure to disable sprite packing for this sprite.", this);
    999.                 return true;
    1000.             }
    1001.         }
    1002.  
    1003.         private Vector2 MapCoordinate(Vector2 local, Rect rect)
    1004.         {
    1005.             Rect spriteRect = sprite.rect;
    1006.             if (type == Type.Simple || type == Type.Filled)
    1007.                 return new Vector2(local.x * spriteRect.width / rect.width, local.y * spriteRect.height / rect.height);
    1008.  
    1009.             Vector4 border = sprite.border;
    1010.             Vector4 adjustedBorder = GetAdjustedBorders(border / pixelsPerUnit, rect);
    1011.  
    1012.             for (int i = 0; i < 2; i++)
    1013.             {
    1014.                 if (local[i] <= adjustedBorder[i])
    1015.                     continue;
    1016.  
    1017.                 if (rect.size[i] - local[i] <= adjustedBorder[i + 2])
    1018.                 {
    1019.                     local[i] -= (rect.size[i] - spriteRect.size[i]);
    1020.                     continue;
    1021.                 }
    1022.  
    1023.                 if (type == Type.Sliced)
    1024.                 {
    1025.                     float lerp = Mathf.InverseLerp(adjustedBorder[i], rect.size[i] - adjustedBorder[i + 2], local[i]);
    1026.                     local[i] = Mathf.Lerp(border[i], spriteRect.size[i] - border[i + 2], lerp);
    1027.                     continue;
    1028.                 }
    1029.                 else
    1030.                 {
    1031.                     local[i] -= adjustedBorder[i];
    1032.                     local[i] = Mathf.Repeat(local[i], spriteRect.size[i] - border[i] - border[i + 2]);
    1033.                     local[i] += border[i];
    1034.                     continue;
    1035.                 }
    1036.             }
    1037.  
    1038.             return local;
    1039.         }
    1040.  
    1041.  
    1042. #if UNITY_EDITOR
    1043.         [UnityEditor.MenuItem("CONTEXT/Image/Replace With UIImage")]
    1044.         private static void ReplaceImage(UnityEditor.MenuCommand menuCommand)
    1045.         {
    1046.             Image sourceImage = menuCommand.context as Image;
    1047.  
    1048.             if (sourceImage != null && sourceImage.GetComponent<UIImage>() == null)
    1049.             {
    1050.                 GameObject go = sourceImage.gameObject;
    1051.                 bool preserveAspect = sourceImage.preserveAspect;
    1052.                 Sprite sprite = sourceImage.sprite;
    1053.                 Color color = sourceImage.color;
    1054.                 UIImage.Type type = (UIImage.Type)(int)sourceImage.type;
    1055.                 bool raycastTarget = sourceImage.raycastTarget;
    1056.                 Material mat = sourceImage.material;
    1057.                 bool fillCenter = sourceImage.fillCenter;
    1058.                 float fillAmount = sourceImage.fillAmount;
    1059.                 bool fillClockwise = sourceImage.fillClockwise;
    1060.                 UIImage.FillMethod fillMethod = (UIImage.FillMethod)(int)sourceImage.fillMethod;
    1061.                 int fillOrigin = sourceImage.fillOrigin;
    1062.  
    1063.                 UnityEditor.Undo.DestroyObjectImmediate(sourceImage);
    1064.                 UIImage newImage = go.AddComponent<UIImage>();
    1065.                 UnityEditor.Undo.RegisterCreatedObjectUndo(newImage, "new UIImage");
    1066.  
    1067.                 newImage.m_Sprite = sprite;
    1068.                 newImage.m_PreserveAspect = preserveAspect;
    1069.                 newImage.m_Type = type;
    1070.                 newImage.color = color;
    1071.                 newImage.raycastTarget = raycastTarget;
    1072.                 newImage.material = mat;
    1073.                 newImage.fillCenter = fillCenter;
    1074.                 newImage.fillAmount = fillAmount;
    1075.                 newImage.fillClockwise = fillClockwise;
    1076.                 newImage.fillMethod = fillMethod;
    1077.                 newImage.fillOrigin = fillOrigin;
    1078.             }
    1079.         }
    1080. #endif
    1081.     }
    1082. }
    1083.  
    Hope you guys enjoy

    - Benbenmushi
     
    Last edited: Feb 17, 2017
  2. Thibaut2810

    Thibaut2810

    Joined:
    Jul 1, 2014
    Posts:
    2
    Nice work !
     
  3. Sylafrs

    Sylafrs

    Joined:
    Jun 25, 2013
    Posts:
    65
    Will try it later;

    Was playing with pixel per unit, depending to the shape I needed, it was annoying ^^
    I hope this can be used in a future Unity version :p

    From which Unity version did you take the Image script you modified?
     
  4. benbenmushi

    benbenmushi

    Joined:
    Jan 29, 2013
    Posts:
    34