2011年4月29日金曜日

にっきちゃん


 ノートPCでは規定速度で動かなかったが、デスクトップでも動かない。
どうも根本的に良くないプログラムなんだろう。
頂点をvectorに溜め込んで一気にDrawPrimitiveしてるんだったかだがこれではいかんらしい。
vectorじゃだめかなと思って、
一行変えるだけでIndexBufferを使えるようにしてた覚えがあるので試してみたけど即効落ちた。
わかんねえー。
 
 最終のデータがどれかわかんないや。
生産がす: にっきty
これかな? 2年も前だ……。
 
 「ホワイトサワー味をしみこませました」ってなお菓子が手元にあるんだけど、
ホワイトサワーって何味だ? 
 ホワイトサワーで調べるとパピコが、
サワーホワイトで調べるとアンバサが大半を占めているなか、
ホワイトサワーパンというのがひょっこり出てきて、
材料を見ると乳酸菌飲料を使っていた。
 ホワイトサワーは乳酸菌飲料味なのだろうか。
いわれてみれば近い気もするが。

2011年4月26日火曜日

aifToWav


Download
size: 37.3KB



■概要
aiffファイルをwavファイルに変換します。


■使い方
aifToWav.exeにaifファイルをドロップすると拡張子を.wavにして出力されます。

8bit, 16bit, モノラル(1ch), ステレオ(2ch), 8kHz, 11.025kHz, 22.05KHz, 44.1KHz
あたりのオーソドックスなファイルはテストしましたが、
ちょっとでも特殊なaiffフォーマットだとうまく変換できない可能性があります。
ご了承ください。



■動作環境
Windows XP
CPU 800MHz以上
メモリ 256MB以上



■アンインストール
レジストリは触っておりません。
削除の際はフォルダごとゴミ箱へどうぞ。



■そのほか
ソースファイルを同封しています。参考にどうぞ。

日記ちゃんaiffをwavに変換

 aiffファイルをwavファイルに変換できたー。
というわけで半年かけてみしょんこんぷりーと。
もう、ツールを作る意味もないから作るだけ作って放置だけど。
 
 ふと思いついてデータ部分をエンディアン変えずに再生したらどうなるかなあとおもって試してみた。
test110426.ogg
fileSize: 921KB
 !後半音量注意
 前半が通常、後半がエンディアンミスってるデータ。
44100hz,16bit
ノイズまみれであまり聞けたもんじゃなくなったが、
こんなになるのかと思うと少し面白い。
 

2011年4月24日日曜日

構造体パディング


struct COMMChunk
{
  DWORD ckID;
  long ckSize;
  short numChannels;
  unsigned long numSampleFrames;
...
};

 とかやるんだけどパディングはいってずれちゃうからしかたなしに

struct COMMChunk
{
  BYTE COMM[4];
  DWORD ckSizeCOMM;
  BYTE numChannels[2];
  BYTE numSampleFrames[4];
...
};

 とかやるけど、これはこれで鬱陶しい……。
なんかいい方法ないのドラえもーん?
 
 #pragma pack
っつーのでアライメント変更できるみたい。

#pragma pack(push, 2)
struct COMMChunk
{
  DWORD COMM;
  long ckSizeCOMM;
  short numChannels;
  unsigned long numSampleFrames;
  short sampleSize;
  BYTE sampleRate[ 10 ];
};
#pragma pack(pop)

 としたらうまくできた。

2011年4月23日土曜日

Bloggerでc++のソースをきれいに表示したい

 調べたらさくっとできたがいくらか引っかかる点があったのでその辺を踏まえた手順を。
まず
クリボウの Blogger Tips: コードをハイライトする「Code Prettify」ウィジェット
にてガジェットを追加する。

 
 
 
 Firefoxのプラグイン「NoScript」が反応してうまく追加できないので、

 なんとかして追加する。
自分の場合「安全でない再読み込み」を実行するとうまくいった。
 
 
 

 追加が成功していればガジェットが増える。
 
 
 
 あとはソースを表示するときに、

<pre class="prettyprint">
 とかやればOK。
overflor: auto;とかwidth: ???pxとかheight: ???pxとかで
適当に制限してやると小奇麗。
 それからソースコードは<や>は&lt;、&gt;で置換しないと表示されないし、
タブも&nbsp;などでスペースに変換しないと表示できない。
 
goodluck.

DIB(10) - 三角形描画


 三角形描画。
三角形ポリゴンのほうが通じそうだけど、個人的にはなんか違うかなと。
 

 まず設定された3点を、yでソートします。上からp1, p2, p3とします。
yが1進んだときのxの変化量を、p1からp2、p1からp3、p2からp3のそれぞれ求めます。
式は(p2.x-p1.x)/(p2.y-p1.y)などになります。
u,vも始点の座標と終点の座標を計算するための変化量を記録しておきます。
 後はレンダリングしていくだけですが、
始点をp1として、p2方向とp3方向のそれぞれにパラメータを変化させ、
描画開始点xと終了点xを決定。
その間のu,vも始点と終点を計算して1ドットずつ描画していきます。
終わったら次の行へ、を繰り返す。
 p1->p2間が完了したら、p2->p3用にパラメータを設定しなおして
また同じように一行ずつ処理していけばp3にたどり着くころには描画完了しているって流れになります。
 
 
 
■サンプル
 描画先の始点と終点決定をtriangle()
与えられた始点終点から実際に描画するのをrenderLine()で行っています。

#include <windows.h>
#include <cmath>
#include <algorithm>
const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
 
 
 
class DIB32
{
public:
  /**
   * Point
   */
  struct Point
  {
    LONG x, y;
    LONG u, v;
 
    void set( LONG x, LONG y, LONG u, LONG v )
    {
      this->x = x;
      this->y = y;
      this->u = u;
      this->v = v;
    }
  };
 
 
 
 
  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;
    m_bmi.bmiHeader.biBitCount = 32;
    m_bmi.bmiHeader.biCompression = BI_RGB;
 
    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 );
  }
 
 
 
  
  inline void renderLine(
    LPDWORD destLine,
    LONG destMaxWidth,
    LONG startX, LONG endX,
    float startU, float startV,
    float endU, float endV )
  {
    if ( startX > endX )
    {
      std::swap( startX, endX );
      std::swap( startU, endU );
      std::swap( startV, endV );
    }
 
    if ( startX > destMaxWidth || endX < 0 || (endX-startX)==0 ) return;
 
    const float addU = (endU-startU) / static_cast<float>(endX - startX);
    const float addV = (endV-startV) / static_cast<float>(endX - startX);
 
    // cliping-left
    if ( startX < 0 )
    {
      startU += (addU * static_cast<float>(-startX));
      startV += (addV * static_cast<float>(-startX));
      startX = 0;
    }
    // cliping-right
    if ( endX > destMaxWidth )
    {
      endX = destMaxWidth;
    }
 
    float u = startU;
    float v = startV;
    for (LONG x=startX; x<endX; x++)
    {      
      *(destLine + x) = getPixel(static_cast<LONG>(u), static_cast<LONG>(v));
 
      u += addU;
      v += addV;
    }
  }
 
  bool triangle( DIB32& dest, const Point& point1, const Point& point2, const Point& point3 )
  {
    Point p1( point1 ), p2( point2 ), p3( point3 );
 
    // 手動ソート
    if ( p1.y > p2.y ) std::swap( p1, p2 );
    if ( p1.y > p3.y ) std::swap( p1, p3 );
    if ( p2.y > p3.y ) std::swap( p2, p3 );
 
    const float height1_2 = static_cast<float>( p2.y-p1.y ? p2.y-p1.y : 1 );
    const float height1_3 = static_cast<float>( p3.y-p1.y ? p3.y-p1.y : 1 );
    const float height2_3 = static_cast<float>( p3.y-p2.y ? p3.y-p2.y : 1 );
    
    const float destAddX1_2 = static_cast<float>(p2.x-p1.x) / height1_2;
    const float destAddX1_3 = static_cast<float>(p3.x-p1.x) / height1_3;
    const float destAddX2_3 = static_cast<float>(p3.x-p2.x) / height2_3;
    
    const float addU1_2 = static_cast<float>(p2.u-p1.u) / height1_2;
    const float addU1_3 = static_cast<float>(p3.u-p1.u) / height1_3;
    const float addU2_3 = static_cast<float>(p3.u-p2.u) / height2_3;
    const float addV1_2 = static_cast<float>(p2.v-p1.v) / height1_2;
    const float addV1_3 = static_cast<float>(p3.v-p1.v) / height1_3;
    const float addV2_3 = static_cast<float>(p3.v-p2.v) / height2_3;
    
    // 1~2
    float startX = static_cast<float>( p1.x );
    float endX = static_cast<float>( p1.x );
    float startU = static_cast<float>( p1.u );
    float startV = static_cast<float>( p1.v );
    float endU = static_cast<float>( p1.u );
    float endV = static_cast<float>( p1.v );
    LPDWORD destLine = dest.getPixelAddr( 0, p1.y );
 
    for (LONG y=p1.y; y<p2.y&&y<dest.getHeight(); y++)
    {
      // render line
      if ( y >= 0 )
      {
        renderLine( destLine, dest.getWidth(), static_cast<LONG>(startX), static_cast<LONG>(endX), startU, startV, endU, endV );
      }
 
      startX += destAddX1_2;
      endX += destAddX1_3;
      startU += addU1_2;
      endU += addU1_3;
      startV += addV1_2;
      endV += addV1_3;
 
      destLine -= dest.getWidth();
    }
    // 2~3
    startX = static_cast<float>( p2.x );
    startU = static_cast<float>( p2.u );
    startV = static_cast<float>( p2.v );
    for (LONG y=p2.y; y<p3.y&&y<dest.getHeight(); ++y)
    {
      if ( y >= 0 )
      {
        renderLine( destLine, dest.getWidth(), static_cast<LONG>(startX), static_cast<LONG>(endX), startU, startV, endU, endV );
      }
 
      startX += destAddX2_3;
      endX += destAddX1_3;
      startU += addU2_3;
      endU += addU1_3;
      startV += addV2_3;
      endV += addV1_3;
 
      destLine -= 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)); }
 
  DWORD getPixel( LONG x, LONG y ) const
  {
    if ( x >= 0 && x < getWidth() && y >= 0 && y < getHeight() )
    {
      return *getPixelAddr( x, y );
    }
    return 0;
  }
  
 
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") );
 
    {
      DIB32::Point p1, p2, p3, p4;
      p1.x = 100;
      p1.y = 100;
      p1.u = 0;
      p1.v = 0;
      p2.x = 100;
      p2.y = 200;
      p2.u = 0;
      p2.v = 32;
      p3.x = 200;
      p3.y = 100;
      p3.u = 32;
      p3.v = 0;
      image.triangle( back, p1, p2, p3 );
 
      p4.x = 300;
      p4.y = 200;
      p4.u = 32;
      p4.v = 32;
      image.triangle( back, p2, p3, p4 );
    }
    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;
}

 
 
 
■そのほか
 renderLineで左から右へ描画するために左右ソートしてる。
別に左右判別しなくてもいけるらしいと調べてて見かけたが、
ぱっと処理が思いつかったのでソートする方法をとった。
 u,vからのピクセル取得時に範囲外参照を防ぐためif文チェックしているが、u&(getWidth()-1)とかやったほうが早いかもしれない。
 floatは遅いので固定小数点数かブレゼンハムにするといくらか速くなる。

■関連記事:
生産がす: 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

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

DIB(8) - 直線描画


 直線を引くプログラムです。
大雑把に言えば、
横に1px進むごとに縦に0.なんぼかpx動かし点を打っていけば直線がかける寸法です。
ただし、横に1px進むごとに縦に1px以上動くようだと線がとぎれとぎれになるので、
そういう場合は縦に1px進むごとに横に0.なんぼかpx動く、のように変更して線が途切れないようにする必要があります。


■サンプル

#include <windows.h>
#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;
  }
 
 
  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 );
  }
 
 
 
  bool line( LONG x1, LONG y1, LONG x2, LONG y2, DWORD color )
  {
    const LONG width = abs( x2 - x1 );
    const LONG height = abs( y2 - y1 );
 
    // check error
    if ( !m_pixel ||
      !width ||
      !height ||
      x1 < 0 && x2 < 0 ||
      y1 < 0 && y2 < 0 ||
      x1 >= getWidth() && x2 >= getWidth() ||
      y1 >= getHeight() && y2 >= getHeight() )
    {
      return false;
    }
    // 長い方を基準にする
    else if ( width >= height )
    {
      if ( x1 > x2 )
      {
        std::swap( x1, x2 );
        std::swap( y1, y2 );
      }
 
      
      LONG startX = x1;
      LONG endX = x2;
      const float addY = static_cast<float>(y2-y1) / static_cast<float>(x2-x1);
      float fy = static_cast<float>( y1 );
 
      // clipping-left
      if ( startX < 0 )
      {
        fy += (addY * static_cast<float>(-startX));
        startX = 0;
      }
 
      // clioping-right
      if ( endX >= getWidth() )
      {
        endX = getWidth()-1;
      }
 
      for (LONG x=startX; x<endX; ++x)
      {
        const LONG y = static_cast<LONG>( fy );
        if ( y >= 0 && y < getHeight() )
        {
          *getPixelAddr(x, y) = color;
        }
 
        fy += addY;
      }
    }
    else if ( width < height )
    {
      if ( y1 > y2 )
      {
        std::swap( x1, x2 );
        std::swap( y1, y2 );
      }
 
      const float addX = static_cast<float>(x2-x1) / static_cast<float>(y2-y1);
      float fx = static_cast<float>( x1 );
 
      // clipping-top
      if ( y1 < 0 )
      {
        fx += (addX * static_cast<float>(-y1));
        y1 = 0;
      }
      // clipping-bottom
      if ( y2 >= getHeight() )
      {
        y2 = getHeight()-1;
      }
 
      for (LONG y=y1; y<=y2; ++y)
      {
        const LONG x = static_cast<LONG>( fx );
        if ( x >= 0 && x < getWidth() )
        {
          *getPixelAddr(x, y) = color;
        }
 
        fx += addX;
      }
    }
    
    return true;
  }
 
  
  LONG getWidth() const { return m_bmi.bmiHeader.biWidth; }
  LONG getHeight() const { return m_bmi.bmiHeader.biHeight; }
  LPDWORD getPixelAddr( LONG x, LONG y) { return m_pixel + (getHeight()-1-y)*getWidth() + x; }
 
protected:
  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( WINDOW_WIDTH, WINDOW_HEIGHT );
    image.line( -50, -20, 690, 500, 0xFFFF8080 );
    break;
  case WM_PAINT:
    {
      PAINTSTRUCT ps = {0};
      HDC hdc = BeginPaint( hWnd, &ps );
      image.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

2011年4月18日月曜日

にきち


 三角形描画のスキャンラインループを
floatから固定小数点数にするだけで20FPS -> 45FPSになった。ちょろい! 
最適化かけたら30FPS -> 60FPSに。
 320x240の解像度だと三角形320個ほどレンダリングできそう。開発環境では。
ノートPCだとどうかなーと準備してたらノートPCが再起動地獄に陥ってた。
おのれWindowsUpdate。セーフモードでシステム復元したがなおらねえや、知らね。
 それからuvの範囲外参照を回避するべく、
if文で範囲外になったらループ終了するのと、
論理積でむりやり範囲内に収めるのと二つ実装していたが、
後者のほうが早いみたい。分岐予測云々か。

2011年4月17日日曜日

にっき

 8bitのDIBSectionを作成して
StretchBltでウィンドウのHDCに描画しようとするとなぜか失敗する。
StretchDIBitsだとうまくいく。なぜ。
 GetLastError()そんなのもあったなあと思いつつチェックすると
ERROR_NOT_ENOUGH_MEMORY( = 8)が帰ってきてた。うーん? 
よく調べてみるともっとはるか以前に発生してるっぽい、どこだ。
おもいっきりCreateDIBSectionですっころんでるがHBITMAPはちゃんと帰ってくるんだな。
LoadImageでも同様にGetLastErrorで8が帰ってくる割に読み込みはできてる、なんなの。
 とりあえずCreateDIBSectionのほうは、HDCでレンダリングできないがピクセルデータはきっちり保持してるらしく、
そのままビットマップファイルに出力してみたら正しくファイルを吐き出せた。
正直よくわからん。
 
 256色にすれば4ピクセルずつ書き込めて高速化できるかなー、とおもったが、
uvによるピクセル取得が1ピクセルごとなので高速化できない。
4ピクセル分あつめてから吐き出すようにすれば早くなる可能性もあるがめんどくせえ。

2011年4月16日土曜日

にっきちゃ

三角形
 uvの指定に対応できた。
回転拡大縮小とプログラミングの難易度はどっこいどっこいぐらいだと思う。
 
 MobleAMD Duron 800MHzのWindowsXPが発売したぐらいのころのノートPCで動かしたところ、
640x480で30FPSぐらい、320x240なら60FPSで動いた。ともにTrueColor。
これでゲーム作ろうとするなら256色ぐらいに落とさないと満足に動きそうにない。
もしくは320x240, 30FPSを想定して作るか。
スターフォックスが20FPSだそうだから(たぶん)、ありといえばありだが、
スターフォックスぐらい3Dしてるんなら素直にDirectX使ったほうが手っ取り早いか。
 時のオカリナも20FPS程度だそうだが、ぜんぜん気がつかなかったわ。
 
 u,vから画像のピクセル値を取ってくるときのmod計算でがっくり処理が落ちる。
2のべき乗にテクスチャ制限して論理積とるか、
範囲外は参照できないようif文ではじくほうがまだ現実的。今のところ後者。
ループ中にifしなきゃならんというのがなんだかなあ。

2011年4月15日金曜日

にきちゃん


 三角形を自力で描画。
1ラインずつちまちま書いていけばいいだけなのでそれほど大変ではない。
塗りつぶす代わりに画像データを参照すれば三角形ポリゴンになるが、
うまく実装できる気がしない。
 
 1ラインずつ書いたらうまくいくんじゃねってところから考えてたけど、
「最大最小法」って名前で世間で一般的に使われてたみたいな。
ググるワードがわかれば後は早いな。

2011年4月14日木曜日

かちかちの上白糖がさらさらに!

 ケースに入れておいた上白糖がスプーンが刺さらないぐらいにかちかちだったから、
ケースの蓋に数滴水をつけてから蓋を閉めたら、小一時間ですごいさらさらになった。
 砂糖は乾燥すると固まるというのは知ってたがこんなに違うとは思わなんだよ。
簡単でお勧め。
グラニュー糖にすればそもそも固まらないらしいが。

にっきty

line
 前からずっと線を引くプログラムを書きたいなーと思ってたんだけど、
ついに達成。5年越しぐらいの念願であった。
別に難しくもないんだけど、線を引くって機能はゲームじゃそうそう使わん。
 なんでいまさらかと言えば、ワイヤーフレームがやりたんじゃ。わしは。
あとは行列計算するだけで簡単3Dでありんす! ホイテョイ!
 
 PC変えてから負担がどの程度になるのか見当がつかなくなったな。
前はちょっと最適化すると劇的に早くなるのが実感できたが、
いまはループ中にifいれてもかけらも遅くならない。困った。

2011年4月12日火曜日

80bit浮動小数点数を64bit浮動小数点数におとす


double float80Tofloat64( BYTE* bin )
{
  // とりあえず符号セット
  double ret = 0;
  Utility::bitCopy( &ret, 63, bin, 79, 1 );
 
  // 指定部コピー
  DWORD e = 0;
  Utility::bitCopy( &e, 0, bin, 64, 15 );
  // 15bitの2の歩数解除
  e -= 0x3FFF;
  // 11bitの2の歩数セット
  e += 0x3FF;
  // 指定部をセット
  Utility::bitCopy( &ret, 52, &e, 0, 11 );
 
  // 仮数部をセット(80bitFloatは暗黙ビットが存在せず1が余分にあるのでカットしつつ52bit分コピー)
  Utility::bitCopy( &ret, 0, bin, 11/**64-52でさらに上位1bit不要なので-1 = 11*/, 52 );
 
  return ret;
}

bitCopyは生産がす: ビット単位のコピーのを使用。
 80bit浮動小数点数の44.1Kと22.05Kは64bitに正しく変換できたのを確認したが、そのほかは知らぬ。
誤差で大変なことになるのが予想される場合はお勧めしない。
 
 あとはaiffデータ部分をwavのデータに変換すれば再生可能か。
半年がかりだ
 もうちょっとわかりやすくunion + bit fieldでうまいことできないかと四苦八苦してみたがうまくいかなかった。ちょっと技術力が足りない。

2011年4月11日月曜日

にき

 最近オムレツを練習してるんだけど、何回作っても卵焼きにしかならない。
火力が強いのか、卵が少ないのかで
フライパンに卵を投入した瞬間に焼いてる面がぼこぼこになる。
もっとさらさらな感じにしたいんだ。
 卵二つで練習してるが、3つか4つ使うのがベターなんだろう。
でもそんなに食べられないよ……。
火力も下げるとフライパンにくっつくし、若干手詰まり感が。
 
 調べなおした。
中火で卵を投入、ぐしゃぐしゃに混ぜつつ火は強火にして、
混ざりきるころにはフライパンもあったまっててくっつかないという流れはどうだろう! 

2011年4月10日日曜日

日記ちゃん

 googleの検索結果だけをだらだら眺めてたら
シューティングの当たり判定は、縦シューなら縦軸を横シューなら横軸
を先に判定すると比較が少なくて良い

とあって、超目からうろこが落ちた。ここ数年のうちでもトップクラスのうろこだ。
でもまあ近頃のWindows環境だと誤差もいいところだが。

2011年4月4日月曜日

指定ウィンドウのウィンドウサイズを変更


setClientSize.zip(6.68KB)
 ひまつぶしというか、Vectorで探すのが面倒だったのでさくっと。
EnumWindowsですべてのウィンドウのハンドルを取得。
SetWindowPosで指定したウィンドウのサイズを変更するプログラム。
サイズはクライアントサイズを指定するのでゲーム画面の大きさをがっちり設定したい場合に向いてる。
なんかしらのサンプル代わりにでもなれば幸い。
 

#include <windows.h>
#include <cstdio>
#include <tchar.h>
#include <vector>
 
struct Data
{
  TCHAR name[ 256 ];
  HWND hWnd;
};
 
typedef std::vector<Data> WindowList;
WindowList g_winList;
 
BOOL CALLBACK EnumWindowProc( HWND hWnd, LPARAM lParam )
{
  if ( hWnd )
  {
    Data data;
    data.hWnd = hWnd;
    if ( 1 < GetWindowText( hWnd, data.name, sizeof(data.name)/sizeof(*data.name) ) )
    {
      g_winList.push_back( data );
    }
  }

  return TRUE;
}
 
 
 
void setClientSize( HWND hWnd, LONG clientWidth, LONG clientHeight )
{
  // 計算するためにとりあえず一回設定
  SetWindowPos( hWnd, HWND_TOP, 0, 0, clientWidth, clientHeight, SWP_NOMOVE );
 
  // 設定サイズと実際のサイズの誤差から正しいサイズを計算
  RECT rect;
  GetClientRect( hWnd, &rect );
  const int frameWidth = clientWidth - rect.right;
  const int frameHeight = clientHeight - rect.bottom;
  const int winWidth = clientWidth + frameWidth;
  const int winHeight = clientHeight + frameHeight;
 
  // 設定
  SetWindowPos( hWnd, HWND_TOP, 0, 0, winWidth, winHeight, SWP_SHOWWINDOW | SWP_NOMOVE );
}
 
 
 
int main()
{
  for(;;)
  {
    g_winList.clear();
    EnumWindows( EnumWindowProc, 0 );
    
    // render list
    {
      size_t n = g_winList.size();
      for (size_t i=0; i<n; ++i)
      {
        _tprintf(TEXT("%d: %s\n"), i, g_winList[i].name);
      }
    }
 
    _tprintf(TEXT("param: [settingWindowNo][width][height]\n"));
    int no;
    std::scanf("%d", &no);
    if ( no < 0 && no >= g_winList.size() ) continue;
 
    int width;
    std::scanf("%d", &width);
    if ( width < 16 && width > 2048 ) continue;
 
    int height;
    std::scanf("%d", &height);
    if ( height < 16 && height > 2048 ) continue;
 
    setClientSize( g_winList[no].hWnd, width, height );
    _tprintf(TEXT("[%d]Set! [%d x %d]\n\n"), no, width, height);
  }
 
  return 0;
}

2011年4月3日日曜日

にきち

 ふえたー。
というわけで、一応メモリ3Gになったにはなったんだけど、
もともと刺さってたのがシングルサイドのDDR2-664-1G、
買ったのがダブルサイドのDDR2-800-2Gと、合わせるには鬼門もいいとこ。
面白半分で同時使用してみてるが、はてさて。
でもスワッピングしなくなったのかHDDのガリガリ音がなくなって快適だわ。
 
 memtestを4周ほど回してみたがエラーなし。
今のところ不具合もなしとなかなか順調。
相性問題がどの程度の割合で発生するのかわからないが案外事例は少ないのか? 
 
 今のところメモリ1.5GB以上使ったことがない。
XP起動直後で0.5GBくらい。
WindowsUpdateが動くと1.2GB程度になり、ブラウザとあわせても通常使用で1.5GBぐらい。
3GBはオーバースペック気味で、何に使ったら3GB行くか見当もつかないな、いまのところ。