2010年1月17日日曜日

32bitDIB(1) 作成と破棄

 とくに32bitである意味はないですが、扱いやすさから32bitを。
ポインタを++したら次のピクセルってあたりが直感的で良いですね。
その前にとりあえずウィンドウを適当に表示するところから。


#include <windows.h>
 
LRESULT CALLBACK wndProc(
  HWND hWnd,
  UINT msg,
  WPARAM wParam,
  LPARAM lParam )
{
  switch (msg)
  {
  case WM_DESTROY:
    ShowWindow( hWnd, SW_HIDE );
    PostQuitMessage(0);
    break;
  case WM_CREATE:
    break;
  case WM_PAINT:
    {
      PAINTSTRUCT ps = {0};
      HDC hdc = BeginPaint( hWnd, &ps );
      EndPaint( hWnd, &ps );
    }
    break;
  default:
    return DefWindowProc( hWnd, msg, wParam, lParam );
  }
 
  return 0;
}
 
 
int WINAPI WinMain(
  HINSTANCE hInstance,
  HINSTANCE hPrevInstance,
  PSTR lpCmdLine,
  int nCmdShow )
{
  LPCTSTR WINDOW_NAME = TEXT("sample");
 
  WNDCLASSEX wc;
  wc.style    = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc  = reinterpret_cast<WNDPROC>( wndProc );
  wc.cbClsExtra  = 0;
  wc.cbWndExtra  = 0;
  wc.cbSize    = sizeof( WNDCLASSEX );
  wc.hInstance  = hInstance;
  wc.hIcon    = NULL;
  wc.hIconSm    = NULL;
  wc.hCursor    = LoadCursor( NULL, IDC_ARROW );
  wc.hbrBackground= reinterpret_cast<HBRUSH>( GetStockObject(WHITE_BRUSH) );
  wc.lpszMenuName  = NULL;
  wc.lpszClassName= WINDOW_NAME;
  if ( !RegisterClassEx(&wc) ) return 0;
 
  LONG winWidth = 640
    + GetSystemMetrics(SM_CXEDGE)
    + GetSystemMetrics(SM_CXBORDER)
    + GetSystemMetrics(SM_CXDLGFRAME);
  LONG winHeight = 480
    + GetSystemMetrics(SM_CYEDGE)
    + GetSystemMetrics(SM_CYBORDER)
    + GetSystemMetrics(SM_CYDLGFRAME)
    + GetSystemMetrics(SM_CYCAPTION);
  HWND hWnd = CreateWindowEx(
    0, WINDOW_NAME, NULL,
    WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME,
    CW_USEDEFAULT, CW_USEDEFAULT,
    winWidth, winHeight,
    NULL, NULL, hInstance, NULL);
  if ( !hWnd ) return -1;
 
  ShowWindow( hWnd, SW_SHOWNORMAL );
  UpdateWindow( hWnd );
 
  MSG msg;
  for (;;)
  {
    if ( !GetMessage(&msg, NULL, 0, 0) ) break;
    TranslateMessage( &msg );
    DispatchMessage( &msg );
  }
 
  UnregisterClass( WINDOW_NAME, hInstance );
  return msg.wParam;
}


 クライアント領域が640x480のなんもないウィンドウが出てきたところで本題。


■初期化処理、終了処理
 DIBはメモリを確保してパラメータをちょちょいと設定してやればすぐ使えます。
扱いやすくクラスにしています。

class DIB32
{
public:
  DIB32()
    : m_pixel( NULL )
  {
    ZeroMemory( &m_bmi, sizeof(m_bmi) );
  }
 
  ~DIB32()
  {
    release();
  }
 
  bool create( LONG width, LONG height )
  {
    if ( width <= 0 || height <= 0 )
    {
      return false;
    }
    release();
 
    m_pixel = new DWORD[ width * height ];
    if ( !m_pixel ) return false;
 
    m_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    m_bmi.bmiHeader.biWidth = width;
    m_bmi.bmiHeader.biHeight = height;
    m_bmi.bmiHeader.biPlanes = 1;
    m_bmi.bmiHeader.biBitCount = 32;  //32bit固定にする
    m_bmi.bmiHeader.biCompression = BI_RGB;
    m_bmi.bmiHeader.biXPelsPerMeter = 3780;  //96dpiだと3780らしい。0の場合もあるとのこと
    m_bmi.bmiHeader.biYPelsPerMeter = 3780;
 
    return true;
  }
 
  void release()
  {
    if ( m_pixel )
    {
      delete [] m_pixel;
      m_pixel = NULL;
    }
    ZeroMemory( &m_bmi, sizeof(m_bmi) );
  }
private:
  LPDWORD m_pixel;
  BITMAPINFO m_bmi;
};

 生成処理のcreate()と解放処理のrelease()、あとコンストラクタとデストラクタのみのクラスです。
create()にてm_pixelにピクセルデータ用のメモリを、m_bmiにDIB情報をセットします。
release()ではピクセルデータの解放をするぐらいです。
 32bitDIB限定なので、BITMAPINFOでなく横幅と高さだけ情報があればどうにでもなるんですが、レンダリングするときにBITMAPINFOがあると楽なのでBITMAPINFOを使用しています。


■レンダリング
 HDCにDIBを描画するときはStretchDIBitsあたりを使うと楽チンです。拡大縮小もばっちり。

#include <windows.h>
 
class DIB32
{
public:
  DIB32()
    : m_pixel( NULL )
  {
    ZeroMemory( &m_bmi, sizeof(m_bmi) );
  }
 
  ~DIB32()
  {
    release();
  }
 
  bool create( LONG width, LONG height )
  {
    if ( width <= 0 || height <= 0 )
    {
      return false;
    }
    release();

    m_pixel = new DWORD[ width * height ];
    if ( !m_pixel ) return false;

    m_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    m_bmi.bmiHeader.biWidth = width;
    m_bmi.bmiHeader.biHeight = height;
    m_bmi.bmiHeader.biPlanes = 1;
    m_bmi.bmiHeader.biBitCount = 32;  //32bit固定にする
    m_bmi.bmiHeader.biCompression = BI_RGB;
    m_bmi.bmiHeader.biXPelsPerMeter = 3780;  //96dpiだと3780らしい。0の場合もあるとのこと
    m_bmi.bmiHeader.biYPelsPerMeter = 3780;

    return true;
  }
 
  void release()
  {
    if ( m_pixel )
    {
      delete [] m_pixel;
      m_pixel = NULL;
    }
    ZeroMemory( &m_bmi, sizeof(m_bmi) );
  }
 
  bool render(
    HDC hdc,
    LONG destX, LONG destY,
    LONG destWidth, LONG destHeight,
    LONG srcX, LONG srcY,
    LONG srcWidth, LONG srcHeight ) const
  {
    return GDI_ERROR != StretchDIBits(
     hdc, destX, destY, destWidth, destHeight,
     srcX, srcY, srcWidth, srcHeight,
     m_pixel, &m_bmi, DIB_RGB_COLORS, SRCCOPY );
  }
 
  LONG getWidth() const { return m_bmi.bmiHeader.biWidth; }
  LONG getHeight() const { return m_bmi.bmiHeader.biHeight; }
 
private:
  LPDWORD m_pixel;
  BITMAPINFO m_bmi;
};
 
 
LRESULT CALLBACK wndProc(
  HWND hWnd,
  UINT msg,
  WPARAM wParam,
  LPARAM lParam )
{
  static DIB32 image;
  switch (msg)
  {
  case WM_DESTROY:
    ShowWindow( hWnd, SW_HIDE );
    PostQuitMessage(0);
    break;
  case WM_CREATE:
    image.create( 320, 240 );
    break;
  case WM_PAINT:
    {
      PAINTSTRUCT ps = {0};
      HDC hdc = BeginPaint( hWnd, &ps );
      image.render( hdc );
      EndPaint( hWnd, &ps );
    }
    break;
  default:
    return DefWindowProc( hWnd, msg, wParam, lParam );
  }
 
  return 0;
}
 
 
int WINAPI WinMain(
  HINSTANCE hInstance,
  HINSTANCE hPrevInstance,
  PSTR lpCmdLine,
  int nCmdShow )
{
  LPCTSTR WINDOW_NAME = TEXT("sample");
 
  WNDCLASSEX wc;
  wc.style    = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc  = reinterpret_cast<WNDPROC>( wndProc );
  wc.cbClsExtra  = 0;
  wc.cbWndExtra  = 0;
  wc.cbSize    = sizeof( WNDCLASSEX );
  wc.hInstance  = hInstance;
  wc.hIcon    = NULL;
  wc.hIconSm    = NULL;
  wc.hCursor    = LoadCursor( NULL, IDC_ARROW );
  wc.hbrBackground= reinterpret_cast<HBRUSH>( GetStockObject(WHITE_BRUSH) );
  wc.lpszMenuName  = NULL;
  wc.lpszClassName= WINDOW_NAME;
  if ( !RegisterClassEx(&wc) ) return 0;
 
  LONG winWidth = 640
    + GetSystemMetrics(SM_CXEDGE)
    + GetSystemMetrics(SM_CXBORDER)
    + GetSystemMetrics(SM_CXDLGFRAME);
  LONG winHeight = 480
    + GetSystemMetrics(SM_CYEDGE)
    + GetSystemMetrics(SM_CYBORDER)
    + GetSystemMetrics(SM_CYDLGFRAME)
    + GetSystemMetrics(SM_CYCAPTION);
  HWND hWnd = CreateWindowEx(
    0, WINDOW_NAME, NULL,
    WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME,
    CW_USEDEFAULT, CW_USEDEFAULT,
    winWidth, winHeight,
    NULL, NULL, hInstance, NULL);
  if ( !hWnd ) return -1;
 
  ShowWindow( hWnd, SW_SHOWNORMAL );
  UpdateWindow( hWnd );
 
  MSG msg;
  for (;;)
  {
    if ( !GetMessage(&msg, NULL, 0, 0) ) break;
    TranslateMessage( &msg );
    DispatchMessage( &msg );
  }
 
   
  UnregisterClass( WINDOW_NAME, hInstance );
  return msg.wParam;
}



 確保したメモリを初期化してないので変な色がでてますが、仕様です。
さらっとgetWidth()、getHeight()を追加しています。
ウィンドいっぱいにレンダリングしたいときは

bool render( HWND hWnd, HDC hdc ) const
{
  RECT rect;
  if ( GetClientRect( hWnd, &rect ) )
  {
    return render( hdc,
      0, 0, rect.right, rect.bottom,
      0, 0, getWidth(), getHeight() );
  }
 
  return false;
}

 といった関数を用意すると便利です。
■関連記事:
生産がす: 32bitDIB(1) 作成と破棄
生産がす: 32bitDIB(2) 画像読み込み
生産がす: 32bitDIB(3) 塗りつぶし
生産がす: 32bitDIB(4) DIBに描画
生産がす: 32bitDIB(5) ファイルに出力
生産がす: 32bitDIB(6) 拡大縮小描画
生産がす: 32bitDIB(7) DIBSection
生産がす: DIB(8) - 直線描画
生産がす: DIB(9) - 回転描画
生産がす: DIB(10) - 三角形描画
生産がす: 日記ちゃん半透明合成
生産がす: 32bitDIBから無圧縮AVI2.0

0 件のコメント: