1

I have a texture user drawn(on a mobile device), and I want to encode that to jpg so that user could share with that but I found the tranparent part will be black in the encoded jpg:

enter image description here

while I want the jpg to be like this: enter image description here

But I don't find any overridden method of texture2d.EncodeToJPG() to do this.

any ideas?

NOTE

the wing of the bird was drawn specifically to Color.white so it could be white in the encoded jpg.

Approach now

I finally manage to make this work by:

 Color32[] pixels = text.GetPixels32();
 Color blackTransparent = Color.black;
 blackTransparent.a = 0;
 for(int i = 0;i < pixels.Length; i++)
 {
    if(pixels[i] == blackTransparent)
    {
            pixels[i] = Color.white;
     }
  }
  text.SetPixels32(pixels);
  text.Apply();

But this will loop through all the pixels of the texture, if someone has a better method, plz let us know.

Problem with this approach:

we found that there will be some edge(when meeting with the black line) on the jpg using above code, maybe need some graphic processing knowledge to solve this? enter image description here

Here is How we load image to texture2d(as required by comment)

  Texture2D text = new Texture2D(1, 1, TextureFormat.PVRTC_RGBA4, false);
  byte[] imagebytes = null;
  string path = "image/path/sample.png";
  if (System.IO.File.Exists(path))
  {
      Debug.Log(" really load file from " + path);
      imagebytes = System.IO.File.ReadAllBytes(path);
  }
  text.LoadImage(imagebytes, false);
armnotstrong
  • 8,605
  • 16
  • 65
  • 130

2 Answers2

2

You can try to encode into PNG. JPG can't render transparency.

Just like :

texture2d.EncodeToPNG();

Find complete doc here

if app run on Microsoft OS you can also use this to convert into jpg

Community
  • 1
  • 1
Unrillaz Ti'bob
  • 71
  • 1
  • 1
  • 7
2

For your first question edit that asked how improve that code that is looping through all the pixels, you can. Just replace for(int i = 0;i < pixels.Length; i++) with for (int i = 0; i < pixels.Length; i += 4) then access each index from i+0 to i+3 inside the loop. The loop is about 4x faster.

Color32[] pixels = text.GetPixels32();
Color blackTransparent = Color.black;
Color overwriteColor = Color.white;

blackTransparent.a = 0;
for (int i = 0; i < pixels.Length; i += 4)
{
    if (pixels[i] == blackTransparent)
        pixels[i] = overwriteColor;


    if (pixels[i + 1] == blackTransparent)
        pixels[i + 1] = overwriteColor;


    if (pixels[i + 2] == blackTransparent)
        pixels[i + 2] = overwriteColor;


    if (pixels[i + 3] == blackTransparent)
        pixels[i + 3] = overwriteColor;
}
text.SetPixels32(pixels);
text.Apply(true);

Although, both code will run into the-same jagged line problem like mentioned in your second edit.

enter image description here



To fix it, don't compare the pixels directly with the == sign. Use a function that let's you use a threshold to compare color not just the numbers. This is the function I use to compare RGB colors in Unity and I suggest you start using it.

You then have to manually check if the alpha is below or equals to certain value. The value of 90 seems to working great for this.

This is what the script looks like:

Color32[] pixels = text.GetPixels32();
Color blackTransparent = Color.black;

for (int i = 0; i < pixels.Length; i += 4)
{
    checkPixel(ref pixels[i], blackTransparent, Color.white, 600);
    checkPixel(ref pixels[i + 1], blackTransparent, Color.white, 600);
    checkPixel(ref pixels[i + 2], blackTransparent, Color.white, 600);
    checkPixel(ref pixels[i + 3], blackTransparent, Color.white, 600);
}
text.SetPixels32(pixels);
text.Apply();

The functions checkPixel and ColourDistance functions:

void checkPixel(ref Color32 pixel1, Color32 pixel2, Color32 newColor, int threshold)
{
    if (ColourDistance(pixel1, pixel2) <= threshold)
    {
        if (pixel1.a <= 90)
        {
            pixel1 = Color.white;
        }
    }
}

private static double ColourDistance(Color32 c1, Color32 c2)
{
    double rmean = (c1.r + c2.r) / 2;

    int r = c1.r - c2.r;
    int g = c1.g - c2.g;
    int b = c1.b - c2.b;
    double weightR = 2 + rmean / 256;
    double weightG = 4.0;
    double weightB = 2 + (255 - rmean) / 256;
    return Math.Sqrt(weightR * r * r + weightG * g * g + weightB * b * b);
}

And this is the result:

enter image description here

It can still be improved.

Community
  • 1
  • 1
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Accept as an answer before trying that :D. thank you very much for your effort. – armnotstrong May 11 '17 at 13:51
  • Lol no problem but let me know if there is one. – Programmer May 11 '17 at 13:54
  • But could you give a hint why the loop you give is *4X* times faster than the origin one I give? It seems that it has compared 4 times in one loop circle. – armnotstrong May 11 '17 at 13:56
  • It is on the first line of the answer. Instead of doing `for(int i = 0;i < pixels.Length; i++)`, it is doing `for (int i = 0; i < pixels.Length; i += 4)`. Hint `i += 4` instead of `i++`. – Programmer May 11 '17 at 14:01
  • This allows it to skip every 4 index in the pixel. It compensates those missing 4 index loops by doing `i+0`, `i+1`, `i+2` and `i+3` instead the `for` loop. Note that `i+0` is not necessary as that will accomplish nothing. It can also be just `i`. A tricky way to loop over pixels in graphics stuff. This can also be improved by using pointers or C++. – Programmer May 11 '17 at 14:01
  • with respect, do you mean it's the `for` loop **itself** that contributes the massive performance consuming rather that the comparison of the color? And shall we use `40` instead of `4` to get a **40X** times faster advantage? :D – armnotstrong May 11 '17 at 14:09
  • Your original loop would loop `pixels.Length` times. That number is (`1,048576`) with your image on my side. If you modify it with the loop I have, it will loop `pixels.Length/4` times. That number (`262,144`). Loop can slow down image processing a-lot if it is big. Why I used 4 is because there are r,g,b,a. I don't think you can use 40 because you will have to to add 40 codes instead of 4 for each missing loop. I don't even know if you can do this without getting the exception. Try it and let me know but your code will get large. – Programmer May 11 '17 at 14:19