【C#】Bitmap の高速化 について

■ はじめに

https://blogs.yahoo.co.jp/dk521123/38086057.html
の続き。
今回は、高速化。

■ サンプル

BitmapEx.cs

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace SampleForm
{
  public class BitmapEx : IDisposable
  {
    /// <summary>
    /// オリジナルのBitmapオブジェクト
    /// </summary>
    private readonly Bitmap originalBitmap;
    /// <summary>
    /// Bitmapに直接アクセスするためのオブジェクト
    /// </summary>
    private readonly BitmapData bitmapData;

    public BitmapEx(Bitmap originalBitmap)
    {
      this.originalBitmap = originalBitmap;
      this.bitmapData = originalBitmap.LockBits(
        new Rectangle(0, 0, originalBitmap.Width, originalBitmap.Height),
        ImageLockMode.ReadWrite,
        PixelFormat.Format24bppRgb);
    }

    private IntPtr Pointer
    {
      get
      {
        return this.bitmapData.Scan0;
      }
    }

    void IDisposable.Dispose()
    {
      if (this.bitmapData != null)
      {
        // オブジェクト開放
        this.originalBitmap.UnlockBits(this.bitmapData);
      }
    }

    /// <summary>
    /// BitmapのGetPixel同等
    /// </summary>
    /// <param name="x">X座標</param>
    /// <param name="y">Y座標</param>
    /// <returns>Colorオブジェクト</returns>
    public Color GetPixel(int x, int y)
    {
      // 非unsafe
      //int position = this.GetPosition(x, y);
      //byte blue = Marshal.ReadByte(this.Pointer, position);
      //byte green = Marshal.ReadByte(this.Pointer, position + 1);
      //byte red = Marshal.ReadByte(this.Pointer, position + 2);
      //return Color.FromArgb(red, green, blue);

      // unsafe
      unsafe
      {
        byte* pointer = (byte*)this.Pointer;
        int position = this.GetPosition(x, y);
        byte blue = pointer[position];
        byte green = pointer[position + 1];
        byte red = pointer[position + 2];
        return Color.FromArgb(red, green, blue);
      }
    }

    /// <summary>
    /// BitmapのSetPixel同等
    /// </summary>
    /// <param name="x">X座標</param>
    /// <param name="y">Y座標</param>
    /// <param name="color">Colorオブジェクト</param>
    public void SetPixel(int x, int y, Color color)
    {
      // 非unsafe
      //int position = this.GetPosition(x, y);
      //Marshal.WriteByte(this.Pointer, position, color.B);
      //Marshal.WriteByte(this.Pointer, position + 1, color.G);
      //Marshal.WriteByte(this.Pointer, position + 2, color.R);

      // unsafe
      unsafe
      {
        byte* pointer = (byte*)this.Pointer;
        int position = this.GetPosition(x, y);
        pointer[position + 0] = color.B;
        pointer[position + 1] = color.G;
        pointer[position + 2] = color.R;
      }
    }

    private int GetPosition(int x, int y)
    {
      return x * 3 + this.bitmapData.Stride * y;
    }
  }
}

Form1.cs

using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      Bitmap targetBitmap = new Bitmap(@"20161215052204.gif");

      Stopwatch stopwatch = new Stopwatch();
      stopwatch.Start();
      using (BitmapEx bitmapEx = new BitmapEx(targetBitmap))
      {
        for (int x = 0; x < targetBitmap.Width; x++)
        {
          for (int y = 0; y < targetBitmap.Height; y++)
          {
            Color pixel = bitmapEx.GetPixel(x, y);
            if (pixel.GetBrightness() <= 0.5f)
            {
              bitmapEx.SetPixel(x, y, Color.FromArgb(255, 255, 0, 0));
            }
            else
            {
              bitmapEx.SetPixel(x, y, Color.FromArgb(255, 0, 0, 0));
            }
          }
        }
      }
      stopwatch.Stop();

      this.label1.Text = "Result : " + stopwatch.ElapsedMilliseconds;
      this.pictureBox1.Image = targetBitmap;
    }
  }
}

測定結果

 * unsafe の方が若干早い
unsafe
1回目: 99ms
2回目:106ms
3回目:110ms
4回目:104ms
5回目:104ms
非unsafe
1回目:115ms
2回目:109ms
3回目:115ms
4回目:115ms
5回目:109ms


関連記事

C#】Bitmap / BitmapData

https://blogs.yahoo.co.jp/dk521123/38086057.html

C#】アンセーフ コード ~ unsafe / fixed ~

https://blogs.yahoo.co.jp/dk521123/38082656.html

C#】IntPtr構造体

https://blogs.yahoo.co.jp/dk521123/28757641.html