Snaparazzi/Assets/MPUIKit/Runtime/Scripts/Utility/MPImageHelper.cs

398 lines
16 KiB
C#
Raw Permalink Normal View History

2024-02-29 20:33:27 +00:00
using System.Text;
namespace UnityEngine.UI.MPUIKIT {
public static class MPImageHelper {
private static readonly Vector3[] SXy = new Vector3[4];
private static readonly Vector3[] SUv = new Vector3[4];
public static void GenerateSimpleSprite(VertexHelper vh, bool preserveAspect, Canvas canvas,
RectTransform rectTransform, Sprite activeSprite, Color32 color, float falloffDistance) {
vh.Clear();
Vector4 v = GetDrawingDimensions(preserveAspect, activeSprite, canvas, rectTransform);
Vector4 uv = (activeSprite != null)
? Sprites.DataUtility.GetOuterUV(activeSprite)
: new Vector4(0, 0, 1, 1);
Color32 color32 = color;
vh.Clear();
Vector3[] pos = {
new Vector3(v.x, v.y),
new Vector3(v.x, v.w),
new Vector3(v.z, v.w),
new Vector3(v.z, v.y),
};
Vector2[] uvs = {
new Vector2(uv.x, uv.y),
new Vector2(uv.x, uv.w),
new Vector2(uv.z, uv.w),
new Vector2(uv.z, uv.y),
};
Vector2[] uv1s =
{
new Vector2(0, 0),
new Vector2(0, 1),
new Vector2(1, 1),
new Vector2(1, 0),
};
Vector2 size = new Vector2(v.z - v.x, v.w - v.y);
vh.AddVert(pos[0], color32, uvs[0], uv1s[0], size, Vector2.zero, Vector3.zero, Vector4.zero);
vh.AddVert(pos[1], color32, uvs[1], uv1s[1], size, Vector2.zero, Vector3.zero, Vector4.zero);
vh.AddVert(pos[2], color32, uvs[2], uv1s[2], size, Vector2.zero, Vector3.zero, Vector4.zero);
vh.AddVert(pos[3], color32, uvs[3], uv1s[3], size, Vector2.zero, Vector3.zero, Vector4.zero);
vh.AddTriangle(0, 1, 2);
vh.AddTriangle(2, 3, 0);
}
public static void GenerateFilledSprite(VertexHelper toFill, bool preserveAspect, Canvas canvas,
RectTransform rectTransform, Sprite activeSprite, Color32 color, Image.FillMethod fillMethod,
float fillAmount, int fillOrigin, bool fillClockwise, float falloffDistance) {
toFill.Clear();
if (fillAmount < 0.001f)
return;
Vector4 v = GetDrawingDimensions(preserveAspect, activeSprite, canvas, rectTransform);
Vector2 size = new Vector2(v.z - v.x, v.w - v.y);
Vector4 outer = activeSprite != null
? Sprites.DataUtility.GetOuterUV(activeSprite)
: new Vector4(0, 0, 1, 1);
UIVertex uiv = UIVertex.simpleVert;
uiv.color = color;
float tx0 = outer.x;
float ty0 = outer.y;
float tx1 = outer.z;
float ty1 = outer.w;
// Horizontal and vertical filled sprites are simple -- just end the Image prematurely
if (fillMethod == Image.FillMethod.Horizontal || fillMethod == Image.FillMethod.Vertical) {
if (fillMethod == Image.FillMethod.Horizontal) {
float fill = (tx1 - tx0) * fillAmount;
if (fillOrigin == 1) {
v.x = v.z - (v.z - v.x) * fillAmount;
tx0 = tx1 - fill;
}
else {
v.z = v.x + (v.z - v.x) * fillAmount;
tx1 = tx0 + fill;
}
}
else if (fillMethod == Image.FillMethod.Vertical) {
float fill = (ty1 - ty0) * fillAmount;
if (fillOrigin == 1) {
v.y = v.w - (v.w - v.y) * fillAmount;
ty0 = ty1 - fill;
}
else {
v.w = v.y + (v.w - v.y) * fillAmount;
ty1 = ty0 + fill;
}
}
}
SXy[0] = new Vector2(v.x, v.y);
SXy[1] = new Vector2(v.x, v.w);
SXy[2] = new Vector2(v.z, v.w);
SXy[3] = new Vector2(v.z, v.y);
SUv[0] = new Vector2(tx0, ty0);
SUv[1] = new Vector2(tx0, ty1);
SUv[2] = new Vector2(tx1, ty1);
SUv[3] = new Vector2(tx1, ty0);
{
if (fillAmount < 1f && fillMethod != Image.FillMethod.Horizontal &&
fillMethod != Image.FillMethod.Vertical) {
if (fillMethod == Image.FillMethod.Radial90) {
if (RadialCut(SXy, SUv, fillAmount, fillClockwise, fillOrigin))
AddQuad(toFill, SXy, color, SUv, size);
}
else if (fillMethod == Image.FillMethod.Radial180) {
for (int side = 0; side < 2; ++side) {
float fx0, fx1, fy0, fy1;
int even = fillOrigin > 1 ? 1 : 0;
if (fillOrigin == 0 || fillOrigin == 2) {
fy0 = 0f;
fy1 = 1f;
if (side == even) {
fx0 = 0f;
fx1 = 0.5f;
}
else {
fx0 = 0.5f;
fx1 = 1f;
}
}
else {
fx0 = 0f;
fx1 = 1f;
if (side == even) {
fy0 = 0.5f;
fy1 = 1f;
}
else {
fy0 = 0f;
fy1 = 0.5f;
}
}
SXy[0].x = Mathf.Lerp(v.x, v.z, fx0);
SXy[1].x = SXy[0].x;
SXy[2].x = Mathf.Lerp(v.x, v.z, fx1);
SXy[3].x = SXy[2].x;
SXy[0].y = Mathf.Lerp(v.y, v.w, fy0);
SXy[1].y = Mathf.Lerp(v.y, v.w, fy1);
SXy[2].y = SXy[1].y;
SXy[3].y = SXy[0].y;
SUv[0].x = Mathf.Lerp(tx0, tx1, fx0);
SUv[1].x = SUv[0].x;
SUv[2].x = Mathf.Lerp(tx0, tx1, fx1);
SUv[3].x = SUv[2].x;
SUv[0].y = Mathf.Lerp(ty0, ty1, fy0);
SUv[1].y = Mathf.Lerp(ty0, ty1, fy1);
SUv[2].y = SUv[1].y;
SUv[3].y = SUv[0].y;
float val = fillClockwise ? fillAmount * 2f - side : fillAmount * 2f - (1 - side);
if (RadialCut(SXy, SUv, Mathf.Clamp01(val), fillClockwise,
((side + fillOrigin + 3) % 4))) {
AddQuad(toFill, SXy, color, SUv, size);
}
}
}
else if (fillMethod == Image.FillMethod.Radial360) {
for (int corner = 0; corner < 4; ++corner) {
float fx0, fx1, fy0, fy1;
if (corner < 2) {
fx0 = 0f;
fx1 = 0.5f;
}
else {
fx0 = 0.5f;
fx1 = 1f;
}
if (corner == 0 || corner == 3) {
fy0 = 0f;
fy1 = 0.5f;
}
else {
fy0 = 0.5f;
fy1 = 1f;
}
SXy[0].x = Mathf.Lerp(v.x, v.z, fx0);
SXy[1].x = SXy[0].x;
SXy[2].x = Mathf.Lerp(v.x, v.z, fx1);
SXy[3].x = SXy[2].x;
SXy[0].y = Mathf.Lerp(v.y, v.w, fy0);
SXy[1].y = Mathf.Lerp(v.y, v.w, fy1);
SXy[2].y = SXy[1].y;
SXy[3].y = SXy[0].y;
SUv[0].x = Mathf.Lerp(tx0, tx1, fx0);
SUv[1].x = SUv[0].x;
SUv[2].x = Mathf.Lerp(tx0, tx1, fx1);
SUv[3].x = SUv[2].x;
SUv[0].y = Mathf.Lerp(ty0, ty1, fy0);
SUv[1].y = Mathf.Lerp(ty0, ty1, fy1);
SUv[2].y = SUv[1].y;
SUv[3].y = SUv[0].y;
float val = fillClockwise
? fillAmount * 4f - ((corner + fillOrigin) % 4)
: fillAmount * 4f - (3 - ((corner + fillOrigin) % 4));
if (RadialCut(SXy, SUv, Mathf.Clamp01(val), fillClockwise, ((corner + 2) % 4)))
AddQuad(toFill, SXy, color, SUv, size);
}
}
}
else {
AddQuad(toFill, SXy, color, SUv, size);
}
}
}
private static void AddQuad(VertexHelper vertexHelper, Vector3[] quadPositions, Color32 color,
Vector3[] quadUVs, Vector2 size) {
int startIndex = vertexHelper.currentVertCount;
StringBuilder sr = new StringBuilder();
for (int i = 0; i < 4; ++i) {
vertexHelper.AddVert(quadPositions[i], color, quadUVs[i], quadUVs[i], size, Vector2.zero,
Vector3.zero, Vector4.zero);
sr.AppendLine($"Pos: {quadPositions[i]}, uv: {quadUVs[i]}");
}
vertexHelper.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
vertexHelper.AddTriangle(startIndex + 2, startIndex + 3, startIndex);
}
private static Vector4 GetDrawingDimensions(bool shouldPreserveAspect, Sprite activeSprite, Canvas canvas,
RectTransform rectTransform) {
var padding = activeSprite == null ? Vector4.zero : Sprites.DataUtility.GetPadding(activeSprite);
var size = activeSprite == null
? new Vector2(rectTransform.rect.width, rectTransform.rect.height)
: new Vector2(activeSprite.rect.width, activeSprite.rect.height);
if (size.x <= 0) size.x = 1;
if (size.y <= 0) size.y = 1;
Rect r = GetPixelAdjustedRect(canvas, rectTransform);
//Debug.Log(string.Format("r:{2}, size:{0}, padding:{1}", size, padding, r));
int spriteW = Mathf.RoundToInt(size.x);
int spriteH = Mathf.RoundToInt(size.y);
Vector4 v = new Vector4(
padding.x / spriteW,
padding.y / spriteH,
(spriteW - padding.z) / spriteW,
(spriteH - padding.w) / spriteH);
if (shouldPreserveAspect && size.sqrMagnitude > 0.0f) {
PreserveSpriteAspectRatio(ref r, rectTransform, size);
}
v = new Vector4(
r.x + r.width * v.x,
r.y + r.height * v.y,
r.x + r.width * v.z,
r.y + r.height * v.w
);
return v;
}
public static void PreserveSpriteAspectRatio(ref Rect rect, RectTransform rectTransform, Vector2 spriteSize) {
float spriteRatio = spriteSize.x / spriteSize.y;
float rectRatio = rect.width / rect.height;
if (spriteRatio > rectRatio) {
float oldHeight = rect.height;
rect.height = rect.width * (1.0f / spriteRatio);
rect.y += (oldHeight - rect.height) * rectTransform.pivot.y;
}
else {
float oldWidth = rect.width;
rect.width = rect.height * spriteRatio;
rect.x += (oldWidth - rect.width) * rectTransform.pivot.x;
}
}
private static Rect GetPixelAdjustedRect(Canvas canvas, RectTransform rectTransform) {
if (!canvas || canvas.renderMode == RenderMode.WorldSpace || canvas.scaleFactor == 0.0f ||
!canvas.pixelPerfect) {
return rectTransform.rect;
}
return RectTransformUtility.PixelAdjustRect(rectTransform, canvas);
}
private static bool RadialCut(Vector3[] xy, Vector3[] uv, float fill, bool invert, int corner) {
// Nothing to fill
if (fill < 0.001f) return false;
// Even corners invert the fill direction
if ((corner & 1) == 1) invert = !invert;
// Nothing to adjust
if (!invert && fill > 0.999f) return true;
// Convert 0-1 value into 0 to 90 degrees angle in radians
float angle = Mathf.Clamp01(fill);
if (invert) angle = 1f - angle;
angle *= 90f * Mathf.Deg2Rad;
// Calculate the effective X and Y factors
float cos = Mathf.Cos(angle);
float sin = Mathf.Sin(angle);
RadialCut(xy, cos, sin, invert, corner);
RadialCut(uv, cos, sin, invert, corner);
return true;
}
private static void RadialCut(Vector3[] xy, float cos, float sin, bool invert, int corner) {
int i0 = corner;
int i1 = ((corner + 1) % 4);
int i2 = ((corner + 2) % 4);
int i3 = ((corner + 3) % 4);
if ((corner & 1) == 1) {
if (sin > cos) {
cos /= sin;
sin = 1f;
if (invert) {
xy[i1].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos);
xy[i2].x = xy[i1].x;
}
}
else if (cos > sin) {
sin /= cos;
cos = 1f;
if (!invert) {
xy[i2].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin);
xy[i3].y = xy[i2].y;
}
}
else {
cos = 1f;
sin = 1f;
}
if (!invert) xy[i3].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos);
else xy[i1].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin);
}
else {
if (cos > sin) {
sin /= cos;
cos = 1f;
if (!invert) {
xy[i1].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin);
xy[i2].y = xy[i1].y;
}
}
else if (sin > cos) {
cos /= sin;
sin = 1f;
if (invert) {
xy[i2].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos);
xy[i3].x = xy[i2].x;
}
}
else {
cos = 1f;
sin = 1f;
}
if (invert) xy[i3].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin);
else xy[i1].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos);
}
}
}
}