Skip to content

Instantly share code, notes, and snippets.

@LokoSoloGames
Last active May 31, 2023 03:06
Show Gist options
  • Save LokoSoloGames/8ff7b131a33435068df3a7681bf3f9a2 to your computer and use it in GitHub Desktop.
Save LokoSoloGames/8ff7b131a33435068df3a7681bf3f9a2 to your computer and use it in GitHub Desktop.
Unity UI Image that expand sprite borders in Sliced Image Type
using UnityEngine.Sprites;
namespace UnityEngine.UI {
public class InversedSlicedImage : Image {
private static readonly Vector2[] s_VertCache = new Vector2[4];
private static readonly Vector2[] s_UVCache = new Vector2[4];
protected override void OnPopulateMesh(VertexHelper toFill) {
if (type == Type.Sliced && hasBorder) {
GenerateInversedSlicedSprite(toFill);
} else {
base.OnPopulateMesh(toFill);
}
}
void GenerateInversedSlicedSprite(VertexHelper toFill) {
var activeSprite = overrideSprite;
Vector4 outer = DataUtility.GetOuterUV(activeSprite);
Vector4 inner = DataUtility.GetInnerUV(activeSprite);
Vector4 padding = DataUtility.GetPadding(activeSprite);
Vector4 border = activeSprite.border;
Rect rect = GetPixelAdjustedRect();
Vector2 spriteSize = activeSprite.rect.size;
spriteSize.x = spriteSize.x - border.x - border.z;
spriteSize.y = spriteSize.y - border.y - border.w;
spriteSize = GetAdjustedSpriteSize(spriteSize / multipliedPixelsPerUnit, rect);
padding = padding / multipliedPixelsPerUnit;
s_VertCache[0] = new Vector2(padding.x, padding.y);
s_VertCache[3] = new Vector2(rect.width - padding.z, rect.height - padding.w);
var slicedWidth = rect.width - padding.x - padding.z - spriteSize.x;
var slicedHeight = rect.height - padding.y - padding.w - spriteSize.y;
var borderWidth = border.x + border.z;
if (borderWidth <= 0f) {
s_VertCache[1].x = s_VertCache[0].x;
s_VertCache[2].x = s_VertCache[3].x;
} else {
s_VertCache[1].x = slicedWidth * border.x / borderWidth + padding.x;
s_VertCache[2].x = rect.width - slicedWidth * border.z / borderWidth - padding.z;
}
var borderHeight = border.y + border.w;
if (borderHeight <= 0f) {
s_VertCache[1].y = s_VertCache[0].y;
s_VertCache[2].y = s_VertCache[3].y;
} else {
s_VertCache[1].y = slicedHeight * border.y / borderHeight + padding.y;
s_VertCache[2].y = rect.height - slicedHeight * border.w / borderHeight - padding.w;
}
for (int i = 0; i < 4; ++i) {
s_VertCache[i].x += rect.x;
s_VertCache[i].y += rect.y;
}
s_UVCache[0] = new Vector2(outer.x, outer.y);
s_UVCache[1] = new Vector2(inner.x, inner.y);
s_UVCache[2] = new Vector2(inner.z, inner.w);
s_UVCache[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 (!fillCenter && x == 1 && y == 1) continue;
int y2 = y + 1;
AddQuad(toFill,
new Vector2(s_VertCache[x].x, s_VertCache[y].y),
new Vector2(s_VertCache[x2].x, s_VertCache[y2].y),
color,
new Vector2(s_UVCache[x].x, s_UVCache[y].y),
new Vector2(s_UVCache[x2].x, s_UVCache[y2].y));
}
}
}
static void AddQuad(VertexHelper vertexHelper, in Vector2 posMin, in Vector2 posMax, in Color32 color, in Vector2 uvMin, in Vector2 uvMax) {
int startIndex = vertexHelper.currentVertCount;
vertexHelper.AddVert(new Vector3(posMin.x, posMin.y, 0), color, new Vector2(uvMin.x, uvMin.y));
vertexHelper.AddVert(new Vector3(posMin.x, posMax.y, 0), color, new Vector2(uvMin.x, uvMax.y));
vertexHelper.AddVert(new Vector3(posMax.x, posMax.y, 0), color, new Vector2(uvMax.x, uvMax.y));
vertexHelper.AddVert(new Vector3(posMax.x, posMin.y, 0), color, new Vector2(uvMax.x, uvMin.y));
vertexHelper.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
vertexHelper.AddTriangle(startIndex + 2, startIndex + 3, startIndex);
}
private Vector2 GetAdjustedSpriteSize(Vector2 spriteSize, in Rect adjustedRect) {
Rect originalRect = rectTransform.rect;
for (int axis = 0; axis <= 1; axis++) {
var size = adjustedRect.size[axis];
// The adjusted rect (adjusted for pixel correctness)
if (originalRect.size[axis] != 0) {
var borderScaleRatio = size / originalRect.size[axis];
spriteSize[axis] *= borderScaleRatio;
}
// If the rect is smaller than the sprite, then there's not room for the borders.
if (size < spriteSize[axis] && spriteSize[axis] != 0) {
spriteSize[axis] = size;
}
}
return spriteSize;
}
#if UNITY_EDITOR
protected override void Reset() {
base.Reset();
type = Type.Sliced;
}
#endif
#if UNITY_EDITOR
static UnityEditor.MonoScript _script;
[UnityEditor.MenuItem("CONTEXT/Image/Replace as Inversed Sliced Image")]
static void ReplaceFromBuiltInImage(UnityEditor.MenuCommand command) {
if (!_script) {
var tmpGO = new GameObject("tempOBJ");
var inst = tmpGO.AddComponent<InversedSlicedImage>();
_script = UnityEditor.MonoScript.FromMonoBehaviour(inst);
DestroyImmediate(tmpGO);
}
var go = ((Component)command.context).gameObject;
UnityEditor.Undo.RegisterCompleteObjectUndo(go, string.Format("Replace Image as Inversed Sliced Image in {0}", go.name));
UnityEditor.SerializedObject so = new UnityEditor.SerializedObject(command.context);
UnityEditor.SerializedProperty scriptProperty = so.FindProperty("m_Script");
so.Update();
scriptProperty.objectReferenceValue = _script;
so.ApplyModifiedProperties();
}
#endif
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment