Open1

Unityもろもろ

CARNAGECARNAGE

PNGからTexture2Dを作成する

参考記事URL
https://qiita.com/r-ngtm/items/6cff25643a1a6ba82a6c

PNGのピクチャサイズ情報はビッグエンディアンで保存されているはず。
一方、C#のBitConverterは読み込み元のエンディアンが何であるかの指定はできない。
Microsoftのサンプルでは配列をリバースしろと書いてあるが、パフォーマンスが気になる(実測はしていない)ため、この部分を自作する。

        static public class Binary {
            static public uint ReadUint32FromBE(in byte[] bytes, uint offset)
            {
                uint tmp;
                if (System.BitConverter.IsLittleEndian) {
                    tmp = ((uint)bytes[offset + 0] << 24)
                        | ((uint)bytes[offset + 1] << 16)
                        | ((uint)bytes[offset + 2] << 8)
                        | ((uint)bytes[offset + 3]);
                } else {
                    tmp = ((uint)bytes[offset + 3] << 24)
                        | ((uint)bytes[offset + 2] << 16)
                        | ((uint)bytes[offset + 1] << 8)
                        | ((uint)bytes[offset + 0]);
                }
                return tmp;
            }
        }

C#はタプルを使えるようなので、PNGのバイナリからサイズを取得する関数はエラー処理を除けば以下のとおり実装できる。

        static private (int width, int height) ReadPngPictureSize(in byte[] png_bytes)
        {
            var tmp_w = Binary.ReadUint32FromBE(png_bytes, 16);
            var tmp_h = Binary.ReadUint32FromBE(png_bytes, 20);
            return ((int)tmp_w, (int)tmp_h);
        }

PNG画像ファイルの読み込みは以下のとおり。

        static private byte[] ReadPngFile(string path){
            var fs = new System.IO.FileStream(path, System.IO.FileMode.Open);
            var br = new System.IO.BinaryReader(fs);
            byte[] bytes = br.ReadBytes((int)br.BaseStream.Length);
            br.Close();
            return bytes;
        }

そして肝心のTexture2Dインスタンスの作成。

        static public UnityEngine.Texture2D CreateTexture2DFromPngFile(string png_file_path)
        {
            var png_binary = ReadPngFile(png_file_path);
            var size = ReadPngPictureSize(png_binary);
            var texture2d = new UnityEngine.Texture2D(size.width, size.height);
            texture2d.LoadImage(png_binary);
            return texture2d;
        }

ここで問題があり、Unity Version 2019.4.14f1 の環境において、UnityEngine.Texture2D.LoadImage()が存在していない模様。
公式リファレンスにも記載なし。

どうやらUnityEngine.ImageConversion.LoadImage()を代わりに使えという事らしい。
UnityEngine.ImageConversion.LoadImageは引数にTexture2Dインスタンスと読み込み元画像のバイナリ列を指定すれば良いみたいなので、おそらくPNGのサイズを自力で読み込んでくる必要はなさそう。
つまり以下の実装で良さそう。

        static private byte[] ReadPngFile(string path){
            var fs = new System.IO.FileStream(path, System.IO.FileMode.Open);
            var br = new System.IO.BinaryReader(fs);
            byte[] bytes = br.ReadBytes((int)br.BaseStream.Length);
            br.Close();
            return bytes;
        }

        static public UnityEngine.Texture2D CreateTexture2DFromPngFile(string png_file_path)
        {
            var png_binary = ReadPngFile(png_file_path);
            var texture2d = new UnityEngine.Texture2D(2,2);
            UnityEngine.ImageConversion.LoadImage(texture2d, png_binary);
            return texture2d;
        }