オブジェクト指向でなぜつくるのか

C++クラスの例>DIBクラス

 今回は、C++のクラスの例としてWindowsDIB(デバイス独立ビットマップ)をクラス化してみます。今回クラス化するのは、DIBの作成と破棄、そして簡単な描画機能だけですが、このクラスを基に機能を拡張して行けばDIBを「オブジェクト」として簡単に扱うためのクラスも出来るでしょう。

DIBクラスの設計

 今回作るクラスは、24ビットフルカラーDIBの初期化と解放、描画処理をクラス化したものです。クラスの設計としては、大きさやビットマップのアドレスといった属性とDIBを操作する関数を定義する事になるわけですが、オブジェクト指向プログラミングの「原則」通り属性の変数はprivateとして直接参照出来ないようにし、アクセスメソッドを用意します。
 属性は、大きさ(幅と高さ)、ビットマップの先頭アドレス、それからDIBの操作の時に必要になるBITMAPINFO構造体といったものを定義しましょう。関数は、DIBを作成・初期化するコンストラクタと破棄するデストラクタ、それに描画関数とビットマップの先頭アドレスのアクセスメソッドを作ってみました。

DIBクラスの宣言

  class CDIB { // DIBクラス

  private:

      DWORD dwWidth,dwHeight,dwLength;
      LPBYTE lpBMP;
      BITMAPINFO biDIB;
      BITMAPINFOHEADER bhDIB;

  public:

      CDIB(DWORD, DWORD); // コンストラクタ
      ~CDIB();            // デストラクタ
      LPBYTE getBMP();    // ビットマップバッファの先頭アドレスを返す
      void draw(HDC, DWORD, DWORD); // ビットマップ描画

  };

 上の関数のうち、getBMP()がビットマップバッファの先頭アドレスを保持するprivate変数lpBMPのアクセスメソッドです。

DIBオブジェクトの操作

 DIBを作る時に必要なのは、ビットマップ用のバッファとBITMAPINFO構造体でした。つまり、DIBオブジェクトを初期化するコンストラクタではバッファの確保とBITMAPINFO構造体(というより構造体のメンバであるBITMAPINFOHEADER構造体やカラーテーブル)を設定する事になります。
 今回のクラスでは、DIBの形式が決まっているのでコンストラクタの引数にはビットマップの大きさを指定するようにすれば良いでしょう。コンストラクタは、この引数をもとにDIBの大きさを設定しBITMAPINFO構造体を初期化、さらにビットマップバッファを確保します。また、今回は使いませんがDIBの4バイト境界に合わせてビットマップバッファの「幅」も計算しておきました。

  CDIB(DWORD wid,DWORD hei) { // コンストラクタ

      dwWidth=wid; // ビットマップの大きさ設定
      dwHeight=hei;

      if ((dwWidth*3) % 4==0) // バッファの1ラインの長さを計算
          dwLength=dwWidth*3;
      else
          dwLength=dwWidth*3+(4-(dwWidth*3) % 4);

      lpBMP= // ビットマップ用バッファ確保
        (LPBYTE)GlobalAlloc(GPTR,dwLength*dwHeight);

      bhDIB.biSize=sizeof(BITMAPINFOHEADER); // 構造体の大きさ
      bhDIB.biWidth=dwWidth;                 // 横幅
      bhDIB.biHeight=dwHeight;               // 高さ
      bhDIB.biPlanes=1;                      // プレーンの数
      bhDIB.biBitCount=24;                   // プレーンの色数
      bhDIB.biCompression=BI_RGB;
      bhDIB.biSizeImage=0;
      bhDIB.biXPelsPerMeter=0;
      bhDIB.biYPelsPerMeter=0;
      bhDIB.biClrUsed=0;
      bhDIB.biClrImportant=0;

      biDIB.bmiHeader=bhDIB;

  }

 DIBオブジェクトを作ったら、ビットマップに何か描いてみましょう。DIBのビットマップに描画するにはビットマップのバッファにデータを書き込むわけですが、そのバッファの先頭アドレスを返す関数がgetBMP()です。この関数を呼び出すとビットマップの先頭アドレスがLPBYTE型で返って来るので、後はバッファにデータを書き込んで行くだけ。

  LPBYTE getBMP() { // ビットマップバッファの先頭アドレスを返す

      return lpBMP;

  }

 例えば、DIBオブジェクトdibの(0,0)に白い点(RGBそれぞれ256)を打つなら、

  FillMemory(dib->getBMP(),3,255);

 として、バッファの先頭3バイトに255を書き込むわけです。

 ビットマップの描画は、GDIのStretchDIBitsを使います。描画関数では、引数にビットマップを描くデバイスコンテキスト(HDC)と描く座標を指定し、そこにビットマップ全体を描画するようにしましょう。

  void draw(HDC hdc,DWORD x,DWORD y) { // ビットマップ描画

      StretchDIBits(hdc,x,y,dwWidth,dwHeight,0,0,dwWidth,dwHeight,
        lpBMP,&biDIB,DIB_RGB_COLORS,SRCCOPY);

  }

 DIBではバッファを使用するので、使い終わったらDIBオブジェクトを破棄してデストラクタでバッファを解放する必要があります。

  ~CDIB() { // デストラクタ

      GlobalFree(lpBMP);

  }

 以上の事をまとめると、DIBクラスCDIBの全体は以下のようになります。

  class CDIB { // DIBクラス

  private:

      DWORD dwWidth,dwHeight,dwLength;
      LPBYTE lpBMP;
      BITMAPINFO biDIB;
      BITMAPINFOHEADER bhDIB;

  public:

      CDIB(DWORD wid,DWORD hei) { // コンストラクタ

          dwWidth=wid;
          dwHeight=hei;

          if ((dwWidth*3) % 4==0) // バッファの1ラインの長さを計算
              dwLength=dwWidth*3;
          else
              dwLength=dwWidth*3+(4-(dwWidth*3) % 4);

          lpBMP= // ビットマップ用バッファ確保
            (LPBYTE)GlobalAlloc(GPTR,dwLength*dwHeight);

          bhDIB.biSize=sizeof(BITMAPINFOHEADER); // 構造体の大きさ
          bhDIB.biWidth=dwWidth;                 // 横幅
          bhDIB.biHeight=dwHeight;               // 高さ
          bhDIB.biPlanes=1;                      // プレーンの数
          bhDIB.biBitCount=24;                   // プレーンの色数
          bhDIB.biCompression=BI_RGB;
          bhDIB.biSizeImage=0;
          bhDIB.biXPelsPerMeter=0;
          bhDIB.biYPelsPerMeter=0;
          bhDIB.biClrUsed=0;
          bhDIB.biClrImportant=0;

          biDIB.bmiHeader=bhDIB;

      }

      ~CDIB() { // デストラクタ

          GlobalFree(lpBMP);

      }

      LPBYTE getBMP() { // ビットマップバッファの先頭アドレスを返す

          return lpBMP;

      }

      void draw(HDC hdc,DWORD x,DWORD y) { // ビットマップ描画

          StretchDIBits(hdc,x,y,dwWidth,dwHeight,0,0,dwWidth,dwHeight,
            lpBMP,&biDIB,DIB_RGB_COLORS,SRCCOPY);

      }

  };

DIBクラスの使い方

 DIBクラスCDIBを使うには、まずCDIB型のポインタを用意します。このポインタは、DIBクラスのインスタンス(DIBオブジェクト)を操作する時に必要になるので、グローバル変数にしておいた方が良いでしょう。このポインタに、new演算子を使って作成したDIBオブジェクトを割り当てます(コンストラクタの引数は、ビットマップの大きさ)。
 例えば、256×256のビットマップを持つDIBオブジェクトdibを作るなら以下のような感じです。

  CDIB* dib;

  dib=new CDIB(256,256); // DIBオブジェクト作成

 DIBオブジェクトを作ったら、ビットマップのアドレスを取得してビットマップに何か描いてみましょう。今回のプログラムでは、256段階のグレースケールを描いてみました。

  for (int i=0;i<256;i++) // ビットマップにグレースケール描画
      FillMemory(dib->getBMP()+i*256*3,256*3,i);

 次に、draw()でビットマップを描画します。描画する時には、描画対象のデバイスコンテキストが必要なので、ウインドウのWM_PAINTメッセージの処理でデバイスコンテキストを取得しこのデバイスコンテキストに描いてみました。これで、ウインドウにビットマップを表示できます。

  case WM_PAINT:

      hdc=BeginPaint(hwnd,&ps); // ウインドウのデバイスコンテキスト取得

      dib->draw(hdc,4,4); // hdcの(4, 4)にビットマップ描画

      EndPaint(hwnd,&ps);

      break;

 DIBオブジェクトを使い終わったら、deleteで削除してビットマップバッファを解放しておきましょう。

  delete dib; // DIBオブジェクト削除

プログラム

 今回のプログラムは、256×256のDIBオブジェクトを作成しグレースケールを描いた後で表示します。

 プログラムをコンパイルする時には、ソースの拡張子をcppにしてください。

プログラムソース表示

戻る