using UnityEngine;
using UnityEngine.UI;
///
/// 计算 Sprite 的非透明可见区域(基于 sprite.rect),
/// 将其中心与 Image 矩形中心对齐,并把偏移应用到「父物体」的 anchoredPosition.y。
/// 同时缓存上下左右边界,避免重复扫描,并提供 UI 尺寸下的边界/宽度。
///
[DisallowMultipleComponent]
public class ImageAlphaBounds : MonoBehaviour
{
[Range(0f, 1f)]
public float alphaThreshold = 0.1f;
private Sprite cachedSprite;
private int cachedTop;
private int cachedBottom;
private int cachedLeft;
private int cachedRight;
private bool cachedValid;
private void EnsureBounds(Image img)
{
Sprite sp = img.sprite;
if (cachedValid && cachedSprite == sp) return;
cachedSprite = sp;
cachedValid = false;
if (sp == null) return;
Texture2D tex = sp.texture;
if (tex == null || !tex.isReadable)
{
Debug.LogWarning($"{name}: Texture not readable. Enable Read/Write in import settings.");
return;
}
Rect r = sp.rect;
int sx = Mathf.RoundToInt(r.x);
int sy = Mathf.RoundToInt(r.y);
int sw = Mathf.RoundToInt(r.width);
int sh = Mathf.RoundToInt(r.height);
Color[] pixels = tex.GetPixels(sx, sy, sw, sh);
int top = -1, bottom = -1, left = -1, right = -1;
for (int y = 0; y < sh; y++)
{
int rowStart = y * sw;
for (int x = 0; x < sw; x++)
{
if (pixels[rowStart + x].a > alphaThreshold)
{
if (top == -1) top = y;
bottom = y;
if (left == -1 || x < left) left = x;
if (right == -1 || x > right) right = x;
}
}
}
if (top == -1 || bottom == -1)
{
cachedTop = cachedBottom = cachedLeft = cachedRight = -1;
}
else
{
cachedTop = top;
cachedBottom = bottom;
cachedLeft = left;
cachedRight = right;
}
cachedValid = true;
}
public void AdjustY()
{
Image img = GetComponent();
if (img == null || img.sprite == null) return;
EnsureBounds(img);
if (!cachedValid || cachedBottom < cachedTop) return;
RectTransform imgRT = img.rectTransform;
float fullHeightUI = imgRT.sizeDelta.y;
float spriteHeightPx = img.sprite.rect.height;
float scaleY = fullHeightUI / spriteHeightPx;
float centerVisibleFromTopPx = (cachedTop + cachedBottom + 1) * 0.5f;
float centerVisibleFromTopUI = centerVisibleFromTopPx * scaleY;
float imageCenterFromTopUI = fullHeightUI * 0.5f;
float deltaY = centerVisibleFromTopUI - imageCenterFromTopUI;
RectTransform parentRT = img.transform.parent as RectTransform;
if (parentRT != null)
{
Vector2 p = parentRT.anchoredPosition;
float parentScaleY = parentRT.localScale.y;
parentRT.anchoredPosition = new Vector2(p.x, -deltaY * parentScaleY);
}
}
public float GetOpaqueHeight()
{
if (!cachedValid || cachedBottom < cachedTop) return 0f;
return cachedBottom - cachedTop + 1;
}
public float GetOpaqueWidth()
{
if (!cachedValid || cachedRight < cachedLeft) return 0f;
return cachedRight - cachedLeft + 1;
}
public float GetOpaqueLeftUI()
{
Image img = GetComponent();
if (img == null || !cachedValid || cachedRight < cachedLeft) return 0f;
RectTransform rt = img.rectTransform;
float spriteWidthPx = img.sprite.rect.width;
float scaleX = rt.sizeDelta.x / spriteWidthPx;
return cachedLeft * scaleX;
}
public float GetOpaqueRightUI()
{
Image img = GetComponent();
if (img == null || !cachedValid || cachedRight < cachedLeft) return 0f;
RectTransform rt = img.rectTransform;
float spriteWidthPx = img.sprite.rect.width;
float scaleX = rt.sizeDelta.x / spriteWidthPx;
return cachedRight * scaleX;
}
///
/// 返回不透明宽度的 UI 尺寸(换算自 sprite.rect 像素宽度)
///
public float GetOpaqueWidthUI()
{
Image img = GetComponent();
if (img == null || !cachedValid || cachedRight < cachedLeft) return 0f;
RectTransform rt = img.rectTransform;
float spriteWidthPx = img.sprite.rect.width;
float scaleX = rt.sizeDelta.x / spriteWidthPx;
return (cachedRight - cachedLeft + 1) * scaleX;
}
}