■ サンプル
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
参考文献
http://nonsoft.la.coocan.jp/SoftSample/CS.NET/SampleBitmapPlus.htmlhttp://hima-tubusi.blogspot.com/2016/08/bitmap.html
https://qiita.com/Nuits/items/4a2fbc0f4e8583bd5531
http://aokomoriuta.hateblo.jp/entry/2016/05/05/145810
https://blogs.yahoo.co.jp/mocchi_2003/40367106.html
https://blogs.yahoo.co.jp/mocchi_2003/40369765.html
計測した結果が載っている
https://www.84kure.com/blog/2014/07/13/c-%E3%83%93%E3%83%83%E3%83%88%E3%83%9E%E3%83%83%E3%83%97%E3%81%AB%E3%83%94%E3%82%AF%E3%82%BB%E3%83%AB%E5%8D%98%E4%BD%8D%E3%81%A7%E9%AB%98%E9%80%9F%E3%81%AB%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9/