Merge textures from ui-images with color

Hi,

I’m building an editor for a 2d avatar with UI Images, where I change the sprite and color out of a list with different options.

Here is an example:
7599052--942928--upload_2021-10-24_22-43-3.png

The problem is to save the images as one png-file.

I tried to create a new texture with ReadPixels from screen like the screenshot example from the docs:

Texture2D texture = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false);
       
texture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0, false);
texture.Apply();

byte[] bytes = texture.EncodeToPNG();
File.WriteAllBytes(Application.persistentDataPath + "/avatar.png", bytes);

With this method the result image is dependent of the screensize and not the source sprites of 1024x1024px. So I get e.g. 357x659px images.

Another approach was to get the source textures and lerp all the pixels:

Texture2D texture = new Texture2D(texture_1.width, texture_1.height);

for (int i = 0; i < texture.width; i++)
{
    for (int j = 0; j < texture.height; j++)
    {
        Color tex1_color = texture_1.GetPixel(i, j);
        Color tex2_color = texture_2.GetPixel(i, j);
        Color merged_color = Color.Lerp(tex1_color, tex2_color, tex2_color.a / 1);

        texture.SetPixel(i, j, merged_color);
    }
}

texture.Apply();

byte[] bytes = texture.EncodeToPNG();
File.WriteAllBytes(Application.persistentDataPath + "/avatar.png", bytes);

But with this method the original colors of the sprites (in this case all default white) are saved and not the colors of the ui image component which is customized. And because there are about 15 images it takes some time to merge all one after another.

Is there a possibility to save the sprites with the color from image component to a single png with original size of the sprites?

Can you just check what color has been set on the image, and multiply by that color in the script?

Easiest would probably be a render texture but I’m not sure wether it also renders GUI elements properly. But could be worth a look.

Thanks for the replies!

With multiplying the color I didn’t get it to work and would have to lerp again every image, which was very slow.
I got a solution now with RenderTexture that captures also the UI.

Create a RenderTexture in the assets.
In the scene I added a Camera with TargetTexture of the RenderTexture and transparent color for background.
Move the UI Panel to a separate Canvas, which is Screen Space - Camera.
Create a plane in the scene with the Render Texture applied so the main camera shows something for the player.

And added a script on the camera where I just set a bool to true, if needed:

public class CameraRender : MonoBehaviour
{
    public bool RenderNow;
    public int ImageWidth = 1024;
    public int ImageHeight = 1024;
    public RenderTexture renderTexture;

    private void OnPostRender()
    {
        if (RenderNow)
        {
            Texture2D tex = new Texture2D(ImageWidth, ImageHeight, TextureFormat.ARGB32, false);

            RenderTexture.active = renderTexture;

            tex.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0, false);
            tex.Apply();

            byte[] bytes = tex.EncodeToPNG();

            Destroy(tex);
            renderTexture.Release();

            File.WriteAllBytes(Application.persistentDataPath + "/avatar.png", bytes);

            RenderNow = false;
        }
    }
}
1 Like