■ サンプル
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/