[Gnuplot] 標準出力に流れるバイナリデータを読み取って表示する

#標準出力に流れるバイナリデータを読み取って表示する
標準出力に流れるデータを直接取得することによって、
ファイルを経由せずに、画像データ(PNG)を表示する方法を以下に記述します。

```CS
var process = new Process()
{
  StartInfo = new ProcessStartInfo
  {
     FileName = @".\gnuplot.exe",
     Arguments = "",
     CreateNoWindow = true,
     UseShellExecute = false,
     RedirectStandardInput = true,
     RedirectStandardOutput = true,
     RedirectStandardError = false,
  }
};

process.Start();
process.StandardInput.WriteLine("set term png enhanced size 640,480");
process.StandardInput.WriteLine("set output");
process.StandardInput.WriteLine("set palette defined(0'#000090',1'#000fff',2'#0090ff',3'#0fffee',4'#90ff70',5'#ffee00',6'#ff7000',7'#ee0000',8'#7f0000')");
process.StandardInput.WriteLine("set xrange[-2:2]");
process.StandardInput.WriteLine("set yrange[-2:2]");
process.StandardInput.WriteLine("set pm3d at bs");
process.StandardInput.WriteLine("set ticslevel 0.8");
process.StandardInput.WriteLine("set isosample 20,20");
process.StandardInput.WriteLine("set view 60,40");
process.StandardInput.WriteLine("splot exp(-x*x) * exp(-y*y)");

var reader = new BinaryReader(process.StandardOutput.BaseStream);

var iend = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 };
var list = new List<byte>();

while (list.Skip(list.Count - iend.Length).SequenceEqual(iend) != true)
{
  list.Add(reader.ReadByte());
}

var image = Mat.FromImageData(list.ToArray(), ImreadModes.Color);
pictureBox.Image = BitmapConverter.ToBitmap(image);

//
// [exit]を送信するまでは何度でもプロットでき、その都度、画像データを取得できます。
//

process.StandardInput.WriteLine("exit");

process.Close();
```
・ReadByte(...)やRead(...)は、ファイルの終端を認識できなかった場合に永久にデータ待ちをしてしまいます。
しかし、標準出力はタイムアウトを設定できないため、何らかの処理が必要となります。
以下に例を記述しますが、もっとスマートな方法があるかもしれません...

```CS
Task<byte> task;
while (list.Skip(list.Count - iend.Length).SequenceEqual(iend) != true)
{
  if ((task = Task.Run(() => reader.ReadByte())).Wait(1000) == false)
  {
     break;
  }

  list.Add(task.Result);
}
```
```CS
byte[] buffer = new byte[1024];
Task<IEnumerable<byte>> task = null;
while (list.Skip(list.Count - iend.Length).SequenceEqual(iend) != true)
{
  if ((task = Task.Run(() => buffer.Take(reader.Read(buffer, 0, buffer.Length)))).Wait(1000) == false)
  {
     break;
  }

  list.AddRange(task.Result);
}
```
```CS
var task = Task.Run(() =>
{
  while (list.Skip(list.Count - iend.Length).SequenceEqual(iend) != true)
  {
     list.Add(reader.ReadByte());
  }
});         

var taskTimeout = Task.Run(() =>
{
  int lastCount = -1;
  while(lastCount != list.Count)
  {
     lastCount = list.Count;
     Task.Delay(1000).Wait();
  }
});

Task.WaitAny(task, taskTimeout);
```

・データ待ちの解除は、以下のようなコードで行います。

```CS
if (task.IsCompleted == false)
{
  process.StandardInput.WriteLine("exit");
}
```

#その他
・画像データの出力が1枚だけの場合は以下の記述でも可能です。

```CS
// --- 省略 ---

process.StandardInput.WriteLine("splot exp(-x*x) * exp(-y*y)");
process.StandardInput.WriteLine("exit");

var reader = new BinaryReader(process.StandardOutput.BaseStream);

var list = new List<byte>();
byte[] buf = new byte[1024];

while (reader.Read(buf, 0, buf.Length) != 0)
{
   list.AddRange(buf);
}

// --- 省略 ---
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662099/a6d9b27a-c1ea-17f7-a723-249b58f99cb6.png)
・[gnuplot - graphing utility](http://www.gnuplot.info/)