デバッグ用画像ファイルを作成する(8,24,32)

どのような環境、どのような言語でプログラミングするにしろ、デバッグ用の画像ファイルが必要です。お気に入りの風景画像やポートレート画像を使ってもいいですが、厳密な意味でのデバッグには使用できません。

下記のような画像を簡単に取得できるコードを下記に紹介します。

8bpp の PNG ファイル
debug_sample_bpp08.png
8bpp の BMP ファイル
debug_sample_bpp08.bmp
24bpp の PNG ファイル
debug_sample_bpp24.png
24bpp の PNG ファイル
debug_sample_bpp24.bmp
32bpp の PNG ファイル
debug_sample_bpp32.png

32bpp の場合 BMP は透明度の指定ができないので PNG だけにしています。32bpp では透明度 0xff で完全不透明としてあります。

下記コードのコンパイルするためには、VisualStudio のビルドオプションで「アンセーフコードの許可」を実施してください。ビルドオプションの設定方法は下記の記事をごらんください。

アンセーフunsafeコードを許可する

データのコピーやポインタ操作などアンセーフ unsafe なコードのビルドを実施する方法を紹介します。

下記の using 追加をお忘れなく.
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Diagnostics;

using System.IO;

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

// この using 追加を忘れずに. Don't forget to add this sentence.
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;

namespace aaa
{
	public partial class Form1 : Form
	{

		// WindowsAPI を定義する、memcpy() と同じ動作をする.
		[DllImport("Kernel32.Dll")]
		public static unsafe extern void CopyMemory( byte* dst, byte* src, int bytes );

		// 24ビットパックのデータ構造体の宣言.
		[StructLayout( LayoutKind.Explicit)]
		public struct GY_IMG_BPP24
		{
			[FieldOffset(0)] public byte B;
			[FieldOffset(1)] public byte G;
			[FieldOffset(2)] public byte R;
		}

		// 32ビットパックのデータ構造体の宣言.
		[StructLayout( LayoutKind.Explicit)]
		public struct GY_IMG_BPP32
		{
			[FieldOffset(0)] public byte B;
			[FieldOffset(1)] public byte G;
			[FieldOffset(2)] public byte R;
			[FieldOffset(3)] public byte A;
		}

		const int INI_W = 512;
		const int INI_H = 256;

		public Form1()
		{
			InitializeComponent();
		}

		private void Form1_Load( object sender, EventArgs e )
		{
			Directory.CreateDirectory( "c:/tmp" );
		}

		private void button1_Click( object sender, EventArgs e )
		{

			Bitmap bmp;
			byte [] data008;

			// ビットマップに書き込みたい8ビットデータの領域を確保する.
			int w = INI_W;
			int h = INI_H;
			int numpix = w * h;
			data008 = new byte[ numpix ];

			// 上が黒くて、下が白いグラデーションの画像データを格納する.
			for ( int j = 0; j < h; j++ )
			{

				int adrs = w * j;
				byte level = (byte)(j);

				for ( int i = 0; i < w; i++ )
				{
					data008[ adrs ] = level;
					adrs++;
				}

			}

			// 8ビットインデックスパレットのビットマップを生成する.
			PixelFormat pixfmt = PixelFormat.Format8bppIndexed;
			bmp = new Bitmap( w, h, pixfmt );

			// いったんデフォルトのパレットを盗む.
			ColorPalette pal = bmp.Palette;

			// 盗んだパレットの中身をいじる.
			for ( int k = 0; k < 256; k++ )
			{
				pal.Entries[k] = Color.FromArgb( k, k, k );
			}

			// いじったパレットを戻す.
			bmp.Palette = pal;

			// グラデーションデータをビットマップにコピーする.
			unsafe
			{

				BitmapData bmdt = new BitmapData();

				// ビットマップをロックする.
				Rectangle rct = new Rectangle( 0, 0, w, h );
				ImageLockMode mode = ImageLockMode.ReadWrite;
				bmdt = bmp.LockBits( rct, mode, pixfmt );

				// ワンスキャンぶんのバイト数を計算する.
				// ビットマップの幅ピクセル数が4の倍数でなかったときは、
				// one_scan_bytes_dst と one_scan_bytes_src は、
				// 違う値になる可能性が高いので別々に扱う.
				int one_scan_bytes_src = w * sizeof( byte);
				int one_scan_bytes_dst = bmdt.Stride;

				// ここで byte[] データを Bitmap に書き込む.
				fixed( byte* src = data008 )
				{

					byte* pt_scan_src = src;
					byte* pt_scan_dst = (byte*)( bmdt.Scan0 );

					for ( int j = 0; j < h; j++ )
					{

						byte* pt_src = pt_scan_src;
						byte* pt_dst = pt_scan_dst;

						for ( int i = 0; i < w; i++ )
						{
							*pt_dst = *pt_src;
							pt_src++;
							pt_dst++;
						}

						pt_scan_src += one_scan_bytes_src;
						pt_scan_dst += one_scan_bytes_dst;

					}

				}

				// ビットマップをアンロックする.
				bmp.UnlockBits( bmdt );

			}

			// ビットマップファイルを保存する.
			String fp_img0 = "c:/tmp/debug_sample_bpp08.bmp";
			ImageFormat imgfmt0 = ImageFormat.Bmp;
			bmp.Save( fp_img0, imgfmt0 );
			
			// ピングファイルを保存する.
			String fp_img1 = "c:/tmp/debug_sample_bpp08.png";
			ImageFormat imgfmt1 = ImageFormat.Png;
			bmp.Save( fp_img1, imgfmt1 );

			// ビットマップは生成したら Dispose しなければならない.
			bmp.Dispose();
			bmp = null;

			// 「フォト」や「ペイントブラシ」などで保存した画像ファイルを開く
			Process.Start( fp_img0 );
			Process.Start( fp_img1 );

		}

		private void button2_Click( object sender, EventArgs e )
		{

			Bitmap bmp;
			GY_IMG_BPP24 [] data024;
		
			int w = INI_W;
			int h = INI_H;
			int numpix = w * h;

			data024 = new GY_IMG_BPP24[ numpix ];

			int quarter_w = w >> 2;

			// 左から順に、Gray, Blue, Green, Red のグラデーションを作成したい.
			int xs0 = quarter_w * 0;
			int xs1 = quarter_w * 1;
			int xs2 = quarter_w * 2;
			int xs3 = quarter_w * 3;

			int xe0 = xs0 + quarter_w - 1;
			int xe1 = xs1 + quarter_w - 1;
			int xe2 = xs2 + quarter_w - 1;
			int xe3 = xs3 + quarter_w - 1;

			for ( int j = 0; j < h; j++ )
			{

				byte level = (byte)(j);

				// Gray のグラデーション.
				for ( int i = xs0; i <= xe0; i++ )
				{
					data024[ i + w * j ].B = level;
					data024[ i + w * j ].G = level;
					data024[ i + w * j ].R = level;
				}

				// Blue のグラデーション.
				for ( int i = xs1; i <= xe1; i++ )
				{
					data024[ i + w * j ].B = level;
					data024[ i + w * j ].G = 0x00;
					data024[ i + w * j ].R = 0x00;
				}

				// Green のグラデーション.
				for ( int i = xs2; i <= xe2; i++ )
				{
					data024[ i + w * j ].B = 0x00;
					data024[ i + w * j ].G = level;
					data024[ i + w * j ].R = 0x00;
				}

				// Red のグラデーション.
				for ( int i = xs3; i <= xe3; i++ )
				{
					data024[ i + w * j ].B = 0x00;
					data024[ i + w * j ].G = 0x00;
					data024[ i + w * j ].R = level;
				}

			}

			// 24bpp のビットマップを作成する.
			PixelFormat pixfmt = PixelFormat.Format24bppRgb;
			bmp = new Bitmap( w, h, pixfmt );

			// グラデーションデータをビットマップにコピーする.
			unsafe
			{

				BitmapData bmdt = new BitmapData();
				Rectangle rct = new Rectangle( 0, 0, w, h );
				ImageLockMode mode = ImageLockMode.ReadWrite;

				// ビットマップをロックする.
				bmdt = bmp.LockBits( rct, mode, pixfmt );

				// ワンスキャンぶんのバイト数を計算する.
				// ビットマップの幅ピクセル数が4の倍数でなかったときは、
				// one_scan_bytes_dst と one_scan_bytes_src は、
				// 違う値になる可能性が高いので別々に扱う.
				int one_scan_bytes_src = w * sizeof( GY_IMG_BPP24 );
				int one_scan_bytes_dst = bmdt.Stride;

				// ここで24bitsパックドデータを Bitmap に書き込む.
				fixed( void* src = &( data024[0] ))
				{

					byte* pt_src = (byte*)( src );
					byte* pt_dst = (byte*)( bmdt.Scan0 );
				
					for ( int j = 0; j < h; j++ )
					{
						CopyMemory( pt_dst, pt_src, one_scan_bytes_src );
						pt_src += one_scan_bytes_src;
						pt_dst += one_scan_bytes_dst;
					}

				}

				// ビットマップをアンロックする.
				bmp.UnlockBits( bmdt );

			}

			// ビットマップファイルを保存する.
			String fp_img0 = "c:/tmp/debug_sample_bpp24.bmp";
			ImageFormat imgfmt0 = ImageFormat.Bmp;
			bmp.Save( fp_img0, imgfmt0 );
			
			// ピングファイルを保存する.
			String fp_img1 = "c:/tmp/debug_sample_bpp24.png";
			ImageFormat imgfmt1 = ImageFormat.Png;
			bmp.Save( fp_img1, imgfmt1 );

			// ビットマップは生成したら Dispose しなければならない.
			bmp.Dispose();
			bmp = null;

			// 「フォト」や「ペイントブラシ」などで保存した画像ファイルを開く.
			Process.Start( fp_img0 );
			Process.Start( fp_img1 );

		}

		private void button3_Click( object sender, EventArgs e )
		{
		
			Bitmap bmp;
			GY_IMG_BPP32 [] data032;

			int w = INI_W;
			int h = INI_H;
			int numpix = w * h;

			data032 = new GY_IMG_BPP32[ numpix ];

			int quarter_w = w >> 2;

			// 左から順に、Gray, Blue, Green, Red のグラデーションを作成したい.
			int xs0 = quarter_w * 0;
			int xs1 = quarter_w * 1;
			int xs2 = quarter_w * 2;
			int xs3 = quarter_w * 3;

			int xe0 = xs0 + quarter_w - 1;
			int xe1 = xs1 + quarter_w - 1;
			int xe2 = xs2 + quarter_w - 1;
			int xe3 = xs3 + quarter_w - 1;

			// 0xff で不透明、0x80 で半透明、0x00 で透明.
			const byte LEVEL_ALPHA = 0xff;

			for ( int j = 0; j < h; j++ )
			{

				byte level = (byte)(j);

				// Gray のグラデーション.
				for ( int i = xs0; i <= xe0; i++ )
				{
					data032[ i + w * j ].B = level;
					data032[ i + w * j ].G = level;
					data032[ i + w * j ].R = level;
					data032[ i + w * j ].A = LEVEL_ALPHA;
				}

				// Blue のグラデーション.
				for ( int i = xs1; i <= xe1; i++ )
				{
					data032[ i + w * j ].B = level;
					data032[ i + w * j ].G = 0x00;
					data032[ i + w * j ].R = 0x00;
					data032[ i + w * j ].A = LEVEL_ALPHA;
				}

				// Green のグラデーション.
				for ( int i = xs2; i <= xe2; i++ )
				{
					data032[ i + w * j ].B = 0x00;
					data032[ i + w * j ].G = level;
					data032[ i + w * j ].R = 0x00;
					data032[ i + w * j ].A = LEVEL_ALPHA;
				}

				// Red のグラデーション.
				for ( int i = xs3; i <= xe3; i++ )
				{
					data032[ i + w * j ].B = 0x00;
					data032[ i + w * j ].G = 0x00;
					data032[ i + w * j ].R = level;
					data032[ i + w * j ].A = LEVEL_ALPHA;
				}

			}

			// 32bpp のビットマップを作成する.
			PixelFormat pixfmt = PixelFormat.Format32bppArgb;
			bmp = new Bitmap( w, h, pixfmt );

			// グラデーションデータをビットマップにコピーする.
			unsafe
			{

				BitmapData bmdt = new BitmapData();
				Rectangle rct = new Rectangle( 0, 0, w, h );
				ImageLockMode mode = ImageLockMode.ReadWrite;

				// ビットマップをロックする.
				bmdt = bmp.LockBits( rct, mode, pixfmt );

				// ワンスキャンぶんのバイト数を計算する.
				// ビットマップの幅ピクセル数が4の倍数でなかったときは、
				// one_scan_bytes_dst と one_scan_bytes_src は、
				// 違う値になる可能性が高いので別々に扱う.
				int one_scan_bytes_src = w * sizeof( GY_IMG_BPP32 );
				int one_scan_bytes_dst = bmdt.Stride;

				// ここで32bitsパックドデータを Bitmap に書き込む.
				fixed( void* src = &( data032[0] ))
				{

					byte* pt_src = (byte*)( src );
					byte* pt_dst = (byte*)( bmdt.Scan0 );
				
					for ( int j = 0; j < h; j++ )
					{
						CopyMemory( pt_dst, pt_src, one_scan_bytes_src );
						pt_src += one_scan_bytes_src;
						pt_dst += one_scan_bytes_dst;
					}

				}

				// ビットマップをアンロックする.
				bmp.UnlockBits( bmdt );

			}

			// BMPファイルは透明度の定義がないのでPNGファイルで保存する.
			// ピングファイルを保存する.
			String fp_img1 = "c:/tmp/debug_sample_bpp32.png";
			ImageFormat imgfmt1 = ImageFormat.Png;
			bmp.Save( fp_img1, imgfmt1 );

			// ビットマップは生成したら Dispose しなければならない.
			bmp.Dispose();
			bmp = null;

			// 「フォト」や「ペイントブラシ」などで保存した画像ファイルを開く.
			Process.Start( fp_img1 );

		}
	}
}

サンプルソースを用意しました。よろしければご利用ください。

出来上がったソフトを実行すると Form1_Load()に記述してあるとおり c:/tmp というディレクトリを作成し、Button1, Button2, Button3 を押すと、c:/tmp の中に画像ファイルが配置されます。