LoginSignup
17
7

More than 3 years have passed since last update.

【Unity(C#)】テクスチャを画像データに変換して端末に保存、読み込み

Last updated at Posted at 2020-09-14

はじめに

画像の保存処理で手間取ったのでメモを残します。

今回は下記の処理を行います。
・ImageコンポーネントにアタッチされたSprite(Texture)をPngに変換して保存
・Pngを読み込んでImageコンポーネントにSprite(Texture)としてアタッチ

デモ

実際にAndroid端末にて検証したデモがこちらになります。
Qiita画像保存.gif

書き込んだ画像を消去したのち復元に成功しました。
端末内に画像データは保存されているのでアプリを終了しても復元が可能です。


下記画像はPC内部の保存先ディレクトリを参照した際のスクショです。
ちゃんとPng形式で保存されていました。
PaintQiitaSaveSS.PNG

お絵描きの部分のコード

お絵描きの部分の実装は完全にそのまんま使わせていただいてます。

【参考リンク】:UI.Image にペイントする。線を手書きする。

スマホ対応部分とテクスチャのリセットだけ下記のように追記してます。

Painterクラスに追記

    /// <summary>
    /// テクスチャーをリセット
    /// </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
    }

画像の保存と読み込み

ボタンの押下処理はUniRxで実装してます。特に意味はないです。

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

/// <summary>
/// テクスチャー ⇔ Png画像 の変換と保存と読み込み
/// </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()
    {
        //セーブボタン
        _saveButton.OnPointerClickAsObservable().Subscribe(_ => ConvertToPngAndSave(GetSavePath(IMAGE_SAVE_FOLDER))).AddTo(this);
        //ロードボタン
        _loadButton.OnPointerClickAsObservable().Subscribe(_ => ConvertToTextureAndLoad(GetSavePath(IMAGE_SAVE_FOLDER))).AddTo(this);
        //リセットボタン
        _resetButton.OnPointerClickAsObservable().Subscribe(_ => _painter.ResetTexture());
    }

    ///  /// <summary>
    /// 保存先のパス取得
    /// </summary>
    /// <param name="folderName">区切りのフォルダ名</param>
    /// <returns>保存先のパス</returns>
    private string GetSavePath(string folderName)
    {
        string directoryPath = Application.persistentDataPath + "/" + folderName + "/";

        if (!Directory.Exists(directoryPath))
        {
            //まだ存在してなかったら作成
            Directory.CreateDirectory(directoryPath);
            return directoryPath + "paint.png";
        }

        return directoryPath + "paint.png";
    }

    /// <summary>
    /// 画像に変換&保存
    /// </summary>
    private void ConvertToPngAndSave(string path)
    {
        //Pngに変換
        byte[] bytes = _paintImage.sprite.texture.EncodeToPNG();
        //保存
        File.WriteAllBytes(path, bytes);
    }

    /// <summary>
    /// テクスチャに変換&読み込み
    /// </summary>
    private void ConvertToTextureAndLoad(string path)
    {
        //読み込み
        byte[] bytes = File.ReadAllBytes(path);
        //画像をテクスチャに変換
        Texture2D loadTexture = new Texture2D(2, 2); 
        loadTexture.LoadImage(bytes);
        //テクスチャをスプライトに変換
        _paintImage.sprite = Sprite.Create(loadTexture, new Rect(0, 0, loadTexture.width, loadTexture.height), Vector2.zero);
    }
}

Application.persistentDataPath

Application.persistentDataPathを呼ぶと
文字通りアプリ内に存在する永続的なパスへアクセスできます。
ここにフォルダを作って画像を保存しています。

EncodeToPNG

ImageConversionクラスからEncodeToPNGを呼び出すとbyte配列としてpngデータに変換してくれます。

【参考リンク】:ImageConversion.EncodeToPNG

WriteAllBytes,ReadAllBytes

WriteAllBytes,ReadAllBytesそれぞれデータの書き込みと読み込みを担います。
これ書くだけでやってくれるのでありがとうございますって感じです。

おわりに

書き込み、読み込み時のリソースの開放とかがよくわかりませんでした。
書かなくても動いたので"まあいっか"ってなってます。
素直で良い子なので危険なコード書いてたら教えて下さい。

参考リンク

【参考リンク】:EncodeToPNG hangs the script
【参考リンク】:はなちるのマイノート

17
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
7