Win32 3原色

GetPixel() 関数, SetPixel() 関数でプログラムすると実行速度が気になります。
そこで Win32 API のデバイス独立ビットマップ(Device-Independent Bitmap) を使って直接アクセスする方法を試します。
画像の色は R(赤) G(緑) B(青) の組み合わせで決まるのですが、3原色を個々に設定して描画してみましょう。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. Win32 API を使って3原色を個々に設定する image.cs のソースコードです。
    次のソースコードは Image Guid にまとめて掲載しているので、こちらを参照して下さい。
    BITMAPINFOHEADER 構造体の定義 BITMAPINFO 構造体の定義 Win32 API の関数宣言InitDib() 関数
    //★ Win32 API で RGB を切り替えて描画    前田 稔
    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    
    struct BITMAPINFOHEADER
    {
        ・・・ Image Guid を参照 ・・・
    };
    
    public class MyForm : Form
    {   Face    App;
        int     mode = 7;
    
        public MyForm()
        {
            BackColor = SystemColors.AppWorkspace;
            App = new Face();
            Width = App.m_bmp.Width;
            Height = App.m_bmp.Height+32;
            App.InitDib();
            App.CopyBmp(App.m_DibDC);
            App.ChkPix();
            Paint += new PaintEventHandler(MyHandler);
            MouseDown += new MouseEventHandler(OnMyMouseDown);
        }
    
        private void MyHandler(object sender, PaintEventArgs e)
        {
            Graphics g; 
            g = e.Graphics;
            if (App.m_bmp == null)  Application.Exit();
            App.ViewDib(g,0,0);
        }
    
        private void OnMyMouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)  //マウスの左ボタン
            {
                mode = (mode+1)%8;
                App.SetRGB(mode);
            }
            Invalidate();
        }
    }
    
    //☆ Face Object Class
    unsafe class Face
    {
        public  Bitmap  m_bmp;      // 入力画像
        public  IntPtr  m_Dib;      // DIB 画像
        public  IntPtr  m_DibDC;    // DIB DC
        public  byte    *dat;       // DIB ピクセルデータ
        string  ImgFile = "";
        public const int BI_RGB = 0;
        public const int DIB_RGB_COLORS = 0;
        public const int SRCCOPY = 0xcc0020;
     
        [DllImport("gdi32.dll")]
    
        ・・・ Image Guid を参照 ・・・
    
        // Constructor
        public Face()
        {
            OpenFileDialog opendlg = new OpenFileDialog();
            opendlg.Filter = "画像ファイル (*.bmp)|*.bmp|すべてのファイル (*.*)|*.*";
            if (opendlg.ShowDialog() == DialogResult.OK)
            {   ImgFile = opendlg.FileName; }
            try
            {   m_bmp = new Bitmap(ImgFile); }
            catch
            {   MessageBox.Show("画像ファイルが読めません!", ImgFile);
                return;
            }
        }
    
        // Destructor
        ~Face()
        {   DeleteDC(m_DibDC);
            DeleteObject(m_Dib);
        }
    
        // 画像ファイルの入力
        public void LoadImage(string file)
        {   m_bmp = new Bitmap(file);  }
        public void LoadImage()
        {   LoadImage(ImgFile);  }
    
        // m_DibDC(DIB) の画像を描画する 
        public void ViewDib(Graphics g, int x, int y)
        {
            if (m_DibDC == null) return;
            IntPtr hDC = g.GetHdc();
            BitBlt(hDC, x, y, m_bmp.Width, m_bmp.Height, m_DibDC, 0, 0, SRCCOPY);
            g.ReleaseHdc(hDC);
        }
    
        // hDC ← Bitmap m_bmp イメージデータの転送 
        public void CopyBmp(IntPtr hDC)
        {
            IntPtr hBmpSrc = m_bmp.GetHbitmap();
            Graphics gdraw = Graphics.FromImage(m_bmp);
            IntPtr srcHDC = gdraw.GetHdc();
            SelectObject(srcHDC, hBmpSrc);
            BitBlt(hDC, 0, 0, m_bmp.Width, m_bmp.Height, srcHDC, 0, 0, SRCCOPY);
            DeleteObject(hBmpSrc);
            gdraw.ReleaseHdc(srcHDC);
        }
    
        //☆ DIB の初期化
        unsafe public void InitDib()
        {
            ・・・ Image Guid を参照 ・・・
        }
    
        // Image 画像を Dib に転送して mode の色に設定する関数
        public void SetRGB(int mode)
        {
            CopyBmp(m_DibDC);
            for(int y=0; y<m_bmp.Height; y++)
                for (int x=0; x<m_bmp.Width; x++)
                {
                    if ((mode & 1) == 0)    MAP(x, y, 0, 0);
                    if ((mode & 2) == 0)    MAP(x, y, 1, 0);
                    if ((mode & 4) == 0)    MAP(x, y, 2, 0);
                }
        }
        // *dat(x, y, c) に v を設定
        public void MAP(int x, int y, int c, byte v)
        {
            byte *pt;
            pt = dat+(y*m_bmp.Width+x)*4+c;
            *pt = v;
        }
    
        // byte *dat; の印字確認
        public void ChkPix()
        {
            for(int i = 0; i < 32; i++)
            {   DebugRGB(dat+i*4);  }
        }
        public void DebugRGB(byte *v)
        {
            Debug.Write("BGRA: " + *v + ", " + *(v+1) + ", " + *(v+2) + ", " + *(v+3) + "\n");
        }
    }
    
    class image01
    {
        [STAThread]
        public static void Main()
        {
            MyForm mf = new MyForm();
            Application.Run(mf);
        }
    }
    
  2. Win32 API の使い方の基本を Image Guid で説明しています。
    Runtime.InteropServices と Diagnostics を取り込んで下さい。
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    
  3. 画像の色(ピクセル)は、3原色(赤緑青)を8ビット(256階調)の組み合わせで表します。
    青色(B)の8ビット 緑色(G)の8ビット 赤色(R)の8ビット
    このプログラムでは、3原色を個々に設定して描画します。
    mode が色の組み合わせを指定する領域で「0~7」で指定します。
    int     mode= 7;        // 色モード(0~7)
    
    1: が青色に対応するビットで、2: が緑色に対応するビットで、4: が赤色に対応するビットです。
    例えば 1: は青色になり、6: は緑と赤の組み合わせで黄色になります。
  4. MyForm では Face Class の Constructor から BMP File を選択して画像を入力します。
    入力した画像に合わせてウインドウサイズを設定します。
    BitBlt で描画するので「ピクセル/インチ」は考慮しません。
    App.InitDib(); 関数で DIB を初期化します。
    MyHandler では m_DibDC の画像を描画するので、App.CopyBmp(App.m_DibDC); で画像を転送します。
    App.ChkPix(); は画像が転送されたことを確認するためにピクセルデータの先頭部分を印字しています。
    public class MyForm : Form
    {   Face    App;
        int     mode = 7;
    
        public MyForm()
        {
            BackColor = SystemColors.AppWorkspace;
            App = new Face();
            Width = App.m_bmp.Width;
            Height = App.m_bmp.Height+32;
            App.InitDib();
            App.CopyBmp(App.m_DibDC);
            App.ChkPix();
            Paint += new PaintEventHandler(MyHandler);
            MouseDown += new MouseEventHandler(OnMyMouseDown);
        }
    
  5. MyHandler() では、3原色を個々に切り替えて App.ViewDib() 関数で描画します。
        private void MyHandler(object sender, PaintEventArgs e)
        {
            Graphics g; 
            g = e.Graphics;
            if (App.m_bmp == null)  Application.Exit();
            App.ViewDib(g,0,0);
        }
    
  6. 画像を描画する Face Object Class では、ポインターを使うので unsafe を設定して下さい。
    m_bmp; が選択した BMP 画像を入力する領域です。
    m_Dib; が DIB の画像の領域で m_DibDC; がそのハンドルです。
    byte *dat; が DIB のピクセルデータへのポインターです。
    //☆ Face Object Class
    unsafe class Face
    {
        public  Bitmap  m_bmp;      // 入力画像
        public  IntPtr  m_Dib;      // DIB 画像
        public  IntPtr  m_DibDC;    // DIB DC
        public  byte    *dat;       // DIB ピクセルデータ
    
  7. Face Class の Destructor で取得したオブジェクトを開放します。
        // Destructor
        ~Face()
        {   DeleteDC(m_DibDC);
            DeleteObject(m_Dib);
        }
    
  8. ViewDib() 関数が RGB を順に切り替えた DIB の画像を描画する関数です。
        // m_DibDC(DIB) の画像を描画する 
        public void ViewDib(Graphics g, int x, int y)
        {
            if (m_DibDC == null) return;
            IntPtr hDC = g.GetHdc();
            BitBlt(hDC, x, y, m_bmp.Width, m_bmp.Height, m_DibDC, 0, 0, SRCCOPY);
            g.ReleaseHdc(hDC);
        }
    
  9. CopyBmp() 関数は hDC で指定されたデバイスに m_bmp の画像を転送する関数です。
        // hDC ← Bitmap m_bmp イメージデータの転送 
        public void CopyBmp(IntPtr hDC)
        {
            IntPtr hBmpSrc = m_bmp.GetHbitmap();
            Graphics gdraw = Graphics.FromImage(m_bmp);
            IntPtr srcHDC = gdraw.GetHdc();
            SelectObject(srcHDC, hBmpSrc);
            BitBlt(hDC, 0, 0, m_bmp.Width, m_bmp.Height, srcHDC, 0, 0, SRCCOPY);
            DeleteObject(hBmpSrc);
            gdraw.ReleaseHdc(srcHDC);
        }
    
  10. SetRGB() が赤緑青の色を mode に従って設定する関数です。
    CopyBmp(m_DibDC); で入力した画像を DIB の m_DibDC に転送します。
    そして m_DibDC のイメージを mode に従って色ごとに off(ゼロ) にします。
    ゼロに設定された色は無くなるので、例えば mode=1 では青色だけで描画されます。
    実行結果を見ていただきたいのですが、カラー写真の画像などでは RGB を切り替えると面白い画像になります。
        // Image 画像を Dib に転送して mode の色に設定する関数
        public void SetRGB(int mode)
        {
            CopyBmp(m_DibDC);
            for(int y=0; y<m_bmp.Height; y++)
                for (int x=0; x<m_bmp.Width; x++)
                {
                    if ((mode & 1) == 0)    MAP(x, y, 0, 0);
                    if ((mode & 2) == 0)    MAP(x, y, 1, 0);
                    if ((mode & 4) == 0)    MAP(x, y, 2, 0);
                }
        }
        // *dat(x, y, c) に v を設定
        public void MAP(int x, int y, int c, byte v)
        {
            byte *pt;
            pt = dat+(y*m_bmp.Width+x)*4+c;
            *pt = v;
        }
    
  11. ChkPix() で *dat が指し示すビットマップデータの先頭部分を印字します。
    BMP FILE(DIB)では上下逆に格納されているので、最下段のラインの左端から印字されます。
        // byte *dat; の印字確認
        public void ChkPix()
        {
            for(int i = 0; i < 32; i++)
            {   DebugRGB(dat+i*4);  }
        }
        public void DebugRGB(byte *v)
        {
            Debug.Write("BGRA: " + *v + ", " + *(v+1) + ", " + *(v+2) + ", " + *(v+3) + "\n");
        }
    
  12. C言語 Windows でも同様のプログラム Image 3原色 を作成しています。

[Next Chapter ↓] W32 Color ⇒ Gray
[Previous Chapter ↑] GetDIBits

超初心者のプログラム入門(C# Frame Work)