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:
while I want the jpg to be like this:
But I don't find any overridden method of texture2d.EncodeToJPG()
to do this.
any ideas?
the wing of the bird was drawn specifically to Color.white
so it could be white in the encoded jpg.
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.
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?
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);
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.
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:
It can still be improved.