検索
連載

第21章 ポインタを使用できる「安全でないコード」連載 改訂版 C#入門(4/4 ページ)

連載最終回となる今回は、C#におけるポインタの利用について解説する。ポインタのサポートにより、C#では低レベルな処理も効率よく行うことができる。

PC用表示 関連情報
Share
Tweet
LINE
Hatena
前のページへ |       

21-9 「安全でないコード」が本当にほしくなる事例

 以下は、「安全でないコード」が本当にほしくなる事例として作成したものである。24ビットのBMPファイルを読み込んで、ネガ反転処理を行うものである。まず、.NET Frameworkのクラス・ライブラリを活用した正統派のソースList 21-9を見ていただこう。

  1: using System;
  2: using System.IO;
  3: using System.Drawing;
  4:
  5: namespace Sample009
  6: {
  7:   using System.IO;
  8:   using System.Drawing;
  9:   class Class1
 10:   {
 11:     private static void NegativeBMP( string inputFileName, string outputFileName )
 12:     {
 13:       Bitmap bitmap = new Bitmap( inputFileName );
 14:       for( int y=0; y<bitmap.PhysicalDimension.Height; y++ )
 15:       {
 16:         for( int x=0; x<bitmap.PhysicalDimension.Width; x++ )
 17:         {
 18:           Color c = bitmap.GetPixel( x, y );
 19:           bitmap.SetPixel( x, y, Color.FromArgb((byte)~c.R, (byte)~c.G,(byte)~c.B) );
 20:         }
 21:       }
 22:       bitmap.Save( outputFileName );
 23:     }
 24:     [STAThread]
 25:     static void Main(string[] args)
 26:     {
 27:       DateTime start = DateTime.Now;
 28:       const string inputFileName = "c:\\test1.bmp";
 29:       const string outputFileName = "c:\\test2.bmp";
 30:       NegativeBMP( inputFileName, outputFileName );
 31:       DateTime end = DateTime.Now;
 32:       Console.WriteLine( end-start );
 33:     }
 34:   }
 35: }

List 21-9

 次に、「安全でないコード」を用いた例、List 21-10を見ていただこう。

  1: using System;
  2: using System.IO;
  3: using System.Runtime.InteropServices;
  4:
  5: namespace Sample010
  6: {
  7:   [StructLayout(LayoutKind.Sequential,Pack=1)]
  8:   struct BITMAPFILEHEADER
  9:   {
 10:     public ushort   bfType;
 11:     public uint bfSize;
 12:     public ushort   bfReserved1;
 13:     public ushort   bfReserved2;
 14:     public uint bfOffBits;
 15:   }
 16:   [StructLayout(LayoutKind.Sequential,Pack=1)]
 17:   struct BITMAPINFOHEADER
 18:   {
 19:     public uint biSize;
 20:     public int  biWidth;
 21:     public int  biHeight;
 22:     public ushort   biPlanes;
 23:     public ushort   biBitCount;
 24:     public uint biCompression;
 25:     public uint biSizeImage;
 26:     public int  biXPelsPerMeter;
 27:     public int  biYPelsPerMeter;
 28:     public uint biClrUsed;
 29:     public uint biClrImportant;
 30:     public const int BI_RGB = 0;
 31:   };
 32:
 33:   class Class1
 34:   {
 35:     unsafe private static void NegativeBMP( string inputFileName, string outputFileName )
 36:     {
 37:       FileStream inputFile = new FileStream(inputFileName,FileMode.  Open);
 38:       byte [] fileImage = new byte[inputFile.Length];
 39:       inputFile.Read(fileImage,0,(int)inputFile.Length);
 40:       inputFile.Close();
 41:
 42:       fixed( byte * basePtr = &fileImage[0] )
 43:       {
 44:         BITMAPFILEHEADER * bitmapFileHeader = (BITMAPFILEHEADER *)basePtr;
 45:         BITMAPINFOHEADER * bitmapInfoHeader = (BITMAPINFOHEADER * )(basePtr + sizeof(BITMAPFILEHEADER));
 46:         byte * bits = basePtr + bitmapFileHeader->bfOffBits;
 47:         if( bitmapInfoHeader->biBitCount != 24 || bitmapInfoHeader->biCompression != BITMAPINFOHEADER.BI_RGB )
 48:         {
 49:           Console.WriteLine( "Error: 24bit RGB Bitmap Only");
 50:           return;
 51:         }
 52:         int bytesInLine = (bitmapInfoHeader->biWidth+3)/4*4;
 53:         int totalBytes = bytesInLine * bitmapInfoHeader->biHeight * 3;
 54:         for( int i = 0; i<totalBytes; i++ )
 55:         {
 56:           *bits = (byte)~*bits;
 57:           bits++;
 58:         }
 59:       }
 60:
 61:       FileStream outputFile = new FileStream(outputFileName,FileMode.Create);
 62:       outputFile.Write(fileImage,0,fileImage.Length);
 63:       outputFile.Close();
 64:     }
 65:     [STAThread]
 66:     static void Main(string[] args)
 67:     {
 68:       DateTime start = DateTime.Now;
 69:       const string inputFileName = "c:\\test1.bmp";
 70:       const string outputFileName = "c:\\test2.bmp";
 71:       NegativeBMP( inputFileName, outputFileName );
 72:       DateTime end = DateTime.Now;
 73:       Console.WriteLine( end-start );
 74:     }
 75:   }
 76: }

List 21-10

 List 21-9List 21-10のどちらを実行しても、Fig.21-10の画像をFig.21-11のように加工する。どちらも同じ結果になる。


Fig.21-10


Fig.21-11

 さて、ソースを見比べれば、正統派の方がずっと短く読みやすいことが分かるだろう。しかも、正統派のソースは、24ビットBMPに制限されず、Bitmapクラスがサポートするあらゆるデータ型を扱える。それにもかかわらず、「安全でないコード」がほしくなる理由は、圧倒的な性能差にある。Pentium 4/1.5GHzのマシンでの処理に、正統派のコードは23秒を必要としたが、「安全でないコード」版の方は0.4秒ほどで済んだ。Fig.21-12は、サンプル・ソースを実行した所要時間を示している。上が正統派のソースの場合で、下が「安全でないコード」を使用したソースの場合である。


Fig.21-12

 もちろん、ここで取り上げた正統派のソースはあまりにも愚直すぎるので、「安全でないコード」を使わなくても、低レベルの処理に置き換えることでかなりの高速化を行うことは可能だ。例えば、byte配列で一気に反転処理を行えば、性能面で大差ないコードを書くこともできるだろう。しかし、このような単純な処理で済まない場合、型を持ったポインタを使用できる「安全でないコード」の方が、よりエレガントに問題を解決できるだろう。

『新プログラミング環境 C#がわかる+使える』
 本記事は、(株)技術評論社が発行する書籍『新プログラミング環境 C#がわかる+使える』から許可を得て一部分を転載したものです。

【本連載と書籍の関係について 】
 この書籍は、本フォーラムで連載した「C#入門」を大幅に加筆修正し、発行されたものです。連載時はベータ版のVS.NETをベースとしていましたが、書籍ではVS.NET製品版を使ってプログラムの検証などが実施されています。技術評論社、および著者である川俣晶氏のご好意により、書籍の内容を本フォーラムの連載記事として掲載させていただけることになりました。

技術評論社の解説ページ

ご注文はこちらから


「連載 改訂版 C#入門」のインデックス

連載 改訂版 C#入門

Copyright© Digital Advantage Corp. All Rights Reserved.

前のページへ |       
ページトップに戻る