Texture2D.ReadPixels() and EncodeToPNG() not working properly (windows, android)

Hi,

Can someone help me with a boring problem?
I’m trying to generate a transparent .png based on a UI text present in a canvas (TextMeshPro beeing used to generate the text). For that, I’m currently using a code to capture the current frame of the scene Camera using the ReadPixels method. Then I save the resulted image to a png file using EncodeToPNG.

But those functions give 2 problems :

  1. On windows the generated .png creates strange grey outlines or extra pixels on the text border,
    between RGB channel and alpha channel. I don’t know where it’s coming from …

  2. On Android the generated .png shows strange grey-black abberations arround letters.

I looked for several ways to fix this (changing shader, texture format, color setting) but none worked. Does someone know why I have those strange results?

Thanks in advance.


TextureFormat: ARGB32

Text Shader : TextMeshPro/Distance Field

Android Build setting :

  • Color space : Gamma

  • Auto graphic API : enabled

  • Color gamut : sRGB

Results :

Android :
alt text

Windows :

And below the code I’m using(sorry if I don’t post it correctly) :

//+-------+
//|Capture|
//+-------+
txtgen_camera.clearFlags = CameraClearFlags.Depth; //Remove Skybox and allows transparency sorting

RenderTexture rt = new RenderTexture(resWidth, resHeight, 24);
txtgen_camera.targetTexture = rt;

//Texture parameters : Format and Resolution
Texture2D screenShot = new Texture2D(resWidth, resHeight, TextureFormat.ARGB32, true);
//Read pixels then release cam
txtgen_camera.Render();
RenderTexture.active = rt;
screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);
txtgen_camera.targetTexture = null;
RenderTexture.active = null;
Destroy(rt);

//Generate Image File
byte[] bytes;
bytes = screenShot.EncodeToPNG();
bytes = B83.Image.PNGTools.ChangePPI(bytes, 100, 100); //can be done in photoshop instead of Unity

Destroy(screenShot);

//+----------+
//|Image name|
//+----------+
string time = System.DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
string name_file = "TestTxtGen" + "_" + time + extension;

//+----------+
//|Save image|
//-----------+
string local_path = Path.Combine(Application.persistentDataPath, "Texts");
string file_path = Path.Combine(local_path, name_file);

if (!System.IO.Directory.Exists(local_path))
{
    Directory.CreateDirectory(local_path);
}

File.WriteAllBytes(file_path, bytes);

Well, you don’t clear the color buffer of your render target (in your case your render texture) at all. So you just assume that it is somehow filled with a fully transparent color. When you not actively clear a render target, it still contains whatever was previously in that memory space. There are two things that might work:

First try just setting the camera’s clear color to Color.clear (which is just (0,0,0,0)) and set the clear mode to SolidColor.

If that doesn’t work you can try manually clear the rendertexture like this before you render the camera:

RenderTexture.active = rt;
GL.Clear(true, true, Color.clear);

@Bunny83 Thanks a lot for the fast answer !

Your solution solved the problem on Android.

I just replaced this:

txtgen_camera.clearFlags = CameraClearFlags.Depth;

by this:

txtgen_camera.clearFlags = CameraClearFlags.Color;
txtgen_camera.backgroundColor = new Color(0, 0, 0, 0);

And for the strange greyed outlines I found this solution (it may not be the best one) :

screenShot = RemoveOutlines(screenShot);

public Texture2D RemoveOutlines(Texture2D texture)
{
    for (int x = 0; x < texture.width; x++)
    {
        for (int y = 0; y < texture.height; y++)
        {
            if (texture.GetPixel(x, y).a != 0)
                texture.SetPixel(x, y, textColor);
        }
    }
    return texture;
}

Result :
alt text