[Unity (C #)] Convert textures to image data, save and load on the terminal

3 minute read

Introduction

I took a lot of time to save the image, so I will leave a note.

This time, the following processing is performed.
**-Convert Sprite (Texture) attached to Image component to Png and save
-Load Png and attach it to the Image component as Sprite (Texture) **

demo

Here is a demo that was actually verified on an Android device.
Qiita画像保存.gif

After erasing the written image, the restoration was successful.
Since the image data is saved in the terminal, it can be restored even if the application is closed.


The image below is a screenshot when referring to the save destination directory inside the PC.
It was properly saved in Png format.
PaintQiitaSaveSS.PNG

Code for the drawing part

I will use the implementation of the drawing part completely as it is.

Only the smartphone compatible part and the texture reset are added as follows.

Added to Painter class



    /// <summary>
    ///Reset texture
    /// </summary>
    public void ResetTexture()
    {
        var img = GetComponent<Image>();
        var rt = GetComponent<RectTransform>();
        var width = (int)rt.rect.width;
        var height = (int)rt.rect.height;
        texture = new Texture2D(width, height, TextureFormat.ARGB32, false);
        img.sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);
        
        Color32[] texColors = Enumerable.Repeat<Color32>(bgColor, width * height).ToArray();
        texture.SetPixels32(texColors);
        texture.Apply();
    }

    void Update()
    {
#if UNITY_EDITOR
        if (Input.GetMouseButtonDown(0))
        {
            beforeMousePos = GetPosition();
        }
        else if (Input.GetMouseButton(0))
        {
            Vector3 v = GetPosition();
            LineTo(beforeMousePos, v, lineColor);
            beforeMousePos = v;
            texture.Apply();
        }
#elif UNITY_ANDROID && !UNITY_EDITOR
        if (0 < Input.touchCount)
        {
            Touch touch = Input.GetTouch(0);

            if (touch.phase == TouchPhase.Began)
            {
                beforeMousePos = GetPosition();
            }
            else if (touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary)
            {
                Vector3 v = GetPosition();
                LineTo(beforeMousePos, v, lineColor);
                beforeMousePos = v;
                texture.Apply();
            }
        }
#endif
    }

Save and load images

Button press processing is implemented in UniRx. There is no particular meaning.

using System.IO;
using UniRx;
using UniRx.Triggers;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
///Texture ⇔ Png Image conversion, saving and loading
/// </summary>
public class TexturePngConverter : MonoBehaviour
{
    [SerializeField] private Button _saveButton;
    [SerializeField] private Button _loadButton;
    [SerializeField] private Button _resetButton;
    [SerializeField] private Image _paintImage;
    [SerializeField] private Painter _painter;

    private const string IMAGE_SAVE_FOLDER = "Image";
    
    private void Start()
    {
        //Save button
        _saveButton.OnPointerClickAsObservable().Subscribe(_ => ConvertToPngAndSave(GetSavePath(IMAGE_SAVE_FOLDER))).AddTo(this);
        //Load button
        _loadButton.OnPointerClickAsObservable().Subscribe(_ => ConvertToTextureAndLoad(GetSavePath(IMAGE_SAVE_FOLDER))).AddTo(this);
        //Reset button
        _resetButton.OnPointerClickAsObservable().Subscribe(_ => _painter.ResetTexture());
    }
    
    ///  /// <summary>
    ///Get the save destination path
    /// </summary>
    /// <param name="folderName">Delimited folder name</param>
    /// <returns>Destination path</returns>
    private string GetSavePath(string folderName)
    {
        string directoryPath = Application.persistentDataPath + "/" + folderName + "/";

        if (!Directory.Exists(directoryPath))
        {
            //Create if it doesn't exist yet
            Directory.CreateDirectory(directoryPath);
            return directoryPath + "paint.png ";
        }

        return directoryPath + "paint.png ";
    }

    /// <summary>
    ///Convert & save to image
    /// </summary>
    private void ConvertToPngAndSave(string path)
    {
        //Convert to png
        byte[] bytes = _paintImage.sprite.texture.EncodeToPNG();
        //Save
        File.WriteAllBytes(path, bytes);
    }

    /// <summary>
    ///Convert & load to texture
    /// </summary>
    private void ConvertToTextureAndLoad(string path)
    {
        //Read
        byte[] bytes = File.ReadAllBytes(path);
        //Convert image to texture
        Texture2D loadTexture = new Texture2D(2, 2); 
        loadTexture.LoadImage(bytes);
        //Convert textures to sprites
        _paintImage.sprite = Sprite.Create(loadTexture, new Rect(0, 0, loadTexture.width, loadTexture.height), Vector2.zero);
    }
}

Application.persistentDataPath
ʻWhen you call Application.persistentDataPath`
You can access the persistent path that literally exists within the app.
I make a folder here to save the images.

EncodeToPNG
If you call ʻEncodeToPNG from the ʻImageConversion class, it will be converted to png data as a byte array.

](https://docs.unity3d.com/ScriptReference/ImageConversion.EncodeToPNG.html)

WriteAllBytes,ReadAllBytes
WriteAllBytes and ReadAllBytes are responsible for writing and reading data, respectively.
I’m grateful that you can do it just by writing this.

in conclusion

I wasn’t sure about ** resource release ** when writing and reading.
It worked even if I didn’t write it, so it says “OK”.
Please let me know if you write dangerous code because you are a good and honest child.

](Https://stackoverflow.com/questions/60484595/encodetopng-hangs-the-script) Reference link: Hanachiru’s My Note / 09/13/214940)