The Texture2D equivalent of Alpha is Transparency for LoadImage png

Hi all,

With pngs imported into my Unity project, the standard ‘Texture’ type has a check box with ‘Alpha Is Transparency’. If I check this, my texture has a nice transparent back ground, as opposed to black. All is lovely.

If I don’t have the png imported though, and instead load it with Texture2D.LoadImage.

I only have the black background option.

Is there a way to make the loaded png behave the same as if it were imported.

Thanks

The images below show the main character with the Texture2D.LoadImage, vs the png imported in Unity with ‘Alpha is Transparency’ set. It’s very minor differences, but i’m keen not to have the slight seam around the top of the leg, as well as the seam where the arm connects to the body, and the small seam at the top of the chin.

2346903--158842--Screen Shot 2015-10-20 at 13.19.32.png 2346903--158843--Screen Shot 2015-10-20 at 13.21.08.png

1 Like

Hi. The ‘Alpha Is Transparency’ is applied via editor only code so it is not available to Texture2D.LoadImage.
The code applies an alpha dilation filter, so its something you could do yourself.
Take a look at this and see if it helps first Unity - Manual: Applying Edge Padding to Alpha Textures

1 Like

Hi Karl, thanks very much for your reply.

Sadly I don’t think Edge Padding is something that will help. Unless i’m mistaken, pngs don’t have an Alpha channel (just alpha transparency), so I can’t dilate the image and then use the alpha channel. To show only the bits of the png I want?

Not sure if it will help but below is how my player is rendered on quads/triangles, along with the sprite sheet used.

Hmm. PNGs can contain RGBA, that’s 4 channels one of which is alpha so it should be fine. Best thing to do is try the photoshop advice, save it as a PNG and see how it looks.
I am fairly certain this will solve your problem :wink:

1 Like

Hi! Bumping this. I have way too many PNG files to do the Photoshop trick manually for each one (> 1000 files), and I’m in the editor, so I have access to any code I want.

Is there a way to apply the alpha dilation filter programmatically @karl_jones ?

If your in the editor then you can just set it on the texture importer. Unity - Scripting API: TextureImporter.alphaIsTransparency

No, because the texture isn’t an asset, it never gets imported. It’s created from an in-memory byte array via LoadImage.

Hmm. I don’t think we expose it any other way. You could save the texture as an asset, import it, grab the converted version and then delete the asset. That can all be done through script.

I’m not sure if this is relevant, but I hope it is :slight_smile:
I was having trouble importing PNG files with corrupted alpha channels, this is how I solved it:

byte[] fileData = File.ReadAllBytes(filePath);

Texture2D tex = new Texture2D(2, 2, TextureFormat.RGBA32, false);
tex.LoadImage(fileData);

Texture2D texPNG = new Texture2D(tex.width, tex.height, TextureFormat.RGBA32, false);
texPNG.SetPixels(tex.GetPixels());
texPNG.Apply();
1 Like

Well, thanks a lot! This worked for me. My usecase was: I wanted user to add a texture (amazon logo ‘a’ for isntance) through selecting the PNG file. The problem was, the logo was getting a cyan tint or sometimes red instead of black color if I set alpha channel traversing through all pixels.

Just tried this and it worked!

How did you apply it? Did you transformed it into a sprite and then assigned the sprite to the sprite renderer? thanks

Hi, sorry for bumping this, I’m trying to do something similar, taking a screenshot and saving it, and the screenshot I want to take is for a single character, in other words I need everything else to be transparent, here’s the code:

int width = Screen.width;
int height = Screen.height;
Texture2D screenshotTexture = new Texture2D(width,
height, TextureFormat.RGBA32, false);
Rect rect = new Rect(0, 0, width, height);
screenshotTexture.ReadPixels(rect, 0, 0);
screenshotTexture.Apply();

byte[] byteArray = screenshotTexture.EncodeToPNG();
System.IO.File.WriteAllBytes(_path, byteArray);

It does take screenshot but not with transparency even if the texture format is RGBA32, so I’m guessing it has something to do with Texture2D.alphaIsTransparency, since the result replaces alpha with black color, am I missing something?

Thanks,

Hi!
I found the following solution for the ‘Alpha Is Transparency’ option:

public static void AlphaIsTransparency(this Texture2D texture)
{
    Color[] pixels = texture.GetPixels();
    for (int i = 0; i < pixels.Length; i++)
    {
        if(Mathf.Approximately(pixels[i].a, 0))
            pixels[i] = new Color();
    }
    texture.SetPixels(pixels);
    texture.Apply();
}

Usage:

var texture = new Texture2D(
    512, 512,
    TextureFormat.RGBA32, false
);
texture.LoadImage(File.ReadAllBytes(fileFullName));
texture.AlphaIsTransparency();

In my case, it fixes filtering artifacts around transparent areas of externally imported PNG’s like ‘Alpha Is Transparency’ does.

I’ve found this to work for a little bit. But then when the texture is set to bilinear filtering mode it breaks!
I spent a while researching this and learned the AlphaIsTransprency basically makes sure that no pixel is without color, even if the alpha is 0.

The problem is bilinear will use pixels around it to make a new pixel. So new Color() always has black. and when you multiply black with a brighter pixel, lets say white, it will become gray. Now you are back to having the same weird fringe outlining around your bright pixels but not your black pixels.

So I came up with an idea to propagate the color of non 0 alphas onto 0 alphas. Note: This probably has issues and need improvement but I guess it works for me.

 public static void AlphaIsTransparency(this Texture2D texture)
 {
     PropagateColors(texture);
     texture.Apply();

     void PropagateColors(Texture2D texture)
     {
         int maxTransparentCount = 4;
         Color[] pixels = texture.GetPixels();
         int width = texture.width;
         int height = texture.height;
         Color currentColor = Color.clear;
         int transparentCount = 0;
         int index = 0;

         void CheckAndSet()
         {
             if (pixels[index].a > 0) // If current pixel has alpha > 0
             {
                 currentColor = pixels[index]; // Track current color
                 currentColor.a = 0; // Set alpha to 0
                 transparentCount = 0; // Reset the transparent counter
             }
             else // If current pixel is transparent
             {
                 if (transparentCount < maxTransparentCount)
                 {
                     pixels[index] = currentColor; // Apply current color
                     transparentCount++; // Increment the transparent counter
                 }
                 else
                 {
                     currentColor = Color.clear; //if it passes max count lets just clear it and reset
                     transparentCount = 0;

                 }
             }

         }

         // Top to Bottom
         for (int x = 0; x < width; x++)
         {
             // Counter for consecutive transparent pixels
             currentColor = Color.clear;
             transparentCount = 0;
             for (int y = 0; y < height; y++)
             {
                 index = y * width + x;
                 CheckAndSet();
             }
         }

         // Bottom to Top
         for (int x = 0; x < width; x++)
         {
             currentColor = Color.clear;
             transparentCount = 0;

             for (int y = height - 1; y >= 0; y--)
             {
                 index = y * width + x;
                 CheckAndSet();
             }
         }

         // Left to Right
         for (int y = 0; y < height; y++)
         {
             currentColor = Color.clear;
             transparentCount = 0;

             for (int x = 0; x < width; x++)
             {
                 index = y * width + x;
                 CheckAndSet();
             }
         }

         // Right to Left
         for (int y = 0; y < height; y++)
         {
             currentColor = Color.clear;
             transparentCount = 0;

             for (int x = width - 1; x >= 0; x--)
             {
                 index = y * width + x;
                 CheckAndSet();
             }
         }

         // Apply modified pixels back to the texture
         texture.SetPixels(pixels);
     }
 }