Search Unity

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

Discussion in 'General Discussion' started by SynSeia, Feb 13, 2020.

  1. SynSeia

    SynSeia

    Joined:
    Nov 17, 2016
    Posts:
    5
    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 borders, between RGB channel and alpha channel. I don't know where it's coming from ...

    2. On Android the generated .png shows strange grey-glack 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 :

    Windows :


    Android :


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

    Code (CSharp):
    1. //+-------+
    2. //|Capture|
    3. //+-------+
    4. txtgen_camera.clearFlags = CameraClearFlags.Depth; //Remove Skybox and allows transparency sorting
    5.  
    6. RenderTexture rt = new RenderTexture(resWidth, resHeight, 24);
    7. txtgen_camera.targetTexture = rt;
    8.  
    9. //Texture parameters : Format and Resolution
    10. Texture2D screenShot = new Texture2D(resWidth, resHeight, TextureFormat.ARGB32, true);
    11. //Read pixels then release cam
    12. txtgen_camera.Render();
    13. RenderTexture.active = rt;
    14. screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);
    15. txtgen_camera.targetTexture = null;
    16. RenderTexture.active = null;
    17. Destroy(rt);
    18.  
    19. //Generate Image File
    20. byte[] bytes;
    21. bytes = screenShot.EncodeToPNG();
    22. bytes = B83.Image.PNGTools.ChangePPI(bytes, 100, 100); //can be done in photoshop instead of Unity
    23.  
    24. Destroy(screenShot);
    25.  
    26. //+----------+
    27. //|Image name|
    28. //+----------+
    29. string time = System.DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
    30. string name_file = "TestTxtGen" + "_" + time + extension;
    31.  
    32. //+----------+
    33. //|Save image|
    34. //-----------+
    35. string local_path = Path.Combine(Application.persistentDataPath, "Texts");
    36. string file_path = Path.Combine(local_path, name_file);
    37.  
    38. if (!System.IO.Directory.Exists(local_path))
    39. {
    40.     Directory.CreateDirectory(local_path);
    41. }
    42.  
    43. File.WriteAllBytes(file_path, bytes);
     
  2. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,572
    Have you tried rendering it to RGB24 format? Because png supports alpha channel, and png with alpha channel can be displayed in interesting ways, depending on viewer. Additionally, B83.Image.PNGTools.ChangePPI is not a part of unity API. Try disabling that and see if it changes anything.
     
  3. SynSeia

    SynSeia

    Joined:
    Nov 17, 2016
    Posts:
    5
    Ok. Here is the solution for the Android problem:

    I just replaced this:
    Code (CSharp):
    1. txtgen_camera.clearFlags = CameraClearFlags.Depth;
    by this:

    Code (CSharp):
    1. txtgen_camera.clearFlags = CameraClearFlags.Color;
    2. txtgen_camera.backgroundColor = new Color(0, 0, 0, 0);
    And it worked :)

    Unfortunatly the strange gray outline around the letters between the white and transparent pixels is still present.

    @neginfinity Both of your solutions gave no result about it :(

    Those extra pixels come from elsewhere. Perhaps a relationship with the openGL language used to compute the image ?
     
  4. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,572
    Those extra pixels probably come from the font texture. This is what happens when you have a sprite with alpha mask and a texture that matches the alpha mask exactly. Then the picture gets interpolated. Because color is interpolated at the same time, you'll get areas with gray or even black pixels where alpha is not still zero, and they form black outlines. This is usually not visible when your font is black, but can show up when it is white.

    Android picture looks like some sort of major failure, like being unable to find proper texture format.
     
    angrypenguin likes this.
  5. SynSeia

    SynSeia

    Joined:
    Nov 17, 2016
    Posts:
    5
    Well, it may not be the best solution, but I found a way to solve this. I just replaced all the no-null alpha pixel by the desired color:

    Code (CSharp):
    1. screenShot = RemoveOutlines(screenShot);
    2.  
    3. public Texture2D RemoveOutlines(Texture2D texture)
    4. {
    5.     for (int x = 0; x < texture.width; x++)
    6.     {
    7.         for (int y = 0; y < texture.height; y++)
    8.         {
    9.             if (texture.GetPixel(x, y).a != 0)
    10.                 texture.SetPixel(x, y, textColor);
    11.         }
    12.     }
    13.     return texture;
    14. }
    Result :