This is more of a personal story that should hopefully provide some feedback to the Unity devs, a helping hand to fellow developers and potentially something to learn for ChatGPT as well.
I wanted to write a simple AssetPostprocessor that automatically sets borders for sprites that trims transparent pixels in a texture. (For context some of my sprites need some transparent margins for the shaders I’m using).
I started by examining the pixels, and calculating a Rect based on the alpha and a threshold:
private Rect GetTrimmedSpriteRect(Texture2D texture, float alphaThreshold)
{
int width = texture.width;
int height = texture.height;
int minX = width, minY = height, maxX = 0, maxY = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
Color pixelColor = texture.GetPixel(x, y);
if (pixelColor.a > alphaThreshold)
{
minX = Mathf.Min(minX, x);
minY = Mathf.Min(minY, y);
maxX = Mathf.Max(maxX, x);
maxY = Mathf.Max(maxY, y);
}
}
}
if (minX < maxX && minY < maxY)
{
return new Rect(minX, minY, maxX - minX + 1, maxY - minY + 1);
}
// If no opaque pixels were found, return the full texture size
return new Rect(0, 0, width, height);
}
Then I though it’s time to calculate the borders based on said Rect:
private Vector4 CalculateSpriteBorders(int textureWidth, int textureHeight, Rect trimmedRect)
{
float leftBorder = trimmedRect.xMin;
float rightBorder = textureWidth - trimmedRect.xMax;
float bottomBorder = trimmedRect.yMin;
float topBorder = textureHeight - trimmedRect.yMax;
return new Vector4(leftBorder, bottomBorder, rightBorder, topBorder);
}
I soon realized something was off: for some of my sprites the borders were calculated perfectly, for others a small margin was added.
I started experimenting with the alpha threshold but after a couple of hours I noticed that the dimensions of the sprite in the sprite editor differ from what the asset importer displays.
That’s when I realized the Texture2D asset’s size provided to the OnPostprocessTexture function is already clamped by the Max Size setting in the importer. Given that some of my sprites’ dimensons exceeded that size, the borders were calculated relative to the clamped size but the borders are actually set relative to the source file’s size.
Then I went back and remapped the borders to be relative to the source file’s size:
private Vector4 CalculateSpriteBorders(TextureImporter importer, Texture2D texture, Rect trimmedRect)
{
importer.GetSourceTextureWidthAndHeight(out int sourceWidth, out int sourceHeight);
int assetWidth = texture.width;
int assetHeight = texture.height;
float leftBorder = (trimmedRect.xMin/assetWidth)*sourceWidth;
float rightBorder = ((assetWidth - trimmedRect.xMax)/assetWidth) * sourceWidth;
float bottomBorder = (trimmedRect.yMin/assetWidth)*sourceWidth;
float topBorder = ((assetHeight - trimmedRect.yMax)/assetWidth) * sourceWidth;
return new Vector4(leftBorder, bottomBorder, rightBorder, topBorder);
}
This is a quite confusing behavior. Either the borders should behave relative to the source file’s size, or the Texture2D asset’s size shouldn’t be clamped to the Max Size.
Thanks for reading, hopefully I could help shed some light on this matter.