2011年4月22日金曜日

DIB(9) - 回転描画


 画像を回転させるプログラム。
 

 転送先ピクセルがどのピクセルを参照するかを計算しなければなりません。
そのとき回転方向は45°でも参照するときは-45°の方向へ参照位置をずらしていく必要があります。
 今回は指定範囲外への描画はしないようにしています。
きっちり四角形に描画する場合は回転角度に合わせて描画範囲を拡大、描画開始位置も修正すればOKかと。
 

 
■サンプル

#include <windows.h>
#include <cmath>
#include <algorithm>
const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
 
 
 
class DIB32
{
public:
  DIB32()
    : m_pixel( NULL )
  {
    ZeroMemory( &m_bmi, sizeof(m_bmi) );
  }
 
  virtual ~DIB32()
  {
    release();
  }
 
  virtual 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;
    //32bit固定にする
    m_bmi.bmiHeader.biBitCount = 32;
    m_bmi.bmiHeader.biCompression = BI_RGB;
    //96dpiだと3780らしい。0の場合もあるとのこと
    m_bmi.bmiHeader.biXPelsPerMeter = 3780;
    m_bmi.bmiHeader.biYPelsPerMeter = 3780;
 
    return true;
  }
 
  /** create from HBITMAP */
  bool create( LPCTSTR fileName )
  {
    HBITMAP hBmp = static_cast<HBITMAP>( LoadImage( NULL, fileName, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE ) );
    if ( !hBmp ) return false;
 
    BITMAP bm = {0};
    GetObject( hBmp, sizeof(BITMAP), &bm );
 
    // create DC
    HDC hdc = CreateCompatibleDC( NULL );
    if ( !hdc ) return false;
 
    // create buf
    if ( !create( bm.bmWidth, bm.bmHeight ) )
    {
      DeleteDC( hdc );
      return false;
    }
 
    // copy
    GetDIBits( hdc, hBmp, 0, bm.bmHeight, m_pixel, &m_bmi, DIB_RGB_COLORS );
 
    DeleteDC( hdc );
    DeleteObject( hBmp );
 
    return true;
  }
 
  virtual void release()
  {
    if ( m_pixel )
    {
      delete [] m_pixel;
      m_pixel = NULL;
    }
    ZeroMemory( &m_bmi, sizeof(m_bmi) );
  }
 
  bool render( HDC hdc, HWND hWnd ) const
  {
    RECT rect;
    GetClientRect( hWnd, &rect );
    return GDI_ERROR != StretchDIBits(
      hdc, 0, 0, rect.right, rect.bottom,
      0, 0, getWidth(), getHeight(),
      m_pixel, &m_bmi, DIB_RGB_COLORS, SRCCOPY );
  }
 
 
 
  /** rotate */
  bool rotate( DIB32& dest,
    LONG destX, LONG destY, LONG destWidth, LONG destHeight,
    LONG srcX, LONG srcY, LONG srcWidth, LONG srcHeight,
    float angle ) const
  {
    // 反転表示不可
    destWidth = abs( destWidth );
    destHeight = abs( destHeight );
    srcWidth = abs( srcWidth );
    srcHeight = abs( srcHeight );
 
    LONG destStartX = destX;
    LONG destStartY = destY;
    LONG destEndX = destX + destWidth;
    LONG destEndY = destY + destHeight;
 
    LONG srcStartX = srcX;
    LONG srcStartY = srcY;
    LONG srcEndX = srcX + srcWidth;
    LONG srcEndY = srcY + srcHeight;
    if ( srcStartX < 0 ) srcStartX = 0;
    if ( srcEndX > getWidth() ) srcEndX = getWidth();
    if ( srcStartY < 0 ) srcStartY = 0;
    if ( srcEndY > getHeight() ) srcEndY = getHeight();
 
    const float fCos = std::cos( -angle / 180 * 3.1415926535897932384626f );
    const float fSin = std::sin( -angle / 180 * 3.1415926535897932384626f );
    const float fCos90 = std::cos( (-angle + 90) / 180 * 3.1415926535897932384626f );
    const float fSin90 = std::sin( (-angle + 90) / 180 * 3.1415926535897932384626f );
    const float srcWidthHalf = static_cast<float>(srcWidth) / 2;
    const float srcHeightHalf = static_cast<float>(srcHeight) / 2;
    float fSrcStartX = static_cast<float>(srcX) + srcWidthHalf + (-srcWidthHalf)*fCos - (-srcHeightHalf)*fSin;
    float fSrcStartY = static_cast<float>(srcY) + srcHeightHalf + (-srcWidthHalf)*fSin + (-srcHeightHalf)*fCos;
 
    const float ratioX = static_cast<float>(srcWidth) / static_cast<float>(destWidth);
    const float ratioY = static_cast<float>(srcHeight) / static_cast<float>(destHeight);
    const float addSrcStartX = fCos * ratioX;
    const float addSrcStartY = fSin * ratioY;
    const float addSrcX = fCos90 * ratioX;
    const float addSrcY = fSin90 * ratioY;
 
    // cliping
    if ( destStartX < 0 )
    {
      fSrcStartX += (static_cast<float>(-destStartX) * addSrcStartX);
      fSrcStartY += (static_cast<float>(-destStartX) * addSrcStartY);
      destStartX = 0;
    }
    if ( destStartY < 0 )
    {
      fSrcStartX += (static_cast<float>(-destStartY) * addSrcX);
      fSrcStartY += (static_cast<float>(-destStartY) * addSrcY);
      destStartY = 0;
    }
    if ( destEndX >= dest.getWidth() ) destEndX = dest.getWidth()-1;
    if ( destEndY >= dest.getHeight() ) destEndY = dest.getHeight()-1;
    
 
    float fSrcX = fSrcStartX;
    float fSrcY = fSrcStartY;
    LPDWORD destLineAddr = dest.getPixelAddr( destStartX, destStartY );
    for (LONG y=destStartY; y<destEndY; ++y)
    {
      float srcLineX = fSrcX;
      float srcLineY = fSrcY;
      LPDWORD destAddr = destLineAddr;
      for (LONG x=destStartX; x<destEndX; ++x)
      {
        const LONG sx = static_cast<LONG>( srcLineX );
        const LONG sy = static_cast<LONG>( srcLineY );
        if ( sx >= srcStartX && sx < srcEndX && sy >= srcStartY && sy < srcEndY )
        {
          *destAddr = *getPixelAddr(sx, sy);
        }
 
        destAddr++;
        srcLineX += addSrcStartX;
        srcLineY += addSrcStartY;
      }
 
      fSrcX += addSrcX;
      fSrcY += addSrcY;
      destLineAddr -= dest.getWidth();
    }
 
    return true;
  }
 
  
  LONG getWidth() const { return m_bmi.bmiHeader.biWidth; }
  LONG getHeight() const { return m_bmi.bmiHeader.biHeight; }
  const LPDWORD getPixelAddr( LONG x, LONG y ) const { return m_pixel + ((getHeight()-1)-y)*getWidth() + x; }
  LPDWORD getPixelAddr( LONG x, LONG y ) { return const_cast<LPDWORD>(static_cast<const DIB32&>(*this).getPixelAddr(x, y)); }
 
protected:
  LPDWORD m_pixel;
  BITMAPINFO m_bmi;
};
 
 
 
 
 
LRESULT CALLBACK wndProc(
  HWND hWnd,
  UINT msg,
  WPARAM wParam,
  LPARAM lParam )
{
  static DIB32 back, image;
  switch (msg)
  {
  case WM_DESTROY:
    ShowWindow( hWnd, SW_HIDE );
    PostQuitMessage(0);
    break;
  case WM_CREATE:
    back.create( WINDOW_WIDTH, WINDOW_HEIGHT );
    image.create( TEXT("image.bmp") );
    image.rotate( back, 100, 100, 100, 100, 0, 0, 32, 32, 0 );
    image.rotate( back, 200, 100, 300, 300, 0, 0, 32, 32, 60 );
    break;
  case WM_PAINT:
    {
      PAINTSTRUCT ps = {0};
      HDC hdc = BeginPaint( hWnd, &ps );
      back.render( hdc, hWnd );
      EndPaint( hWnd, &ps );
    }
    break;
  default:
    return DefWindowProc( hWnd, msg, wParam, lParam );
  }
 
  return 0;
}
 
 
 
 
int WINAPI WinMain(
  HINSTANCE hInstance,
  HINSTANCE, PSTR, int )
{
  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 = WINDOW_WIDTH
    + GetSystemMetrics(SM_CXEDGE)
    + GetSystemMetrics(SM_CXBORDER)
    + GetSystemMetrics(SM_CXDLGFRAME);
  LONG winHeight = WINDOW_HEIGHT
    + 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;
}


■関連記事:
生産がす: 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 件のコメント: