2010年1月18日月曜日

32bitDIB(6) 拡大縮小描画

■拡大縮小レンダリング
 いままでは*destPixel++ = *srcPixel++;でしたが、

float addX = srcWidth / destWidth;
float sx = srcX;
for (LONG x=0; x<width; ++x)
{
  *destPixel++ = srcPixel[ static_cast<int>(sx) ];
  sx += addX;
}

 みたいに不動小数点数を使うと手軽に実装できます。
固定小数点数でもブレゼンハムでもいけますが速度はどっこいどっこい、心持ち浮動小数点のほうが遅いです。やむなし。


 クリッピングが面倒ですが、参照位置や参照画像サイズを描画倍率にあわせることでいままでのクリッピング関数を使いまわすことが出来ます。
クリッピング後に元に直せば、必要な部分だけクリッピングされた領域が計算されている状態になります。

 うまくクリッピングされていればループ中にif文がいらないのでそこそこ高速に処理できます。

bool render(
  DIB32& dest,
  LONG destX, LONG destY, LONG destWidth, LONG destHeight,
  LONG srcX, LONG srcY, LONG srcWidth, LONG srcHeight ) const
{
  if ( !m_pixel || !dest.m_pixel ) return false;
 
  const float magniX = static_cast<float>(destWidth) / static_cast<float>(srcWidth);
  const float magniY = static_cast<float>(destHeight) / static_cast<float>(srcHeight);
  const LONG sw = static_cast<LONG>( static_cast<float>(getWidth()) * magniX );
  const LONG sh = static_cast<LONG>( static_cast<float>(getHeight()) * magniY );
  LONG sx = static_cast<LONG>( static_cast<float>(srcX) * magniX );
  LONG sy = static_cast<LONG>( static_cast<float>(srcY) * magniY );
 
  if ( cliping( destX, destY, destWidth, destHeight, sx, sy, dest.getWidth(), dest.getHeight(), sw, sh ) )
  {
    const float addX = 1.f / magniX;
    const float addY = 1.f / magniY;
    float fsy = static_cast<float>( sy ) / magniY;
    LPDWORD destLine = dest.m_pixel
      + ((dest.getHeight()-1)-destY) * dest.getWidth()
      + destX;
 
    for (LONG y=0; y<destHeight; ++y)
    {
      LPDWORD destPixel = destLine;
      LPDWORD srcLine = m_pixel
        + ((getHeight()-1)-static_cast<LONG>(fsy)) * getWidth();
      float fsx = static_cast<float>( sx ) / magniY;
      for (LONG x=0; x<destWidth; ++x)
      {
        *destPixel++ = srcLine[ static_cast<LONG>(fsx) ];
        fsx += addX;
      }
 
      fsy += addY;
      destLine -= dest.getWidth();
    }
 
    return true;
  }
 
  return false;
}





●サンプル
http://cid-8cd7cf5ea9fbca55.skydrive.live.com/self.aspx/Public/program/Sample6.cpp


#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;
  //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;
 }
 
 bool create( LPCTSTR bitmapFile )
 {
  if ( !bitmapFile ) return false;
 
  HBITMAP hBmp = static_cast<HBITMAP>(
   LoadImage(NULL, bitmapFile, 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 )
  {
   DeleteDC( hdc );
   DeleteObject( hBmp );
   return false;
  }
 
  // create buf
  if ( !create( bm.bmWidth, bm.bmHeight ) )
  {
   DeleteDC( hdc );
   DeleteObject( hBmp );
   return false;
  }
 
  // copy
  GetDIBits( hdc, hBmp, 0, bm.bmHeight,m_pixel, &m_bmi,
   DIB_RGB_COLORS );
 
  DeleteDC( hdc );
  DeleteObject( hBmp );
 
  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 );
 }
 
 
 
 bool render(
  DIB32& dest,
  LONG destX, LONG destY, LONG destWidth, LONG destHeight,
  LONG srcX, LONG srcY, LONG srcWidth, LONG srcHeight ) const
 {
  if ( !m_pixel || !dest.m_pixel ) return false;
 
  const float magniX = static_cast<float>(destWidth) / static_cast<float>(srcWidth);
  const float magniY = static_cast<float>(destHeight) / static_cast<float>(srcHeight);
  const LONG sw = static_cast<LONG>( static_cast<float>(getWidth()) * magniX );
  const LONG sh = static_cast<LONG>( static_cast<float>(getHeight()) * magniY );
  LONG sx = static_cast<LONG>( static_cast<float>(srcX) * magniX );
  LONG sy = static_cast<LONG>( static_cast<float>(srcY) * magniY );
 
  if ( cliping( destX, destY, destWidth, destHeight, sx, sy, dest.getWidth(), dest.getHeight(), sw, sh ) )
  {
   const float addX = 1.f / magniX;
   const float addY = 1.f / magniY;
   float fsy = static_cast<float>( sy ) / magniY;
   LPDWORD destLine = dest.m_pixel
    + ((dest.getHeight()-1)-destY) * dest.getWidth()
    + destX;
 
   for (LONG y=0; y<destHeight; ++y)
   {
    LPDWORD destPixel = destLine;
    LPDWORD srcLine = m_pixel
     + ((getHeight()-1)-static_cast<LONG>(fsy)) * getWidth();
    float fsx = static_cast<float>( sx ) / magniY;
    for (LONG x=0; x<destWidth; ++x)
    {
     *destPixel++ = srcLine[ static_cast<LONG>(fsx) ];
     fsx += addX;
    }
 
    fsy += addY;
    destLine -= dest.getWidth();
   }
 
   return true;
  }
 
  return false;
 }
 
 
 LONG getWidth() const { return m_bmi.bmiHeader.biWidth; }
 LONG getHeight() const { return m_bmi.bmiHeader.biHeight; }
 
 static bool cliping(
  LONG& destX, LONG& destY,
  LONG& width, LONG& height,
  LONG& srcX, LONG& srcY,
  const LONG destWidth, const LONG destHeight,
  const LONG srcWidth, const LONG srcHeight )
 {
  // 左端クリッピング
  if ( destX < 0 ) { width += destX; srcX -= destX; destX = 0; }
  if ( srcX < 0 ) { width += srcX; destX -= srcX; srcX = 0; }
 
  // 右端
  if ( destX + width > destWidth ) { width -= ((destX+width)-destWidth); }
  if ( srcX + width > srcWidth ) { width -= ((srcX+width)-srcWidth); }
 
  // 上
  if ( destY < 0 ) { height += destY; srcY -= destY; destY = 0; }
  if ( srcY < 0 ) { height += srcY; destY -= srcY; srcY = 0; }
 
  // 下
  if ( destY + height > destHeight ) { height -= ((destY+height)-destHeight); }
  if ( srcY + height > srcHeight ) { height -= ((srcY+height)-srcHeight); }
 
  return ((width > 0) & (height > 0));
 }
 
private:
 LPDWORD m_pixel;
 BITMAPINFO m_bmi;
};
 
 
 
 
LRESULT CALLBACK wndProc(
 HWND hWnd,
 UINT msg,
 WPARAM wParam,
 LPARAM lParam )
{
 static DIB32 image;
 static DIB32 image2;
 switch (msg)
 {
 case WM_DESTROY:
  ShowWindow( hWnd, SW_HIDE );
  PostQuitMessage(0);
  break;
 case WM_CREATE:
  image.create( TEXT("image.bmp") );
  image2.create( TEXT("image2.bmp") );
  break;
 case WM_PAINT:
  {
   PAINTSTRUCT ps = {0};
   HDC hdc = BeginPaint( hWnd, &ps );
   image2.render( image, -100, -50, image2.getWidth()*2, image2.getHeight()*2, 0, 0, image2.getWidth(), image2.getHeight() );
   image.render(
    hdc, 10, 10, image.getWidth(), image.getHeight(),
    0, 0, image.getWidth(), image.getHeight() );
   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;
}

 
 
 


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