Xamarin.Forms 実行中に作成した画像をImageSourceにして表示する

こんにちは。レモンティーです。

以前、AndroidでBitmapを作りましたが、
www.sawalemontea.com

今回はXamarin.Formsで同じように実行中に画像を作って
ImageのSourceにセットして表示します。

(なお、↓の13章にはここより綺麗に書いてあります。)
プログラミングXamarin 上 マイクロソフト関連書

それではやっていきます。

Pictureクラス

今回はPictureというクラスを作成して必要な機能はこれに付けました。
変数とコンストラクタはこんな感じです。

        private int headerSize = 54;
        private byte[] buffer;

        public Picture(Dictionary<(int, int), (byte, byte, byte, byte)> colorInfo, int width, int height)
        {
            buffer = MakeBuffer(width, height);
            foreach (var info in colorInfo)
            {
                var (row, col) = info.Key;
                var (a, r, g, b) = info.Value;
                SetPixel(row, col, width, r, g, b, a);
            }
        }

今回は画像をDictionaryとして扱っていますので
配列で扱う場合はコンストラクタをちょっと書き換えないといけません。

MakeBufferとSetPixelはこの後説明します。

Bufferを作成し、header情報を書き込むメソッド

MakeBufferでbufferを作成し、header情報を書き込みます。
詳しくはここ↓に書いてあります。
Bitmapファイルフォーマット

前回の記事では色を1つのintとして扱いましたが、今回は
4つのbyteとして扱っています。

 private byte[] MakeBuffer(int width, int height)
   {
       //buffer作成
       var numPixels = width * height;
       var numPixelBytes = 4 * numPixels;
       var filesize = headerSize + numPixelBytes;
       var buffer = new byte[filesize];

       //bufferにheader情報を書き込む

       using (var memoryStream = new MemoryStream(buffer))
      {
        using (var writer = new BinaryWriter(memoryStream, Encoding.UTF8))
      {
        writer.Write(new char[] { 'B', 'M' });
        writer.Write(filesize);
        writer.Write((short)0);
        writer.Write((short)0);
        writer.Write(headerSize);

        writer.Write(40);
        writer.Write(width);
        writer.Write(height);
        writer.Write((short)1);
        writer.Write((short)32);
        writer.Write(0);
        writer.Write(numPixelBytes);
        writer.Write(0);
        writer.Write(0);
        writer.Write(0);
        writer.Write(0);
       }

       return buffer;
   }

Pixelに色の情報をセットするメソッド

SetPixelで色の情報をセットします。

       private void SetPixel(int row, int col, int width, int r, int g, int b, int a = 255)
        {
            var index = (row * width + col) * 4 + headerSize;
            buffer[index + 0] = (byte)b;
            buffer[index + 1] = (byte)g;
            buffer[index + 2] = (byte)r;
            buffer[index + 3] = (byte)a;
        }

ImageSourceに変換するメソッド

bufferからImageSourceに変換します。

        public ImageSource GetImageSource()
        {
            MemoryStream memoryStream = new MemoryStream(_buffer);

            ImageSource imageSource = ImageSource.FromStream(() =>
            {
                return memoryStream;
            });

            return imageSource;
        }

使用

実際に使用します。

var picture = new Picture(colorInfo, width, height);
Image1.Source = picture.GetImageSource();

このcolorInfoはユーザーの書いた絵などからあらかじめつくっておきます。
今回はテストなので適当な模様にします。

            var width = 128;
            var height = 128;
            var colorInfo = new Dictionary<(int,int),(byte,byte,byte,byte)>();
            for(int h = 0;h < height; h++)
            {
                for(int w = 0; w < width; w++)
                {
                    colorInfo[(h, w)] = (255, (byte)w,(byte)h, (byte)(w * h));
                }
            }

この場合こんな感じになります。
f:id:sawalemontea:20180429174618p:plain
…なんだか上下が逆な気がしますね。
後で…なおします(- -;)

今回はこれでおしまいです。
www.sawalemontea.com