/*
###################################
#
# dxruby.c Ver. 1.0.6
#
###################################
*/
#define DXRUBY_VERSION "1.0.6"

#undef DIRECTINPUT_VERSION
#define DIRECTINPUT_VERSION        (0x0800)            /* DirectInputバージョン定義 */
#define WINVER 0x0500            //    バージョン定義 Windows2000以上
#define _WIN32_WINNT WINVER

#include "ruby.h"
#include <stdlib.h>
#include <mmsystem.h>
#include <tchar.h>
#include <d3dx9.h>
#include <dmusici.h>
#include <dinput.h>
#include <memory.h>
#include <math.h>
//#include <DShow.h>

#ifndef RSTRING_PTR
#  define RSTRING_PTR(s) (RSTRING(s)->ptr)
#endif
#ifndef RSTRING_LEN
#  define RSTRING_LEN(s) (RSTRING(s)->len)
#endif

#ifndef RARRAY_PTR
#  define RARRAY_PTR(s) (RARRAY(s)->ptr)
#endif
#ifndef RARRAY_LEN
#  define RARRAY_LEN(s) (RARRAY(s)->len)
#endif

#define RELEASE( x )  if( x ) { x->lpVtbl->Release( x ); x = NULL; }

#define PADMAX 2

#define P_LEFT     0
#define P_RIGHT    1
#define P_UP       2
#define P_DOWN     3
#define P_BUTTON0  4
#define P_BUTTON1  5
#define P_BUTTON2  6
#define P_BUTTON3  7
#define P_BUTTON4  8
#define P_BUTTON5  9
#define P_BUTTON6  10
#define P_BUTTON7  11
#define P_BUTTON8  12
#define P_BUTTON9  13
#define P_BUTTON10 14
#define P_BUTTON11 15
#define P_BUTTON12 16
#define P_BUTTON13 17
#define P_BUTTON14 18
#define P_BUTTON15 19
#define M_LBUTTON   0
#define M_RBUTTON   1
#define M_MBUTTON   2
#define FORMAT_JPEG D3DXIFF_JPG
#define FORMAT_JPG  D3DXIFF_JPG
#define FORMAT_PNG  D3DXIFF_PNG
#define FORMAT_BMP  D3DXIFF_BMP
#define FORMAT_DDS  D3DXIFF_DDS
#define WAVE_RECT 0
#define WAVE_SIN 1
#define WAVE_SAW 2
#define WAVE_TRI 3

//#define WM_GRAPH_NOTIFY (WM_APP + 1) /* IMediaEventExのメッセージ */

static VALUE mDXRuby;       /* DXRubyモジュール     */
static VALUE mWindow;       /* ウィンドウモジュール */
static VALUE mInput;        /* インプットモジュール */
static VALUE cImage;        /* イメージクラス       */
static VALUE cSound;        /* サウンドクラス       */
static VALUE cFont;         /* フォントクラス       */
static VALUE cSoundEffect;  /* 生成効果音クラス     */
static VALUE eDXRubyError;  /* 例外                 */
static VALUE cDXRubyInternalMark; /* 内部処理用 */
static VALUE oDXRubyInternalMark; /* 内部処理用 */

/* グローバル変数たち */
static HINSTANCE             g_hInstance   = NULL; /* アプリケーションインスタンス   */
static HANDLE                g_hWind       = NULL; /* ウィンドウハンドル             */
static LPDIRECT3D9           g_pD3D        = NULL; /* Direct3Dインターフェイス       */
static LPDIRECT3DDEVICE9     g_pD3DDevice  = NULL; /* Direct3DDeviceインターフェイス */
static D3DPRESENT_PARAMETERS g_D3DPP;              /* D3DDeviceの設定                */
static LPD3DXSPRITE          g_pD3DXSprite = NULL; /* スプライト                     */

static LPDIRECTINPUT8        g_pDInput            = NULL; /* DirectInput */
static LPDIRECTINPUTDEVICE8  g_pDIDKeyBoard       = NULL; /* DirectInputのキーボードデバイス */
static LPDIRECTINPUTDEVICE8  g_pDIDJoyPad[PADMAX];        /* DirectInputのパッドデバイス     */
static BYTE g_diKeyState[256];                            /* DirectInputでのキーボード入力用バッファ */
static BYTE g_diKeyStateOld[256];                         /* DirectInputでのキーボード入力用バッファ１フレーム前 */
static BYTE g_diKeyCount[256];                            /* DirectInputでのキーボード入力用カウンタ */
static BYTE g_diKeyConfig[256];                           /* DirectInputでのキーボード・パッド割り当て */
static BYTE g_diKeyWait[256];                             /* オートリピートウェイト時間 */
static BYTE g_diKeyInterval[256];                         /* オートリピート間隔 */
static int g_JoystickCount = 0;
static BYTE g_byMouseState_L, g_byMouseStateOld_L;
static BYTE g_byMouseState_M, g_byMouseStateOld_M;
static BYTE g_byMouseState_R, g_byMouseStateOld_R;


static IDirectMusicPerformance8 *g_pDMPerformance = NULL;       /* DirectMusicPerformance8インターフェイス */
static IDirectMusicLoader8      *g_pDMLoader = NULL;            /* ローダー */
static LPDIRECTSOUND8           g_pDSound = NULL;               /* DirectSoundインターフェイス */
static int g_iRefDM = 0; /* DirectMusicパフォーマンスの参照カウント */
static int g_iRefDS = 0; /* DirectSoundの参照カウント */
static int g_iRefAll = 1; /* インターフェースの参照カウント */

//static IGraphBuilder *g_pGraphBuilder = NULL; /* IGraphBuilderインターフェイス */
//static IMediaControl *g_pMediaControl = NULL; /* IMediaControlインターフェイス */
//static IVideoWindow  *g_pVideoWindow = NULL;  /* IVideoWindowインターフェイス */
//static IMediaEventEx *g_pMediaEventEx = NULL; /* IMediaEventExインターフェイス */
//static int g_PlayingMovie = 0; /* 動画再生中フラグ */


/* ゲーム調整用 */
static int g_bActive = 0;

/* フレーム調整用 */
static __int64 g_OneSecondCount       = 0;         /* 一秒間にカウンタが数える数         */
static int     g_isPerformanceCounter = 0;         /* パフォーマンスカウンタがあったら１ */
static __int64 g_OldTime              = 0;         /* 前回のフレームが終わった時間       */
static __int64 g_OneFrameCount        = 0;         /* １フレームの処理にかかった時間     */
static __int64 g_DrawTime             = 0;         /* 描画にかかった時間                 */

/* ウィンドウ情報構造体 */
static struct DXRubyWindowInfo {
    int x;              /* ｘ座標 */
    int y;              /* ｙ座標 */
    int width;          /* 幅     */
    int height;         /* 高さ   */
    int windowed;       /* ウィンドウモード時にQtrue */
    int created;        /* ウィンドウを作成したらQtrue */
    float scale;        /* ウィンドウのサイズ倍率 */
    int RefreshRate;    /* リフレッシュレート */
    int enablemouse;    /* マウスを表示するかどうか */
    int mousewheelpos;  /* マウスホイールの位置 */
    int fps;            /* fps */
    int fpscheck;       /* 現在のfps */
    int frameskip;      /* コマ落ち制御フラグ */
    int r;              /* 背景クリア色 赤成分 */
    int g;              /* 背景クリア色 緑成分 */
    int b;              /* 背景クリア色 青成分 */
    int minfilter;      /* 縮小フィルタ */
    int magfilter;      /* 拡大フィルタ */
    HANDLE hIcon;       /* ウィンドウアイコンハンドル */
} g_WindowInfo;

/* テクスチャデータ */
struct DXRubyTexture {
    LPDIRECT3DTEXTURE9 pD3DTexture;     /* スプライトに使うテクスチャ   */
	int width;
	int height;
	int refcount;
};

/* フォントデータ */
struct DXRubyFont {
    LPD3DXFONT pD3DXFont;       /* フォントオブジェクト   */
    HFONT hFont;                /* Image描画に使うフォント  */
    int size;                   /* フォントサイズ */
};

/* フォントデータ */
struct DXRubyFontSprite {
    struct DXRubyFont *font;    /* スプライトに使うフォント */
    VALUE str;                  /* 文字列オブジェクト */
    int color;                  /* フォントの色 */
};

/* スプライトデータ */
struct DXRubySprite {
    void *image;                /* 描画するイメージオブジェクトもしくはフォント */
    int x;                      /* 描画位置 x */
    int y;                      /* 描画位置 y */
    float angle;                /* 回転角度 */
    float scalex;               /* 横拡大率 */
    float scaley;               /* 縦拡大率 */
    float centerx;              /* 回転時の中心 x */
    float centery;              /* 回転時の中心 y */
    unsigned char alpha;        /* アルファ（透明）値 */
    unsigned char blendflag :2; /* 半透明(00)、加算合成1(01)、加算合成2(10)、減算合成(11)のフラグ */
    unsigned char fontflag :1;  /* フォントなら１ */
    unsigned char vpflag :1;    /* ビューポートなら１ */
    unsigned char reserve1 :4;  /* 予約2 */
    char reserve2;              /* 予約3 */
    char reserve3;              /* 予約4 */
    VALUE value;
};

/* Viewport */
struct DXRubyViewport {
    D3DVIEWPORT9 viewport;
};

/* Imageオブジェクトの中身 */
struct DXRubyImage {
    struct DXRubyTexture *texture;
    float x;     /* x始点位置      */
    float y;     /* y始点位置      */
    float width; /* イメージの幅   */
    float height;/* イメージの高さ */
};

/* Soundオブジェクト */
struct DXRubySound {
    IDirectMusicAudioPath8   *pDMDefAudioPath; /* デフォルトオーディオパス */
    IDirectMusicSegment8     *pDMSegment;        /* セグメント       */
    int start;
    int loopstart;
    int loopend;
    int loopcount;
    int midwavflag; /* midは0、wavは1 */
};

/* SoundEffectオブジェクト */
struct DXRubySoundEffect {
    LPDIRECTSOUNDBUFFER pDSBuffer;    /* バッファ         */
};

/* Pad情報 */
static struct DXRubyPadInfo {
    int right;
    int left;
    int up;
    int down;
    LPDIRECTINPUTDEVICE8  pDIDJoyPad; /* DirectInputのパッドデバイス */
} g_PadInfo[PADMAX];

/* Pad状態 */
static struct DXRubyPadState {
    char button[20];
    int PadConfig[20];
    int count[20];
    int wait[20];
    int interval[20];
} g_PadState[PADMAX], g_PadStateOld[PADMAX];

/* スプライト配列 */
static struct DXRubySpriteList {
	float z;                        /* スプライトのZ座標 */
	struct DXRubySprite *sprite;    /* スプライト構造体へのポインタ */
} *g_SpriteList;
static int g_SpriteCount;                  /* スプライトの登録数 */
static int g_SpriteAllocateCount;          /* スプライト配列のメモリ確保数 */
static struct DXRubySprite *g_SpriteStruct;

/* 色 */
struct DXRubyColor {
    unsigned char blue;
    unsigned char green;
    unsigned char red;
    unsigned char alpha;
};

/* シンボル */
static VALUE symbol_blend   = Qundef;
static VALUE symbol_angle   = Qundef;
static VALUE symbol_alpha   = Qundef;
static VALUE symbol_scalex  = Qundef;
static VALUE symbol_scaley  = Qundef;
static VALUE symbol_centerx = Qundef;
static VALUE symbol_centery = Qundef;
static VALUE symbol_z       = Qundef;
static VALUE symbol_color   = Qundef;
static VALUE symbol_add     = Qundef;
static VALUE symbol_add2    = Qundef;
static VALUE symbol_sub     = Qundef;

/* キーのオートリピート用 */
//static int g_iInputWait;
//static int g_iInterval;

/* プロトタイプ宣言 */
static void InitWindow( void );
static void InitDXGraphics( void );
static void InitDirectInput( void );
LRESULT CALLBACK MainWndProc( HWND hWnd,UINT msg,UINT wParam,LONG lParam );
static void InitSync( void );
static __int64 GetSystemCounter( void );
static void Window_DirectXRelease( void );
static struct DXRubyTexture *Image_textureload( char *filename, D3DXIMAGE_INFO *psrcinfo );
static void Input_SetConfig( int number, int pad, int key );
static void ChangeSize( void );
static void inputupdate( void);
static VALUE Input_update( VALUE );
static VALUE Window_update( VALUE );
static VALUE Window_sync( VALUE );
static VALUE Window_create( VALUE );
static void AllocSpriteList( void );
static void SortSpriteList( void );

static void release_Image( struct DXRubyImage* image );
static void release_Font( struct DXRubyFont* font );

/*********************************************************************
 * Windowモジュール
 * ウィンドウの管理・描画を行う。
 *********************************************************************/

/*--------------------------------------------------------------------
   ウィンドウの生成と初期化
 ---------------------------------------------------------------------*/
static VALUE Window_create( VALUE obj )
{
    HRESULT hr;
    RECT rect;

    if( g_WindowInfo.created == Qtrue )
    {
        rb_raise( eDXRubyError, "ウィンドウ作成後に実行することはできません - Window_create" );
    }

    /* ウィンドウのサイズ設定 */
    rect.top     = 0;
    rect.left    = 0;
    rect.right   = g_WindowInfo.width * g_WindowInfo.scale;
    rect.bottom  = g_WindowInfo.height * g_WindowInfo.scale;

    /* ウィンドウのサイズ修正 */
    if( g_WindowInfo.windowed == Qnil || g_WindowInfo.windowed == Qfalse )
    {   /* フルスクリーン */
        SetWindowLong( g_hWind, GWL_STYLE, WS_POPUP );
        SetWindowPos( g_hWind, HWND_TOP, 0, 0, g_WindowInfo.width, g_WindowInfo.height, SWP_NOZORDER);
    }
    else
    {   /* ウィンドウモード */
        SetWindowLong( g_hWind, GWL_STYLE, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX);
        AdjustWindowRect( &rect, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE );

        /* 幅と高さ計算 */
        rect.right   = rect.right - rect.left;
        rect.bottom  = rect.bottom - rect.top;

        /* ウィンドウ移動/サイズ設定 */
        if( g_WindowInfo.x == CW_USEDEFAULT )
        {   /* 位置がデフォルトの場合 */
            SetWindowPos( g_hWind, HWND_TOP, 0, 0, rect.right, rect.bottom, SWP_NOZORDER | SWP_NOMOVE);
        }
        else
        {   /* 位置指定の場合 */
            SetWindowPos( g_hWind, HWND_TOP, g_WindowInfo.x, g_WindowInfo.y, rect.right, rect.bottom, SWP_NOZORDER );
        }
    }

    /* DirectXのスクリーンサイズ変更 */
    ChangeSize();

    /* ウィンドウ表示 */
    ShowWindow( g_hWind, SW_SHOWNORMAL );
    UpdateWindow( g_hWind );

    /* シーンのクリア */
    g_pD3DDevice->lpVtbl->Clear( g_pD3DDevice, 0, NULL, D3DCLEAR_TARGET,
                                 D3DCOLOR_XRGB( g_WindowInfo.r, g_WindowInfo.g, g_WindowInfo.b ), 0, 0 );
    g_pD3DDevice->lpVtbl->Present( g_pD3DDevice, NULL, NULL, NULL, NULL );

    /* フレーム調整処理初期化 */
    InitSync();

    g_WindowInfo.created = Qtrue;

    return obj;
}


/*--------------------------------------------------------------------
  （内部関数）スクリーンサイズ変更
 ---------------------------------------------------------------------*/
static void ChangeSize( void )
{
    D3DVIEWPORT9 vp;
    HRESULT hr;


    g_D3DPP.BackBufferWidth    = g_WindowInfo.width;
    g_D3DPP.BackBufferHeight   = g_WindowInfo.height;

    if( g_WindowInfo.windowed == Qnil || g_WindowInfo.windowed == Qfalse )
    {
        /* フルスクリーン時（32bitColor/60Hz固定) */
        g_D3DPP.BackBufferFormat           = D3DFMT_X8R8G8B8;
        g_D3DPP.Windowed                   = FALSE;
        g_D3DPP.FullScreen_RefreshRateInHz = 60;
        g_D3DPP.PresentationInterval       = D3DPRESENT_INTERVAL_ONE;
        g_WindowInfo.RefreshRate = 60;
    }
    else
    {
        /* ウィンドウモード時 */
        g_D3DPP.BackBufferFormat           = D3DFMT_UNKNOWN;
        g_D3DPP.Windowed                   = TRUE;
        g_D3DPP.FullScreen_RefreshRateInHz = 0;
        g_D3DPP.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;
        g_WindowInfo.RefreshRate = 0;
    }

    /* スプライトをロストさせる */
    if( g_pD3DXSprite )
    {
        g_pD3DXSprite->lpVtbl->OnLostDevice( g_pD3DXSprite );
    }

    /* 設定変更 */
    hr = g_pD3DDevice->lpVtbl->Reset( g_pD3DDevice, &g_D3DPP );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "設定に失敗しました - Reset" );
    }

    /* スプライト復帰 */
    if( g_pD3DXSprite )
    {
        g_pD3DXSprite->lpVtbl->OnResetDevice( g_pD3DXSprite );
    }

    /* ビューポートの設定 */
    vp.X       = 0;
    vp.Y       = 0;
    vp.Width   = g_D3DPP.BackBufferWidth;
    vp.Height  = g_D3DPP.BackBufferHeight;
    vp.MinZ    = 0.0f;
    vp.MaxZ    = 1.0f;

    hr = g_pD3DDevice->lpVtbl->SetViewport( g_pD3DDevice, &vp );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "設定に失敗しました - SetViewport" );
    }
}


/*--------------------------------------------------------------------
   Windowsのメッセージループ
 ---------------------------------------------------------------------*/
static VALUE Window_loop( VALUE obj )
{
    MSG msg;
    msg.message = WM_NULL;

    if( g_WindowInfo.created == Qtrue )
    {
        rb_raise( eDXRubyError, "ウィンドウ作成後に実行することはできません - Window_loop" );
    }

    Window_create( obj );

    /* メッセージループ */
    /* WM_QUITが届くまでメッセージ処理をし続ける */
    /* メッセージが無い時はブロックをyieldする */
    while( msg.message != WM_QUIT )
    {
        if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) != 0)
        {
            /* メッセージがある時 */
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
        else
        {
            /* メッセージが無い時 */
            /* 入力状態更新 */
            inputupdate();
            /* ブロック実行 */
            rb_yield( obj );
            /* fps調整 */
            Window_sync( obj );
            /* 描画 */
            Window_update( obj );
        }
    }

    return Qtrue;
}


/*--------------------------------------------------------------------
   （内部関数）ウィンドウプロシージャ
 ---------------------------------------------------------------------*/
LRESULT CALLBACK MainWndProc( HWND hWnd, UINT msg, UINT wParam, LONG lParam )
{
    RECT rect;
    
    switch( msg )
    {
    /* ウィンドウアクティブ／非アクティブ化 */
    case WM_ACTIVATE:
        g_bActive = (LOWORD(wParam) != 0);
        break;
    /* ウィンドウ破棄 */
    case WM_DESTROY:
        /* ウインドウを閉じる */
        PostQuitMessage( 0 );
        g_hWind = NULL;
        return 0;

//    case WM_GRAPH_NOTIFY:
//    {
//        long evCode;
//        LONG param1, param2;
//        while( SUCCEEDED( g_pMediaEventEx->lpVtbl->GetEvent( g_pMediaEventEx, &evCode, &param1, &param2, 0 )))
//        {
//            g_pMediaEventEx->lpVtbl->FreeEventParams( g_pMediaEventEx, evCode, param1, param2 );
//
//            switch (evCode) {
//                case EC_COMPLETE:
//                    // 再生終了
//                    g_PlayingMovie = 0;
//                    break;
//            }
//        }
//        break;
//    }

    case WM_MOUSEWHEEL:
        g_WindowInfo.mousewheelpos += (short)HIWORD(wParam);
        break;
/*
    case WM_SETCURSOR:
        if( g_pD3DDevice )
        {
            if( LOWORD( lParam ) == HTCLIENT )  // クライアント領域のみ
            {
                if( g_WindowInfo.enablemouse == Qfalse )
                {
                    SetCursor( NULL );              // ウインドウの標準カーソルを消す
                    return TRUE;                    // 設定を終えたことを通知
                }
            }
        }
        break;
*/
    }

    /* デフォルト処理 */
    return DefWindowProc( hWnd, msg, wParam, lParam );
}


/*--------------------------------------------------------------------
   画面クリア色指定
 ---------------------------------------------------------------------*/
static VALUE Window_getbgcolor( VALUE obj )
{
    return rb_ary_new3( 3, INT2FIX( g_WindowInfo.r ), INT2FIX( g_WindowInfo.g ), INT2FIX( g_WindowInfo.b ) );
}


/*--------------------------------------------------------------------
   画面クリア色指定
 ---------------------------------------------------------------------*/
static VALUE Window_setbgcolor( VALUE obj, VALUE array )
{
    Check_Type(array, T_ARRAY);

    if( RARRAY_LEN(array) == 4 )
    {
        g_WindowInfo.r = NUM2INT( rb_ary_entry(array, 1) );
        g_WindowInfo.g = NUM2INT( rb_ary_entry(array, 2) );
        g_WindowInfo.b = NUM2INT( rb_ary_entry(array, 3) );
    }
    else
    {
        g_WindowInfo.r = NUM2INT( rb_ary_entry(array, 0) );
        g_WindowInfo.g = NUM2INT( rb_ary_entry(array, 1) );
        g_WindowInfo.b = NUM2INT( rb_ary_entry(array, 2) );
    }

    return obj;
}


/*--------------------------------------------------------------------
   描画設定（通常描画）
 ---------------------------------------------------------------------*/
static VALUE Window_draw( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;

    if( argc < 3 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 3);

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( argv[2] ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( argv[2] ));
    image = (struct DXRubyImage *)DATA_PTR( argv[2] );
    if (RDATA(argv[2])->dfree != (RUBY_DATA_FUNC)release_Image)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( argv[2] ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
    g_SpriteStruct[g_SpriteCount].centerx = image->width / 2;
    g_SpriteStruct[g_SpriteCount].centery = image->height / 2;
    g_SpriteStruct[g_SpriteCount].blendflag = 0;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = argv[2];

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = argc < 4 || argv[3] == Qnil ? 0.0f : NUM2DBL( argv[3] );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   描画設定（拡大縮小描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawScale( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;

    if( argc < 5 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 3);

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( argv[2] ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( argv[2] ));
    image = (struct DXRubyImage *)DATA_PTR( argv[2] );
    if (RDATA(argv[2])->dfree != (RUBY_DATA_FUNC)release_Image)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( argv[2] ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = NUM2DBL( argv[3] );
    g_SpriteStruct[g_SpriteCount].scaley = NUM2DBL( argv[4] );
    g_SpriteStruct[g_SpriteCount].centerx = argc < 6 || argv[5] == Qnil  ? image->width / 2 : NUM2DBL( argv[5] );
    g_SpriteStruct[g_SpriteCount].centery = argc < 7 || argv[6] == Qnil  ? image->height / 2 : NUM2DBL( argv[6] );
    g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
    g_SpriteStruct[g_SpriteCount].blendflag = 0;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = argv[2];

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = argc < 8 || argv[7] == Qnil ? 0.0f : NUM2DBL( argv[7] );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   描画設定（回転描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawRot( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;

    if( argc < 4 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 3);

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( argv[2] ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( argv[2] ));
    image = (struct DXRubyImage *)DATA_PTR( argv[2] );
    if (RDATA(argv[2])->dfree != (RUBY_DATA_FUNC)release_Image)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( argv[2] ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle  = NUM2DBL( argv[3] );
    g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
    g_SpriteStruct[g_SpriteCount].blendflag = 0;
    g_SpriteStruct[g_SpriteCount].centerx = argc < 5 || argv[4] == Qnil  ? image->width / 2 : NUM2DBL( argv[4] );
    g_SpriteStruct[g_SpriteCount].centery = argc < 6 || argv[5] == Qnil  ? image->height / 2 : NUM2DBL( argv[5] );
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = argv[2];

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = argc < 7 || argv[6] == Qnil ? 0.0f : NUM2DBL( argv[6] );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   描画設定（半透明描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawAlpha( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;

    if( argc < 4 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 3);

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( argv[2] ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( argv[2] ));
    image = (struct DXRubyImage *)DATA_PTR( argv[2] );
    if (RDATA(argv[2])->dfree != (RUBY_DATA_FUNC)release_Image)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( argv[2] ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = NUM2INT( argv[3] );
    g_SpriteStruct[g_SpriteCount].blendflag = 0;
    g_SpriteStruct[g_SpriteCount].centerx = image->width / 2;
    g_SpriteStruct[g_SpriteCount].centery = image->height / 2;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = argv[2];

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = argc < 5 || argv[4] == Qnil ? 0.0f : NUM2DBL( argv[4] );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   描画設定（加算合成描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawAdd( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;

    if( argc < 3 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 3);

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( argv[2] ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( argv[2] ));
    image = (struct DXRubyImage *)DATA_PTR( argv[2] );
    if (RDATA(argv[2])->dfree != (RUBY_DATA_FUNC)release_Image)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( argv[2] ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
    g_SpriteStruct[g_SpriteCount].blendflag = 1;
    g_SpriteStruct[g_SpriteCount].centerx = image->width / 2;
    g_SpriteStruct[g_SpriteCount].centery = image->height / 2;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = argv[2];

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = argc < 4 || argv[3] == Qnil ? 0.0f : NUM2DBL( argv[3] );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   描画設定（減算合成描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawSub( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;

    if( argc < 3 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 3);

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( argv[2] ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( argv[2] ));
    image = (struct DXRubyImage *)DATA_PTR( argv[2] );
    if (RDATA(argv[2])->dfree != (RUBY_DATA_FUNC)release_Image)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( argv[2] ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
    g_SpriteStruct[g_SpriteCount].blendflag = 3;
    g_SpriteStruct[g_SpriteCount].centerx = image->width / 2;
    g_SpriteStruct[g_SpriteCount].centery = image->height / 2;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = argv[2];

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = argc < 4 || argv[3] == Qnil ? 0.0f : NUM2DBL( argv[3] );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   描画設定（フルオプション）
 ---------------------------------------------------------------------*/
static VALUE Window_drawEx( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;
    VALUE x, y, z, data, angle, scalex, scaley, alpha, centerx, centery, blend, add;
    VALUE option;

    if( argc < 3 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 3);

    if( argc < 4 || argv[3] == Qnil )
    {
        option = rb_hash_new();
    }
    else
    {
        Check_Type( argv[3], T_HASH );
        option = argv[3];
    }

    AllocSpriteList();

    blend = rb_hash_aref( option, symbol_blend );
    angle = rb_hash_aref( option, symbol_angle );
    alpha = rb_hash_aref( option, symbol_alpha );
    scalex = rb_hash_aref( option, symbol_scalex );
    scaley = rb_hash_aref( option, symbol_scaley );
    centerx = rb_hash_aref( option, symbol_centerx );
    centery = rb_hash_aref( option, symbol_centery );
    z = rb_hash_aref( option, symbol_z );

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( argv[2] ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( argv[2] ));
    image = (struct DXRubyImage *)DATA_PTR( argv[2] );
    if (RDATA(argv[2])->dfree != (RUBY_DATA_FUNC)release_Image)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( argv[2] ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle   = (angle   == Qnil ? 0.0f             : NUM2DBL( angle   ));
    g_SpriteStruct[g_SpriteCount].scalex  = (scalex  == Qnil ? 1.0f             : NUM2DBL( scalex  ));
    g_SpriteStruct[g_SpriteCount].scaley  = (scaley  == Qnil ? 1.0f             : NUM2DBL( scaley  ));
    g_SpriteStruct[g_SpriteCount].centerx = (centerx == Qnil ? image->width / 2 : NUM2DBL( centerx ));
    g_SpriteStruct[g_SpriteCount].centery = (centery == Qnil ? image->height / 2 : NUM2DBL( centery ));
    g_SpriteStruct[g_SpriteCount].alpha   = (alpha   == Qnil ? 0xff             : NUM2INT( alpha   ));
    g_SpriteStruct[g_SpriteCount].blendflag = (blend == symbol_add ? 1 : (blend == symbol_add2 ? 2 : (blend == symbol_sub ? 3 : 0)));
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = argv[2];

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = z == Qnil ? 0.0f : NUM2DBL( z );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   フォント描画
 ---------------------------------------------------------------------*/
static VALUE Window_drawFont( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubySprite *spr;
    struct DXRubyFont *font;
    VALUE color;
    int cr, cg, cb;
    VALUE z, angle, scalex, scaley, alpha, centerx, centery, blend, add;
    VALUE option;
    struct DXRubyFontSprite *fontsprite;

    if( argc < 4 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 4);

    Check_Type(argv[2], T_STRING);

    if( argc < 5 || argv[4] == Qnil )
    {
        option = rb_hash_new();
    }
    else
    {
        Check_Type( argv[4], T_HASH );
        option = argv[4];
    }

    blend = rb_hash_aref( option, symbol_blend );
    angle = rb_hash_aref( option, symbol_angle );
    alpha = rb_hash_aref( option, symbol_alpha );
    scalex = rb_hash_aref( option, symbol_scalex );
    scaley = rb_hash_aref( option, symbol_scaley );
    centerx = rb_hash_aref( option, symbol_centerx );
    centery = rb_hash_aref( option, symbol_centery );
    z = rb_hash_aref( option, symbol_z );
    color = rb_hash_aref( option, symbol_color );

    AllocSpriteList();

    /* 引数のフォントオブジェクトから中身を取り出す */
    Data_Get_Struct( argv[3], struct DXRubyFont, font );
    if (RDATA(argv[3])->dfree != (RUBY_DATA_FUNC)release_Font)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Font)", rb_obj_classname( argv[3] ));
    }

    fontsprite = ALLOC( struct DXRubyFontSprite );
    if( fontsprite == NULL )
    {
        rb_raise( eDXRubyError, "フォント用メモリの確保に失敗しました" );
    }
    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = fontsprite;
    g_SpriteStruct[g_SpriteCount].x = NUM2INT( argv[0] );
    g_SpriteStruct[g_SpriteCount].y = NUM2INT( argv[1] );
    g_SpriteStruct[g_SpriteCount].angle   = (angle   == Qnil ? 0.0f             : NUM2DBL( angle ));
    g_SpriteStruct[g_SpriteCount].scalex  = (scalex  == Qnil ? 1.0f             : NUM2DBL( scalex ));
    g_SpriteStruct[g_SpriteCount].scaley  = (scaley  == Qnil ? 1.0f             : NUM2DBL( scaley ));
    g_SpriteStruct[g_SpriteCount].centerx = (centerx == Qnil ? 0.0f             : NUM2DBL( centerx ));;
    g_SpriteStruct[g_SpriteCount].centery = (centery == Qnil ? 0.0f             : NUM2DBL( centery ));;
    g_SpriteStruct[g_SpriteCount].alpha   = (alpha   == Qnil ? 0xff             : NUM2INT( alpha ));
    g_SpriteStruct[g_SpriteCount].blendflag = (blend == symbol_add ? 1 : (blend == symbol_add2 ? 2 : (blend == symbol_sub ? 3 : 0)));
    g_SpriteStruct[g_SpriteCount].fontflag = 1;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = argv[3];
    fontsprite->str = argv[2];
    if( color != Qnil )
    {
        Check_Type(color, T_ARRAY);
        if( RARRAY_LEN(color) == 4 )
        {
            g_SpriteStruct[g_SpriteCount].alpha = NUM2INT(rb_ary_entry(color, 0));
            cr = NUM2INT(rb_ary_entry(color, 1));
            cg = NUM2INT(rb_ary_entry(color, 2));
            cb = NUM2INT(rb_ary_entry(color, 3));
        }
        else
        {
            cr = NUM2INT(rb_ary_entry(color, 0));
            cg = NUM2INT(rb_ary_entry(color, 1));
            cb = NUM2INT(rb_ary_entry(color, 2));
        }
    }
    else
    {
        cr = 255;
        cg = 255;
        cb = 255;
    }
    fontsprite->color = D3DCOLOR_XRGB(cr, cg, cb);
    fontsprite->font = font;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = z == Qnil ? 0.0f : NUM2DBL( z );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   ビューポート設定
 ---------------------------------------------------------------------*/
static VALUE Window_setViewport( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyViewport *vp;

    if( argc < 4 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 4);

    AllocSpriteList();

    if( NUM2INT(argv[0]) < 0 || NUM2INT(argv[1]) < 0 || NUM2INT(argv[2]) < 0 || NUM2INT(argv[3]) < 0 || 
        NUM2INT(argv[0]) + NUM2INT(argv[2]) > g_WindowInfo.width || NUM2INT(argv[1]) + NUM2INT(argv[3]) > g_WindowInfo.height)
    {
        rb_raise(rb_eArgError, "範囲の指定が異常です - setViewport");
    }

    vp = malloc( sizeof(struct DXRubyViewport) );

    vp->viewport.X = NUM2INT( argv[0] );
    vp->viewport.Y = NUM2INT( argv[1] );
    vp->viewport.Width = NUM2INT( argv[2] );
    vp->viewport.Height = NUM2INT( argv[3] );
    vp->viewport.MinZ = 0.0f;
    vp->viewport.MaxZ = 1.0f;

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = vp;
    g_SpriteStruct[g_SpriteCount].x = 0;
    g_SpriteStruct[g_SpriteCount].y = 0;
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = 0.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 0.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = 0;
    g_SpriteStruct[g_SpriteCount].centerx = 0;
    g_SpriteStruct[g_SpriteCount].centery = 0;
    g_SpriteStruct[g_SpriteCount].blendflag = 0;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 1;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = argc < 5 || argv[4] == Qnil ? 0.0f : NUM2DBL( argv[4] );
    g_SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   マップ描画
 ---------------------------------------------------------------------*/
static VALUE Window_drawTile( int argc, VALUE *argv, VALUE obj )
{
    VALUE vstartx, vstarty, vz;
    VALUE *arr1;
    VALUE *arr2;
    VALUE *mapdata;
    VALUE vdata1, vdata2;
    VALUE vbasex, vbasey, vmap, vmapdata, vsizex, vsizey;
    int basex, basey, sizex, sizey;
    int startx, starty;
    float z;
    int i, j, x, y;
    int width, height;
    struct DXRubyImage *image;

    rb_scan_args( argc, argv, "81", &vbasex, &vbasey, &vmap, &vmapdata, &vstartx, &vstarty, &vsizex, &vsizey, &vz );

    basex = NUM2INT( vbasex );
    basey = NUM2INT( vbasey );
    sizex = NUM2INT( vsizex );
    sizey = NUM2INT( vsizey );

    Check_Type(vmap, T_ARRAY);
    Check_Type(vmapdata, T_ARRAY);

    arr1 = RARRAY_PTR( vmap );
    mapdata = RARRAY_PTR( vmapdata );

    startx = NUM2INT( vstartx );
    starty = NUM2INT( vstarty );
    z = vz == Qnil ? 0.0f : NUM2DBL( vz );

    /* イメージ配列が全部イメージかどうかをチェックしとく */
    for( i = 0; i < RARRAY_LEN(vmapdata); i++ )
    {
        if( TYPE( mapdata[i] ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( mapdata[i] ));
        image = (struct DXRubyImage *)DATA_PTR( mapdata[i] );
        if( RDATA( mapdata[i] )->dfree != (RUBY_DATA_FUNC)release_Image )
        {
          rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( mapdata[i] ));
        }
    }

    /* 幅と高さ取得 */
    width  = NUM2INT( rb_funcall(RARRAY_PTR(vmapdata)[0], rb_intern("width"), 0) );
    height = NUM2INT( rb_funcall(RARRAY_PTR(vmapdata)[0], rb_intern("height"), 0) );

    y = basey - ((starty % height) < 0 ? height + (starty % height) : (starty % height));
    /* 描画 */
    for( i = starty % height < 0 ? -1 : 0; i < sizey + (starty % height <= 0 ? 0 : 1); i++ )
    {
        int my;

        if( i + starty / height < 0 )
        {
            my = (((i + starty / height) % RARRAY_LEN( vmap )) + RARRAY_LEN( vmap )) % RARRAY_LEN( vmap );
        }
        else
        {
            my = (i + starty / height) % RARRAY_LEN( vmap );
        }
        Check_Type(arr1[my], T_ARRAY);
        arr2 = RARRAY_PTR( arr1[my] );

        x = basex - ((startx % width) < 0 ? width + (startx % width) : (startx % width));

        for( j = startx % width < 0 ? -1 : 0; j < sizex + (startx % width <= 0 ? 0 : 1); j++ )
        {
            int mx;
            int index;

            if( j + startx / width < 0 )
            {
                mx = (((j + startx / width) % RARRAY_LEN( arr1[my] )) + RARRAY_LEN( arr1[my] )) % RARRAY_LEN( arr1[my] );
            }
            else
            {
                mx = (j + startx / width) % RARRAY_LEN( arr1[my] );
            }

            if( arr2[mx] != Qnil )
            {
                index = NUM2INT( arr2[mx] );

                if( index >= RARRAY_LEN( vmapdata ) ) rb_raise(eDXRubyError, "マップチップ番号がイメージ配列の範囲を超えています - Window_drawTile");

                /* イメージオブジェクトから中身を取り出す */
                image = (struct DXRubyImage *)DATA_PTR( mapdata[index] );

                AllocSpriteList();

                /* DXRubySpriteオブジェクト設定 */
                g_SpriteStruct[g_SpriteCount].image = image;
                g_SpriteStruct[g_SpriteCount].x = x;
                g_SpriteStruct[g_SpriteCount].y = y;
                g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
                g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
                g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
                g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
                g_SpriteStruct[g_SpriteCount].centerx = image->width / 2;
                g_SpriteStruct[g_SpriteCount].centery = image->height / 2;
                g_SpriteStruct[g_SpriteCount].blendflag = 0;
                g_SpriteStruct[g_SpriteCount].fontflag = 0;
                g_SpriteStruct[g_SpriteCount].vpflag = 0;
                g_SpriteStruct[g_SpriteCount].value = mapdata[index];

                /* リストデータに追加 */
                g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
                g_SpriteList[g_SpriteCount].z = z;
                g_SpriteCount++;
            }

            x = x + width;
        }
        y = y + height;
    }

    return obj;
}


typedef struct tag_dx_TLVERTEX {
    float           x, y, z;
    D3DCOLOR        color;
    float           tu, tv;
}TLVERTX, *LPTLVERTEX;

/*--------------------------------------------------------------------
   （内部関数）画面更新
 ---------------------------------------------------------------------*/
static VALUE Window_update( VALUE obj )
{
    HRESULT hr;
    static int max;
    __int64 drawStartCounter;

    while( 1 )
    {
        hr = g_pD3DDevice->lpVtbl->TestCooperativeLevel( g_pD3DDevice );
        if( FAILED( hr ) )
        {
            if( hr == D3DERR_DEVICELOST )  /* デバイスはロスト状態である */
            {
                Sleep( 100 );
                continue;
            }
            else if( hr == D3DERR_DEVICENOTRESET ) /* デバイスはロスト状態であるがリセット可能である */
            {
                /* ここへ来た時はデバイスがリセット可能状態である */
                /* スプライトをロストさせる */
                if( g_pD3DXSprite )
                {
                    g_pD3DXSprite->lpVtbl->OnLostDevice( g_pD3DXSprite );
                }

                hr = g_pD3DDevice->lpVtbl->Reset( g_pD3DDevice, &g_D3DPP ); /* 復元を試みる */
                if( FAILED( hr ) )
                {
                    if( hr == D3DERR_DEVICELOST )
                    {
                        return obj; /* またロストした */
                    }
                    rb_raise( eDXRubyError, "DirectX APIの呼び出しに失敗しました - Reset" );
                }

                if( g_pD3DXSprite ) /* スプライト復帰 */
                {
                    g_pD3DXSprite->lpVtbl->OnResetDevice( g_pD3DXSprite );
                }
                break;
            }
            else /* DirectXの内部エラー */
            {
                rb_raise( eDXRubyError, "DirectX APIの内部エラーが発生しました - TestCooperativeLevel" );
            }
        }
        else
        {
            break;
        }
    }

    drawStartCounter = GetSystemCounter();

//    /* 動画再生中 */
//    if( g_PlayingMovie == 1 )
//    {
//        return obj;
//    }
    /* 描画対象が1つも無ければ描画処理そのものを行わない */
    if( g_SpriteCount == 0 )
    {
        /* シーンの表示 */
        if( g_WindowInfo.windowed == Qnil || g_WindowInfo.windowed == Qfalse )
        {
            g_DrawTime = 0;
            g_OldTime += g_OneSecondCount / g_WindowInfo.RefreshRate;
        }
        else
        {
            g_pD3DDevice->lpVtbl->Present( g_pD3DDevice, NULL, NULL, NULL, NULL );
        }
        return obj;
    }

    /* シーンのクリア */
    g_pD3DDevice->lpVtbl->Clear( g_pD3DDevice, 0, NULL, D3DCLEAR_TARGET,
                                 D3DCOLOR_XRGB( g_WindowInfo.r, g_WindowInfo.g, g_WindowInfo.b ), 0, 0 );

    /* シーンの描画開始 */
    if( SUCCEEDED( g_pD3DDevice->lpVtbl->BeginScene( g_pD3DDevice ) ) )
    {
        int i;
        int oldflag = 0;

        /* スプライトの描画開始 */
        g_pD3DXSprite->lpVtbl->Begin( g_pD3DXSprite, D3DXSPRITE_ALPHABLEND );

        /* 拡大縮小フィルタ設定 */
        g_pD3DDevice->lpVtbl->SetSamplerState(g_pD3DDevice, 0, D3DSAMP_MINFILTER,
                                         g_WindowInfo.minfilter);
        g_pD3DDevice->lpVtbl->SetSamplerState(g_pD3DDevice, 0, D3DSAMP_MAGFILTER,
                                         g_WindowInfo.magfilter);

        /* スプライトのz順ソート */
        SortSpriteList();

        /* スプライトリストに登録されたもののうちスプライトを順に描画 */
        for( i = 0; i < g_SpriteCount; i++ )
        {
            float angle;
            struct DXRubySprite *temp;

            temp = g_SpriteList[i].sprite;

            if( temp->vpflag == 1 ) 
            {
                g_pD3DDevice->lpVtbl->SetViewport( g_pD3DDevice, &((struct DXRubyViewport *)temp->image)->viewport );
                free( temp->image );
                continue;
            }

            if( temp->blendflag != oldflag ) 
            {
                switch( temp->blendflag )
                {
                case 0:          /* 半透明合成 */
                    g_pD3DDevice->lpVtbl->SetRenderState(g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
                    g_pD3DDevice->lpVtbl->SetRenderState(g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
                    break;
                case 1:          /* 加算合成1の設定 */
                    g_pD3DDevice->lpVtbl->SetRenderState(g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
                    g_pD3DDevice->lpVtbl->SetRenderState(g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_ONE);
                    break;
                case 2:          /* 加算合成2の設定 */
                    g_pD3DDevice->lpVtbl->SetRenderState(g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_ONE);
                    g_pD3DDevice->lpVtbl->SetRenderState(g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
                    break;
                case 3:          /* 減算合成の設定 */
                    g_pD3DDevice->lpVtbl->SetRenderState(g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_ZERO);
                    g_pD3DDevice->lpVtbl->SetRenderState(g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
                    break;
                }
            }

            oldflag = temp->blendflag;

            angle = 3.141592653589793115997963468544185161590576171875f / 180.0f * temp->angle;

            if( temp->fontflag == 0)
            {
                TLVERTX VertexDataTbl[6];
                struct DXRubyImage *image = temp->image;
                float sina = sin(angle);
                float cosa = cos(angle);
                float data1x = temp->scalex * cosa;
                float data2x = temp->scalex * sina;
                float data1y = temp->scaley * sina;
                float data2y = temp->scaley * cosa;
                float tu1 = image->x / image->texture->width;
                float tu2 = (image->x + image->width) / image->texture->width;
                float tv1 = image->y / image->texture->height;
                float tv2 = (image->y + image->height) / image->texture->height;
                float centerx = temp->centerx;
                float centery = temp->centery;
                float width = image->width;
                float height = image->height;
                float basex = temp->x + centerx;
                float basey = temp->y + centery;

                /* 頂点１ */
                VertexDataTbl[0].x =  -centerx * data1x - (-centery) * data1y + basex;
                VertexDataTbl[0].y =  -centerx * data2x + (-centery) * data2y + basey;
                /* 頂点２ */
                VertexDataTbl[1].x = VertexDataTbl[3].x =  (-centerx+width) * data1x - (-centery) * data1y + basex;
                VertexDataTbl[1].y = VertexDataTbl[3].y =  (-centerx+width) * data2x + (-centery) * data2y + basey;
                /* 頂点３ */
                VertexDataTbl[4].x =  (-centerx+width) * data1x - (-centery+height) * data1y + basex;
                VertexDataTbl[4].y =  (-centerx+width) * data2x + (-centery+height) * data2y + basey;
                /* 頂点４ */
                VertexDataTbl[2].x = VertexDataTbl[5].x =  (-centerx) * data1x - (-centery+height) * data1y + basex;
                VertexDataTbl[2].y = VertexDataTbl[5].y =  (-centerx) * data2x + (-centery+height) * data2y + basey;
                /* 頂点色 */
                VertexDataTbl[0].color  =  
                VertexDataTbl[1].color  =  
                VertexDataTbl[2].color  =  
                VertexDataTbl[3].color  =  
                VertexDataTbl[4].color  =  
                VertexDataTbl[5].color  =  D3DCOLOR_ARGB(temp->alpha,255,255,255);
                /* Ｚ座標 */
                VertexDataTbl[0].z      =  
                VertexDataTbl[1].z      =  
                VertexDataTbl[2].z      =  
                VertexDataTbl[3].z      =  
                VertexDataTbl[4].z      =  
                VertexDataTbl[5].z      =  0.0f;
                /* テクスチャ座標 */
                VertexDataTbl[0].tu     =  
                VertexDataTbl[5].tu     =  VertexDataTbl[2].tu = tu1;
                VertexDataTbl[0].tv     =  
                VertexDataTbl[1].tv     =  VertexDataTbl[3].tv =  tv1;
                VertexDataTbl[1].tu     =  VertexDataTbl[3].tu =
                VertexDataTbl[4].tu     =  tu2;
                VertexDataTbl[4].tv     =  
                VertexDataTbl[5].tv     =  VertexDataTbl[2].tv = tv2;

                /* テクスチャをセット */
                g_pD3DDevice->lpVtbl->SetTexture(g_pD3DDevice, 0, (IDirect3DBaseTexture9*)image->texture->pD3DTexture);

                /* デバイスに使用する頂点フォーマットをセット */
                g_pD3DDevice->lpVtbl->SetFVF(g_pD3DDevice, D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);

                /* 描画 */
                g_pD3DDevice->lpVtbl->DrawPrimitiveUP(g_pD3DDevice, D3DPT_TRIANGLELIST, 2, VertexDataTbl, sizeof(TLVERTX));
            }
            else
            {
                D3DVECTOR vector;
                D3DXMATRIX matrix;
                D3DXMATRIX matrix_t;
                RECT rect;

                /* 回転及び拡大縮小 */
                D3DXMatrixScaling    ( &matrix_t, temp->scalex, temp->scaley, 1 );
                D3DXMatrixRotationZ  ( &matrix  , angle );
                D3DXMatrixMultiply   ( &matrix  , &matrix_t, &matrix );

                /* 平行移動 */
                D3DXMatrixTranslation( &matrix_t, (float)temp->x + temp->centerx, (float)temp->y + temp->centery, 0 );
                D3DXMatrixMultiply   ( &matrix  , &matrix, &matrix_t );

                g_pD3DXSprite->lpVtbl->SetTransform( g_pD3DXSprite, &matrix );

                rect.left   = -temp->centerx;
                rect.top    = -temp->centery;
                rect.right  = temp->centerx;
                rect.bottom = temp->centery;
                ((struct DXRubyFontSprite *)temp->image)->font->pD3DXFont->lpVtbl->DrawText(((struct DXRubyFontSprite *)temp->image)->font->pD3DXFont, g_pD3DXSprite,
                                                        RSTRING_PTR(((struct DXRubyFontSprite *)temp->image)->str),
                                                        -1,
                                                        &rect,
                                                        DT_LEFT | DT_NOCLIP,
                                                        ((int)temp->alpha << 24) | ((struct DXRubyFontSprite *)temp->image)->color & 0x00ffffff);
                g_pD3DXSprite->lpVtbl->Flush( g_pD3DXSprite );
                xfree( temp->image );
            }
        }

        /* スプライトの描画終了 */
        g_pD3DXSprite->lpVtbl->End( g_pD3DXSprite );

        /* シーンの描画終了 */
        g_pD3DDevice->lpVtbl->EndScene( g_pD3DDevice );
    }

    if( g_WindowInfo.windowed == Qnil || g_WindowInfo.windowed == Qfalse )
    {
        g_DrawTime = GetSystemCounter() - drawStartCounter;
    }

    /* シーンの表示 */
    hr = g_pD3DDevice->lpVtbl->Present( g_pD3DDevice, NULL, NULL, NULL, NULL );

    if( g_WindowInfo.windowed == Qnil || g_WindowInfo.windowed == Qfalse )
    {
        g_OldTime = GetSystemCounter();
    }

    g_SpriteCount = 0;
    return obj;
}


/*--------------------------------------------------------------------
  （内部関数）フレーム調整
 ---------------------------------------------------------------------*/
static VALUE Window_sync( VALUE obj )
{
    __int64 NowTime;
    __int64 WaitTime;
    static __int64 BeforeSecond = 0;
    static int fps = 0;
    static int skip = 0;

    NowTime = GetSystemCounter();

    /* ウィンドウモード時 */
    if( g_WindowInfo.windowed != Qnil && g_WindowInfo.windowed != Qfalse )
    {
        g_OneFrameCount = NowTime - g_OldTime;
        if ( g_WindowInfo.fps > 0 ) /* fps指定がnil or 0の時はWait処理しない */
        {
            __int64 SleepTime;

            WaitTime = g_OneSecondCount / g_WindowInfo.fps;

            /* もう既に前回から１フレーム分の時間が経っていたら */
            if( g_OldTime + WaitTime < NowTime && skip == 0 )
            {
                /* コマ落ち制御しない場合 */
                if( g_WindowInfo.frameskip == Qfalse )
                {
                    fps++;
                    g_OldTime = NowTime;
                }
                else /* する場合 */
                {
                    /* 今回はウェイトも描画もしない */
                    skip++;
                    g_SpriteCount = 0;
                    g_OldTime = g_OldTime + WaitTime;
                }
            }
            else
            {
                /* 前回描画を飛ばしたのに今回も間に合ってない場合 */
                if( g_OldTime + WaitTime < NowTime && skip == 1 )
                {
                    /* 諦めて処理落ち */
                    g_OldTime = NowTime;
                }
                else
                {
                    __int64 TempTime;
                    /* おおまかな時間をSleepで待つ */
                    TempTime = GetSystemCounter();
                    SleepTime = (WaitTime - (TempTime - g_OldTime)) * 1000 / g_OneSecondCount;
                    if( SleepTime > 2 )
                    {
                        Sleep( (unsigned long)(SleepTime - 2) );
                    }

                    TempTime = GetSystemCounter();

                    /* ループで厳密に処理をする */
                    for ( ; ; )
                    {
                        TempTime = GetSystemCounter();
                        if( g_OldTime +  WaitTime < TempTime )
                        {
                            break;
                        }
                    }
                    g_OldTime = g_OldTime +  WaitTime;
                }
                skip = 0;
                fps++;
            }
        }
        else
        {
            g_OldTime = NowTime;
            fps++;
        }

        /* FPS値設定 */
        if( (NowTime - BeforeSecond) >= g_OneSecondCount )
        {
            BeforeSecond = NowTime;
            g_WindowInfo.fpscheck = fps;
            fps = 0;
        }
    }
    else
    {
        g_OneFrameCount = NowTime - g_OldTime + g_DrawTime;
        WaitTime = g_OneSecondCount / g_WindowInfo.RefreshRate;

        /* もう既に前回から１フレーム分の時間が経っていたら */
        if( g_OldTime +  WaitTime - g_DrawTime < NowTime && skip == 0 )
        {
            /* 今回はウェイトも描画もしない */
            skip++;
            g_SpriteCount = 0;
//            g_OldTime = NowTime;
            return obj;
        }

        skip = 0;

//        g_OldTime = g_OldTime +  WaitTime;
        fps++;

        /* FPS値設定 */
        if( (NowTime - BeforeSecond) >= g_OneSecondCount )
        {
            BeforeSecond = NowTime;
            g_WindowInfo.fpscheck = fps;
            fps = 0;
        }
    }

    return obj;
}
/*--------------------------------------------------------------------
  （内部関数）フレーム調整用カウンタ値取得
 ---------------------------------------------------------------------*/
static __int64 GetSystemCounter( void )
{
    __int64 time;

    if( g_isPerformanceCounter == 1 )
    {
        QueryPerformanceCounter( (LARGE_INTEGER *)&time );
        return time;
    }
    else
    {
        return timeGetTime();
    }
}


/*--------------------------------------------------------------------
   ウィンドウのモード（ウィンドウ/全画面）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Window_setwindowed( VALUE obj, VALUE windowed )
{
    g_WindowInfo.windowed = windowed;

    if( g_WindowInfo.created == Qtrue )
    {
        g_WindowInfo.created = Qfalse;
        Window_create( obj );
    }

    return g_WindowInfo.windowed;
}


/*--------------------------------------------------------------------
   ウィンドウのモード（ウィンドウ/全画面）を取得する。
 ---------------------------------------------------------------------*/
static VALUE Window_getwindowed( VALUE obj )
{
    return g_WindowInfo.windowed;
}


/*--------------------------------------------------------------------
   ウィンドウの位置（x座標）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Window_setx( VALUE obj, VALUE x )
{
    if( g_WindowInfo.created == Qtrue )
    {
        rb_raise( eDXRubyError, "ウィンドウ作成後に設定を変更することはできません - Window_setx" );
    }

    g_WindowInfo.x = NUM2INT( x );
    return x;
}


/*--------------------------------------------------------------------
   ウィンドウの位置（y座標）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Window_sety( VALUE obj , VALUE y )
{
    if( g_WindowInfo.created == Qtrue )
    {
        rb_raise( eDXRubyError, "ウィンドウ作成後に設定を変更することはできません - Window_sety" );
    }

    g_WindowInfo.y = NUM2INT( y );
    return y;
}


/*--------------------------------------------------------------------
   ウィンドウのサイズ（幅）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Window_setwidth( VALUE obj, VALUE vwidth )
{
    int width;

    if( g_WindowInfo.created == Qtrue )
    {
        rb_raise( eDXRubyError, "ウィンドウ作成後に設定を変更することはできません - Window_setwidth" );
    }

    width = NUM2INT( vwidth );
    if( width < 0 )
    {
        width = 0;
    }

    g_WindowInfo.width = width;
    return vwidth;
}


/*--------------------------------------------------------------------
   ウィンドウのサイズ（高さ）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Window_setheight( VALUE obj , VALUE vheight)
{
    int height;

    if( g_WindowInfo.created == Qtrue )
    {
        rb_raise( eDXRubyError, "ウィンドウ作成後に設定を変更することはできません - Window_setheight" );
    }

    height = NUM2INT( vheight );
    if( height < 0 )
    {
        height = 0;
    }

    g_WindowInfo.height = height;
    return vheight;
}


/*--------------------------------------------------------------------
   ウィンドウの位置（x座標）を返す。
 ---------------------------------------------------------------------*/
static VALUE Window_x( VALUE obj )
{
    return INT2FIX( g_WindowInfo.x );
}


/*--------------------------------------------------------------------
   ウィンドウの位置（y座標）を返す。
 ---------------------------------------------------------------------*/
static VALUE Window_y( VALUE obj )
{
    return INT2FIX( g_WindowInfo.y );
}


/*--------------------------------------------------------------------
   ウィンドウのサイズ（幅）を返す。
 ---------------------------------------------------------------------*/
static VALUE Window_width( VALUE obj )
{
    return INT2FIX( g_WindowInfo.width );
}


/*--------------------------------------------------------------------
   ウィンドウのサイズ（高さ）を返す。
 ---------------------------------------------------------------------*/
static VALUE Window_height( VALUE obj )
{
    return INT2FIX( g_WindowInfo.height );
}


/*--------------------------------------------------------------------
  ウィンドウタイトル取得
 ---------------------------------------------------------------------*/
static VALUE Window_getCaption( VALUE obj )
{
    char buf[256];

    GetWindowText( g_hWind, buf, 256 );

    return rb_str_new2( buf );
}


/*--------------------------------------------------------------------
  ウィンドウタイトル設定
 ---------------------------------------------------------------------*/
static VALUE Window_setCaption( VALUE obj, VALUE caption )
{
    Check_Type(caption, T_STRING);

    SetWindowText( g_hWind, RSTRING_PTR( caption ) );

    return caption;
}


/*--------------------------------------------------------------------
  ウィンドウのサイズ倍率を取得する
 ---------------------------------------------------------------------*/
static VALUE Window_getScale( VALUE obj )
{
    return rb_float_new( g_WindowInfo.scale );
}


/*--------------------------------------------------------------------
  ウィンドウのサイズ倍率を設定する
 ---------------------------------------------------------------------*/
static VALUE Window_setScale( VALUE obj, VALUE scale )
{
    g_WindowInfo.scale = NUM2DBL(scale);

    return scale;
}


/*--------------------------------------------------------------------
  fps値を設定する
 ---------------------------------------------------------------------*/
static VALUE Window_getrealfps( VALUE obj )
{
    return INT2FIX( g_WindowInfo.fpscheck );
}


/*--------------------------------------------------------------------
  fps値を取得する
 ---------------------------------------------------------------------*/
static VALUE Window_getfps( VALUE obj )
{
    return INT2FIX( g_WindowInfo.fpscheck );
}


/*--------------------------------------------------------------------
  fps値を設定する
 ---------------------------------------------------------------------*/
static VALUE Window_setfps( VALUE obj, VALUE fps )
{
    g_WindowInfo.fps = fps == Qnil ? 0 : NUM2INT(fps);

    return fps;
}


/*--------------------------------------------------------------------
   縮小フィルタ取得
 ---------------------------------------------------------------------*/
static VALUE Window_getMinFilter( VALUE obj )
{
    return INT2FIX( g_WindowInfo.minfilter );
}


/*--------------------------------------------------------------------
   縮小フィルタ設定
 ---------------------------------------------------------------------*/
static VALUE Window_setMinFilter( VALUE obj, VALUE minfilter )
{
    g_WindowInfo.minfilter = FIX2INT( minfilter );
    return minfilter;
}


/*--------------------------------------------------------------------
   拡大フィルタ取得
 ---------------------------------------------------------------------*/
static VALUE Window_getMagFilter( VALUE obj )
{
    return INT2FIX( g_WindowInfo.minfilter );
}


/*--------------------------------------------------------------------
   拡大フィルタ設定
 ---------------------------------------------------------------------*/
static VALUE Window_setMagFilter( VALUE obj, VALUE magfilter )
{
    g_WindowInfo.magfilter = FIX2INT( magfilter );
    return magfilter;
}


/*--------------------------------------------------------------------
  1フレームの処理負荷を％で取得する
 ---------------------------------------------------------------------*/
static VALUE Window_getload( VALUE obj )
{
    if( g_WindowInfo.fps == 0 )
    {
        return INT2FIX( 0 );
    }
    return rb_float_new( (double) ( (double)g_OneFrameCount * 100.0 / ((double)g_OneSecondCount / (double)g_WindowInfo.fps )) );
}


/*--------------------------------------------------------------------
  ウィンドウハンドルを取得する
 ---------------------------------------------------------------------*/
static VALUE Window_gethWnd( VALUE obj )
{
    return INT2NUM( (int)g_hWind );
}


/*--------------------------------------------------------------------
  フレームスキップon/offを取得する
 ---------------------------------------------------------------------*/
static VALUE Window_getframeskip( VALUE obj )
{
    return g_WindowInfo.frameskip;
}


/*--------------------------------------------------------------------
  フレームスキップon/offを設定する
 ---------------------------------------------------------------------*/
static VALUE Window_setframeskip( VALUE obj, VALUE skip )
{
    g_WindowInfo.frameskip = (skip == Qnil || skip == Qfalse) ? Qfalse : Qtrue;

    return skip;
}


/*--------------------------------------------------------------------
  ウィンドウアイコンを設定する
 ---------------------------------------------------------------------*/
static VALUE Window_loadIcon( VALUE obj, VALUE filename )
{
    Check_Type( filename, T_STRING );
    g_WindowInfo.hIcon = LoadImage( 0, RSTRING_PTR(filename), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE|LR_LOADFROMFILE );
    if( g_WindowInfo.hIcon == NULL )
    {
        rb_raise( eDXRubyError, "アイコン画像ファイルのロードに失敗しました - Window_loadIcon" );
    }

    SendMessage(g_hWind, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)g_WindowInfo.hIcon);

    return Qnil;
}


/*--------------------------------------------------------------------
  ファイルオープンダイアログを表示する
 ---------------------------------------------------------------------*/
static VALUE Window_openDialog(VALUE obj, VALUE vfilter, VALUE vtitle)
{
    OPENFILENAME OFN; 
    char buf[MAX_PATH*2];
	VALUE filter;
	buf[0] = 0;

	Check_Type(vfilter, T_ARRAY);
	Check_Type(vtitle, T_STRING);

	ZeroMemory(&OFN,sizeof(OPENFILENAME));
    OFN.lStructSize = sizeof(OPENFILENAME); 
    OFN.hwndOwner = g_hWind;

	OFN.lpstrFilter = "すべてのファイル (*.*)\0*.*\0\0";
	filter = rb_ary_join( vfilter, rb_str_new( "\0", 1 ) );
	filter = rb_str_cat( filter, "\0", 1 );
	OFN.lpstrFilter = RSTRING_PTR(filter);
    OFN.lpstrFile = buf;
    OFN.nMaxFile = MAX_PATH*2;
    OFN.Flags = OFN_FILEMUSTEXIST;
	OFN.lpstrTitle = RSTRING_PTR(vtitle);
	OFN.lpstrDefExt = 0;
	if( !GetOpenFileName(&OFN) )
	{
        return Qnil;
	}

	return rb_str_new2( buf );
}


/*--------------------------------------------------------------------
  ファイルセーブダイアログを表示する
 ---------------------------------------------------------------------*/
static VALUE Window_saveDialog(VALUE obj, VALUE vfilter, VALUE vtitle)
{
    OPENFILENAME OFN; 
    char buf[MAX_PATH*2];
	VALUE filter;
	buf[0] = 0;

	Check_Type(vfilter, T_ARRAY);
	Check_Type(vtitle, T_STRING);

	ZeroMemory(&OFN,sizeof(OPENFILENAME));
    OFN.lStructSize = sizeof(OPENFILENAME); 
    OFN.hwndOwner = g_hWind;

	OFN.lpstrFilter = "すべてのファイル (*.*)\0*.*\0\0";
	filter = rb_ary_join( vfilter, rb_str_new( "\0", 1 ) );
	filter = rb_str_cat( filter, "\0", 1 );
	OFN.lpstrFilter = RSTRING_PTR(filter);
    OFN.lpstrFile = buf;
    OFN.nMaxFile = MAX_PATH*2;
    OFN.Flags = OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT;
	OFN.lpstrTitle = RSTRING_PTR(vtitle);
	OFN.lpstrDefExt = 0;
	if( !GetSaveFileName(&OFN) )
	{
        return Qnil;
	}

	return rb_str_new2( buf );
}


/*--------------------------------------------------------------------
   終了時に実行する
 ---------------------------------------------------------------------*/
static VALUE Window_shutdown( VALUE obj )
{
    /* アイコンリソース解放 */
    if( g_WindowInfo.hIcon != 0 )
    {
        DestroyIcon(g_WindowInfo.hIcon);
    }

    /* マウス状態復元 */
    if( g_WindowInfo.enablemouse == Qfalse )
    {
        int c;
        c = ShowCursor( TRUE );
        while( c < 0 ) c = ShowCursor( TRUE );
    }

    /* リスト解放 */
    g_SpriteCount = 0;

//    /* 動画再生用インターフェイスの解放 */
//    if( g_pGraphBuilder != NULL )
//    {
//        g_pMediaControl->lpVtbl->Stop( g_pMediaControl );
//        RELEASE( g_pMediaEventEx );
//        RELEASE( g_pVideoWindow );
//        RELEASE( g_pMediaControl );
//        RELEASE( g_pGraphBuilder );
//    }

    /* DirectX解放 */
    Window_DirectXRelease();

    /* ウインドウ・クラスの登録解除 */
    UnregisterClass( "DXRuby", g_hInstance );

    /* フレーム調整の後始末 */
    timeEndPeriod( 1 );

	g_iRefAll--;
	if( g_iRefAll == 0 )
	{
		CoUninitialize();
	}

    return obj;
}


/*--------------------------------------------------------------------
   DirectXオブジェクトを解放する
 ---------------------------------------------------------------------*/
void Window_DirectXRelease()
{
    int i;
    HRESULT hr;

    /* スプライトオブジェクトの使用終了 */
    if( g_pD3DXSprite )
    {
        g_pD3DXSprite->lpVtbl->OnLostDevice( g_pD3DXSprite );
    }

    /* スプライトオブジェクト破棄 */
    RELEASE( g_pD3DXSprite );

    for( i = 0 ; i < g_JoystickCount; i++ )
    {
        /* DirectInputDevice(JoyPad)オブジェクトの使用終了 */
        if( g_pDIDJoyPad[i] )
        {
            g_pDIDJoyPad[i]->lpVtbl->Unacquire( g_pDIDJoyPad[i] ); 
        }

        /* DirectInputDevide(JoyPad)オブジェクトの破棄 */
        RELEASE( g_pDIDJoyPad[i] );
    }

    /* DirectInputDevice(Keyboard)オブジェクトの使用終了 */
    if( g_pDIDKeyBoard )
    {
           g_pDIDKeyBoard->lpVtbl->Unacquire( g_pDIDKeyBoard ); 
    }

    /* DirectInputDevide(Keyboard)オブジェクトの破棄 */
    RELEASE( g_pDIDKeyBoard );

    /* DirectInputオブジェクトの破棄 */
    RELEASE( g_pDInput );

    /* Direct3D Deviceオブジェクトの破棄 */
    RELEASE( g_pD3DDevice );

	/* Direct3Dオブジェクトの破棄 */
    RELEASE( g_pD3D );
}


/*--------------------------------------------------------------------
   スクリーンショットを撮る
 ---------------------------------------------------------------------*/
static VALUE Window_getScreenShot( int argc, VALUE *argv, VALUE obj )
{
	HRESULT hr;
	D3DDISPLAYMODE dmode;
	LPDIRECT3DSURFACE9 pSurface;
	RECT rect;
    VALUE filename, format;

    rb_scan_args( argc, argv, "11", &filename, &format );

    Check_Type( filename, T_STRING );

    /* 現在のディスプレイのフォーマットなどを取得 */
	hr = g_pD3D->lpVtbl->GetAdapterDisplayMode(g_pD3D, D3DADAPTER_DEFAULT, &dmode);
	if( FAILED( hr ) )
	{
        rb_raise( eDXRubyError, "キャプチャに失敗しました - GetAdapterDisplayMode" );
    }

	/* キャプチャ用サーフェス作成 */
	hr = g_pD3DDevice->lpVtbl->CreateOffscreenPlainSurface(g_pD3DDevice,
			dmode.Width,
			dmode.Height,
			D3DFMT_A8R8G8B8,
			D3DPOOL_SCRATCH, &pSurface, NULL);
	if( FAILED( hr ) )
	{
        rb_raise( eDXRubyError, "キャプチャに失敗しました - CreateOffscreenPlainSurface" );
    }
	/* キャプチャ */
	hr = g_pD3DDevice->lpVtbl->GetFrontBufferData(g_pD3DDevice, 0, pSurface);
	if( FAILED( hr ) )
	{
		RELEASE(pSurface);
        rb_raise( eDXRubyError, "キャプチャに失敗しました - GetFrontBufferData" );
	}

	/* サーフェスの保存 */
	if( g_D3DPP.Windowed )
	{
		POINT p = { 0, 0 };
		ClientToScreen(g_hWind  , &p);
		rect.left = p.x; rect.top = p.y;
		p.x = g_D3DPP.BackBufferWidth * g_WindowInfo.scale;
		p.y = g_D3DPP.BackBufferHeight * g_WindowInfo.scale;
		ClientToScreen(g_hWind, &p);
		rect.right = p.x; rect.bottom = p.y;
	}
	hr = D3DXSaveSurfaceToFile(
			RSTRING_PTR( filename ),							/* 保存ファイル名 */
			format == Qnil ? D3DXIFF_JPG : FIX2INT( format ),	/* ファイルフォーマット */
			pSurface,											/* 保存するサーフェス */
			NULL,												/* パレット */
			g_D3DPP.Windowed ? &rect : NULL);					/* 保存領域 */
	RELEASE(pSurface);
	if( FAILED( hr ) )
	{
        rb_raise( eDXRubyError, "キャプチャに失敗しました - D3DXSaveSurfaceToFile" );
    }

	return obj;
}


///*--------------------------------------------------------------------
//  動画を再生する
// ---------------------------------------------------------------------*/
//static VALUE Window_playMovie( VALUE obj, VALUE vfilename )
//{
//    HRESULT hr;
//    WCHAR filename [ MAX_PATH ] ;
//
//    Check_Type( vfilename, T_STRING );
//    MultiByteToWideChar ( CP_ACP, 0, RSTRING_PTR(vfilename), -1, filename, MAX_PATH );
//
//    // IGraphBuilderインターフェイスの取得
//    hr = CoCreateInstance( &CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (void **)(&g_pGraphBuilder) );
//
//    if( FAILED( hr ) )
//    {
//        rb_raise( eDXRubyError, "IGraphBuilderインターフェイスの取得に失敗しました - Window_playMovie" );
//    }
//
//    // IMediaControlインターフェイスの取得
//    hr = g_pGraphBuilder->lpVtbl->QueryInterface( g_pGraphBuilder, &IID_IMediaControl, (void**)(&g_pMediaControl) );
//    if( FAILED( hr ) )
//    {
//        rb_raise( eDXRubyError, "IMediaControlインターフェイスの取得に失敗しました - Window_playMovie" );
//    }
//
//    hr = g_pGraphBuilder->lpVtbl->QueryInterface( g_pGraphBuilder, &IID_IVideoWindow, (LPVOID *)&g_pVideoWindow );
//    if( FAILED( hr ) )
//    {
//        rb_raise( eDXRubyError, "IVideoWindowインターフェイスの取得に失敗しました - Window_playMovie" );
//    }
//
//    hr = g_pGraphBuilder->lpVtbl->RenderFile( g_pGraphBuilder, filename, NULL );
//    if( FAILED( hr ) )
//    {
//        rb_raise( eDXRubyError, "ファイルの読み込みに失敗しました - Window_playMovie" );
//    }
//
//    g_pVideoWindow->lpVtbl->put_Owner( g_pVideoWindow, (OAHWND)g_hWind );
//    g_pVideoWindow->lpVtbl->put_WindowStyle( g_pVideoWindow, WS_CHILD|WS_CLIPSIBLINGS );
//   g_pVideoWindow->lpVtbl->SetWindowPosition( g_pVideoWindow,0, 0, g_WindowInfo.width, g_WindowInfo.height );
//
//    // メディア再生
//    hr = g_pMediaControl->lpVtbl->Run( g_pMediaControl );
//    if( FAILED( hr ) )
//    {
//        rb_raise( eDXRubyError, "再生に失敗しました - Window_playMovie" );
//    }
//
//    g_PlayingMovie = 1;
////    g_pMediaControl->lpVtbl->Stop( g_pMediaControl );
//    return obj;
//}



/*********************************************************************
 * Imageクラス
 *
 * 描画用の画像を保持するクラス。
 * ファイル名を渡してloadすると読み込まれ、Window::drawに渡して描画する。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void release_Image( struct DXRubyImage* image )
{
    /* テクスチャオブジェクトの開放 */
    if( image )
    {
    	if( image->texture )
    	{
	    	image->texture->refcount--;
	    	if( image->texture->refcount == 0 )
	    	{
				RELEASE( image->texture->pD3DTexture );
				free( image->texture );
	    	}
    	}
        free( image );
        image = NULL;

    	g_iRefAll--;
    	if( g_iRefAll == 0 )
    	{
    		CoUninitialize();
    	}
    }
}


/*--------------------------------------------------------------------
   Imgaeクラスのallocate。メモリを確保する為にinitialize前に呼ばれる。
 ---------------------------------------------------------------------*/
static VALUE Image_allocate( VALUE klass )
{
    VALUE obj;
    struct DXRubyImage *image;

    /* DXRubyImageのメモリ取得＆Imageオブジェクト生成 */
    image = malloc(sizeof(struct DXRubyImage));
    if( image == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Image_allocate" );
    obj = Data_Wrap_Struct(klass, 0, release_Image, image);

    /* とりあえずテクスチャオブジェクトはNULLにしておく */
    image->texture = NULL;

    return obj;
}


/*--------------------------------------------------------------------
    イメージのデータ設定(内部処理用box描画)
 ---------------------------------------------------------------------*/
static void fill( int x1, int y1, int x2, int y2, int col, struct DXRubyImage *image )
{
    D3DLOCKED_RECT texrect;
    int x, y;
    RECT rect;

    rect.left = x1 + image->x;
    rect.top = y1 + image->y;
    rect.right = x2 + image->x + 1;
    rect.bottom = y2 + image->y + 1;
    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, &rect, 0 );
    for( y = 0; y <= y2 - y1; y++ )
    {
        for( x = 0; x <= x2 - x1; x++ )
        {
            *((int*)((char *)texrect.pBits + x * 4 + y * texrect.Pitch)) = col;
        }
    }
    image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );
    return;
}


/*--------------------------------------------------------------------
    配列から色取得
 ---------------------------------------------------------------------*/
static int array2color( VALUE color )
{
    int col;

    Check_Type(color, T_ARRAY);
    if( RARRAY_LEN( color ) < 3 ) rb_raise( eDXRubyError, "配列から色の取得に失敗しました - getcolor" );

    if( RARRAY_LEN( color ) == 3 )
    {
        col = D3DCOLOR_ARGB(255, NUM2INT(rb_ary_entry(color, 0)), NUM2INT(rb_ary_entry(color, 1)), NUM2INT(rb_ary_entry(color, 2)));
    }
    else
    {
        col = D3DCOLOR_ARGB(NUM2INT(rb_ary_entry(color, 0)), NUM2INT(rb_ary_entry(color, 1)), 
                            NUM2INT(rb_ary_entry(color, 2)), NUM2INT(rb_ary_entry(color, 3)));
    }
    return col;
}


/*--------------------------------------------------------------------
   ImgaeクラスのInitialize
 ---------------------------------------------------------------------*/
static VALUE Image_initialize( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;
    struct DXRubyTexture *texture;
    HRESULT hr;
    D3DSURFACE_DESC desc;
    VALUE vwidth, vheight, vary;
    int col = 0, width, height;

    rb_scan_args( argc, argv, "21", &vwidth, &vheight, &vary );

    width = NUM2INT( vwidth );
    height = NUM2INT( vheight );

    if( width <= 0 || height <= 0 )
    {
        rb_raise( eDXRubyError, "Imageオブジェクトの作成に失敗しました - Image_initialize" );
    }

    if( vary != Qnil )
    {
        Check_Type( vary, T_ARRAY );
        col = array2color( vary );
    }

    /* テクスチャメモリ取得 */
    texture = (struct  DXRubyTexture *)malloc( sizeof( struct DXRubyTexture ) );

    if( texture == NULL )
    {
        rb_raise( eDXRubyError, "画像用メモリの取得に失敗しました - Image_initialize" );
    }

    /* テクスチャオブジェクトを作成する */
    hr = D3DXCreateTexture( g_pD3DDevice, width, height,
                                      1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
                                      &texture->pD3DTexture);

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "テクスチャの作成に失敗しました - Image_initialize" );
    }

    texture->refcount = 1;

    texture->pD3DTexture->lpVtbl->GetLevelDesc(texture->pD3DTexture, 0, &desc );
    texture->width = desc.Width;
    texture->height = desc.Height;

    /* Imageオブジェクト設定 */
    Data_Get_Struct( obj, struct DXRubyImage, image );

    image->texture = texture;
    image->x = 0;
    image->y = 0;
    image->width = width;
    image->height = height;

    g_iRefAll++;

    fill( 0, 0, width - 1, height - 1, col, image );

    return obj;
}


/*--------------------------------------------------------------------
   Imgaeクラスのload
 ---------------------------------------------------------------------*/
static VALUE Image_load( int argc, VALUE *argv, VALUE klass )
{
    struct DXRubyImage *image;
    struct DXRubyTexture *texture;
    D3DXIMAGE_INFO srcinfo;
    D3DSURFACE_DESC desc;
    HRESULT hr;
    VALUE filename, vx, vy, vwidth, vheight, obj;
    int x, y, width, height;

    /* デバイスオブジェクトの初期化チェック */
    if( g_pD3DDevice == NULL )
    {
        rb_raise( eDXRubyError, "DirectX Graphicsが初期化されていません" );
    }

    rb_scan_args( argc, argv, "14", &filename, &vx, &vy, &vwidth, &vheight );

    Check_Type(filename, T_STRING);

    /* ファイル情報取得 */
    hr = D3DXGetImageInfoFromFile( RSTRING_PTR( filename ), &srcinfo );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ファイルの読み込みに失敗しました - %s", RSTRING_PTR( filename ) );
    }

    if( vx == Qnil )
    {
        x = 0;
        y = 0;
        width = srcinfo.Width;
        height = srcinfo.Height;
    }
    else
    {
        x = NUM2INT( vx );
        y = vy == Qnil ? 0 : NUM2INT( vy );
        if( x < 0 || x >= srcinfo.Width || y < 0 || y >= srcinfo.Height )
        {
            rb_raise( eDXRubyError, "画像の原点位置が異常値です(x=%d,y=%d, tex_width=%d,tex_height=%d) - Image_load", x, y, srcinfo.Width, srcinfo.Height );
        }
        width = vwidth == Qnil ? srcinfo.Width - x : NUM2INT( vwidth );
        height = vheight == Qnil ? srcinfo.Height - y : NUM2INT( vheight );
        if( srcinfo.Width - x < width || x + width > srcinfo.Width || srcinfo.Height - y < height || y + height > srcinfo.Height ||
            width < 0 || height < 0 )
        {
            rb_raise( eDXRubyError, "画像のサイズが異常値です - Image_load" );
        }
    }

    /* テクスチャロード */
    texture = Image_textureload( RSTRING_PTR( filename ), &srcinfo );
    texture->refcount = 1;

    texture->pD3DTexture->lpVtbl->GetLevelDesc(texture->pD3DTexture, 0, &desc );
    texture->width = desc.Width;
    texture->height = desc.Height;

    /* Imageオブジェクト設定 */
    image = malloc(sizeof(struct DXRubyImage));
    if( image == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Image_load" );
    obj = Data_Wrap_Struct(cImage, 0, release_Image, image);

    image->texture = texture;
    image->x = x;
    image->y = y;
    image->width = width;
    image->height = height;

    g_iRefAll++;

    return obj;
}


/*--------------------------------------------------------------------
   ImgaeクラスのloadFromFileInMemory
 ---------------------------------------------------------------------*/
static VALUE Image_loadFromFileInMemory( VALUE klass, VALUE vstring )
{
    struct DXRubyImage *image;
    struct DXRubyTexture *texture;
    D3DXIMAGE_INFO srcinfo;
    D3DSURFACE_DESC desc;
    HRESULT hr;
    VALUE obj;
    int size, x, y, width, height;

    /* デバイスオブジェクトの初期化チェック */
    if( g_pD3DDevice == NULL )
    {
        rb_raise( eDXRubyError, "DirectX Graphicsが初期化されていません" );
    }

    Check_Type(vstring, T_STRING);
    size = RSTRING_LEN( vstring );

    /* ファイル情報取得 */
    hr = D3DXGetImageInfoFromFileInMemory( RSTRING_PTR( vstring ), size, &srcinfo );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "イメージデータの読み込みに失敗しました - Image_loadFromFileInMemory" );
    }

    /* テクスチャメモリ取得 */
    texture = (struct  DXRubyTexture *)malloc( sizeof( struct DXRubyTexture ) );

    if( texture == NULL )
    {
        rb_raise( eDXRubyError, "画像用メモリの取得に失敗しました - Image_loadFromFileInMemory" );
    }

    /* ファイルを読み込んでテクスチャオブジェクトを作成する */
    hr = D3DXCreateTextureFromFileInMemoryEx( g_pD3DDevice, RSTRING_PTR( vstring ), size, srcinfo.Width, srcinfo.Height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
                                      D3DX_DEFAULT,  D3DX_DEFAULT, 0,
                                      0, 0, &texture->pD3DTexture);

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "イメージデータの読み込みに失敗しました - Image_loadFromFileInMemory" );
    }

    texture->refcount = 1;

    texture->pD3DTexture->lpVtbl->GetLevelDesc(texture->pD3DTexture, 0, &desc );
    texture->width = desc.Width;
    texture->height = desc.Height;

    /* Imageオブジェクト設定 */
    image = malloc(sizeof(struct DXRubyImage));
    if( image == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Image_loadFromFileInMemory" );
    obj = Data_Wrap_Struct(cImage, 0, release_Image, image);

    image->texture = texture;
    image->x = 0;
    image->y = 0;
    image->width = srcinfo.Width;
    image->height = srcinfo.Height;

    g_iRefAll++;

    return obj;
}


/*--------------------------------------------------------------------
   Imgaeオブジェクトの分割作成
 ---------------------------------------------------------------------*/
static VALUE Image_loadToArray( VALUE klass, VALUE filename, VALUE vx, VALUE vy )
{
    struct DXRubyImage *image;
    struct DXRubyTexture *texture;
    int i, j, x, y;
    VALUE obj;
    VALUE array;
    D3DXIMAGE_INFO srcinfo;
	D3DSURFACE_DESC desc;
    HRESULT hr;

	Check_Type(filename, T_STRING);
	x = NUM2INT( vx );
	y = NUM2INT( vy );

    if( x <= 0 || y <= 0 ) rb_raise( eDXRubyError, "画像の数指定が異常値です(x=%d,y=%d) - Image_loadToArray", x, y );

    /* ファイル情報取得 */
    hr = D3DXGetImageInfoFromFile( RSTRING_PTR( filename ), &srcinfo );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ファイルの読み込みに失敗しました - %s", RSTRING_PTR( filename ) );
    }

    /* テクスチャロード */
    texture = Image_textureload( RSTRING_PTR( filename ), &srcinfo );
	texture->refcount = x * y;

	texture->pD3DTexture->lpVtbl->GetLevelDesc(texture->pD3DTexture, 0, &desc );
	texture->width = desc.Width;
    texture->height = desc.Height;

    /* Ruby配列作成 */
    array = rb_ary_new();

    for( i = 0; i < y; i++ )
    {
        for( j = 0; j < x; j++ )
        {
            /* DXRubyImageのメモリ取得＆Imageオブジェクト生成 */
            image = malloc(sizeof(struct DXRubyImage));
            if( image == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Image_loadToArray" );
            obj = Data_Wrap_Struct(cImage, 0, release_Image, image);

            image->texture = texture;
            image->x = j * srcinfo.Width / x;
            image->y = i * srcinfo.Height / y;
            image->width = srcinfo.Width / x;
            image->height = srcinfo.Height / y;

            rb_ary_push(array, obj);
			g_iRefAll++;
        }
    }

    return array;
}


/*--------------------------------------------------------------------
   Textureロード
 ---------------------------------------------------------------------*/
static struct DXRubyTexture *Image_textureload( char *filename, D3DXIMAGE_INFO *psrcinfo)
{
    HRESULT hr;
    struct DXRubyTexture *texture;

    /* テクスチャメモリ取得 */
    texture = (struct  DXRubyTexture *)malloc( sizeof( struct DXRubyTexture ) );

    if( texture == NULL )
    {
        rb_raise( eDXRubyError, "画像用メモリの取得に失敗しました - Image_textureload" );
    }

    /* ファイルを読み込んでテクスチャオブジェクトを作成する */
    hr = D3DXCreateTextureFromFileEx( g_pD3DDevice, filename,psrcinfo->Width, psrcinfo->Height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
                                      D3DX_DEFAULT,  D3DX_DEFAULT, 0,
                                      0, 0, &texture->pD3DTexture);

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ファイルの読み込みに失敗しました - %s", filename );
    }

    return texture;
}


/*--------------------------------------------------------------------
   配列からイメージを作る
 ---------------------------------------------------------------------*/
static VALUE Image_createFromArray( VALUE klass, VALUE vwidth, VALUE vheight, VALUE array )
{
    struct DXRubyImage *image;
    struct DXRubyTexture *texture;
    HRESULT hr;
    int i, j, x, y;
    D3DLOCKED_RECT LockedRect;
    VALUE obj;
    int width, height;
    D3DSURFACE_DESC desc;

    /* デバイスオブジェクトの初期化チェック */
    if( g_pD3DDevice == NULL )
    {
        rb_raise( eDXRubyError, "DirectX Graphicsが初期化されていません" );
    }

    width = NUM2INT( vwidth );
    height = NUM2INT( vheight );
    Check_Type(array, T_ARRAY);

    if( width <= 0 || height <= 0 ) rb_raise( eDXRubyError, "画像のサイズ指定が異常値です(width=%d,height=%d) - Image_loadToArray", width, height );

    /* テクスチャメモリ取得 */
    texture = (struct  DXRubyTexture *)malloc( sizeof( struct DXRubyTexture ) );
    if( texture == NULL )
    {
        rb_raise( eDXRubyError, "画像用メモリの取得に失敗しました - Image_textureload" );
    }

    /* テクスチャサイズ割り出し */
    for( x = 1; x < width;  x = x * 2 );
    for( y = 1; y < height; y = y * 2 );

    /* テクスチャオブジェクトを作成する */
    hr = D3DXCreateTexture( g_pD3DDevice, x, y,
                            1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
                            &texture->pD3DTexture);
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "テクスチャの作成に失敗しました - Image_initialize" );
    }

    /* テクスチャロック */
    hr = texture->pD3DTexture->lpVtbl->LockRect( texture->pD3DTexture, 0, &LockedRect, NULL, 0 );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "サーフェイスのロックに失敗しました - LockRect" );
    }

    /* 書き込み */
    for( i = 0; i < height; i++ )
    {
        for( j = 0; j < width * 4; j += 4 )
        {
            int a1 = NUM2INT(rb_ary_entry(array, j + i * width * 4));
            int a2 = NUM2INT(rb_ary_entry(array, j + i * width * 4 + 1));
            int a3 = NUM2INT(rb_ary_entry(array, j + i * width * 4 + 2));
            int a4 = NUM2INT(rb_ary_entry(array, j + i * width * 4 + 3));
            *((int*)((char *)LockedRect.pBits + j + i * LockedRect.Pitch)) = D3DCOLOR_ARGB(a1, a2, a3, a4);
        }
    }

    /* テクスチャアンロック */
    texture->pD3DTexture->lpVtbl->UnlockRect( texture->pD3DTexture, 0 );

    texture->refcount = 1;
    texture->pD3DTexture->lpVtbl->GetLevelDesc( texture->pD3DTexture, 0, &desc );
    texture->width = desc.Width;
    texture->height = desc.Height;

    /* DXRubyImageのメモリ取得＆Imageオブジェクト生成 */
    image = malloc(sizeof(struct DXRubyImage));
    if( image == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Image_createFromArray" );
    obj = Data_Wrap_Struct(cImage, 0, release_Image, image);

    image->texture = texture;
    image->x = 0;
    image->y = 0;
    image->width = width;
    image->height = height;

    g_iRefAll++;

    return obj;
}


/*--------------------------------------------------------------------
   Imgaeオブジェクトのslice
 ---------------------------------------------------------------------*/
static VALUE Image_slice_instance( int argc, VALUE *argv, VALUE vsrcimage )
{
    struct DXRubyImage *srcimage;
    struct DXRubyImage *image;
    struct DXRubyTexture *texture;
    D3DXIMAGE_INFO srcinfo;
    HRESULT hr;
    VALUE vx, vy, vwidth, vheight, obj;
    int x, y, width, height;

    rb_scan_args( argc, argv, "04", &vx, &vy, &vwidth, &vheight );

    Data_Get_Struct( vsrcimage, struct DXRubyImage, srcimage );

    if( vx == Qnil )
    {
        x = 0;
        y = 0;
        width = srcimage->width;
        height = srcimage->height;
    }
    else
    {
        x = NUM2INT( vx );
        y = vy == Qnil ? 0 : NUM2INT( vy );
        if( x < 0 || x >= srcimage->width || y < 0 || y >= srcimage->height )
        {
            rb_raise( eDXRubyError, "画像の原点位置が異常値です(x=%d,y=%d, tex_width=%d,tex_height=%d) - Image_slice", x, y, srcimage->width, srcimage->height );
        }
        width = vwidth == Qnil ? srcimage->width - x : NUM2INT( vwidth );
        height = vheight == Qnil ? srcimage->height - y : NUM2INT( vheight );
        if( srcimage->width - x < width || x + width > srcimage->width || srcimage->height - y < height || y + height > srcimage->height ||
            width < 0 || height < 0 )
        {
            rb_raise( eDXRubyError, "画像のサイズが異常値です - Image_slice" );
        }
    }

    /* テクスチャロード */
    texture = srcimage->texture;
    texture->refcount += 1;

    /* Imageオブジェクト設定 */
    image = malloc(sizeof(struct DXRubyImage));
    if( image == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Image_slice" );
    obj = Data_Wrap_Struct(cImage, 0, release_Image, image);

    image->texture = texture;
    image->x = x + srcimage->x;
    image->y = y + srcimage->y;
    image->width = width;
    image->height = height;

    g_iRefAll++;

    return obj;
}


/*--------------------------------------------------------------------
   イメージのデータ取得
 ---------------------------------------------------------------------*/
static VALUE Image_getPixel( VALUE obj, VALUE vx, VALUE vy )
{
    struct DXRubyImage *image;
    VALUE ary[4];
    struct DXRubyColor a;
    D3DLOCKED_RECT texrect;
    int x, y;
    RECT rect;

    x = NUM2INT( vx );
    y = NUM2INT( vy );

    Data_Get_Struct( obj, struct DXRubyImage, image );

    if( x < 0 || x >= image->width || y < 0 || y >= image->height )
    {
        ary[0] = ary[1] = ary[2] = ary[3] = INT2FIX( 0 );
        return rb_ary_new4( 4, ary );
    }

    rect.left = x + image->x;
    rect.top = y + image->y;
    rect.right = x + image->x + 1;
    rect.bottom = y + image->y + 1;
    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, &rect, D3DLOCK_READONLY );

    a = *(struct DXRubyColor *)texrect.pBits;

    image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );

    ary[0] = INT2FIX( a.alpha );
    ary[1] = INT2FIX( a.red );
    ary[2] = INT2FIX( a.green );
    ary[3] = INT2FIX( a.blue );

    return rb_ary_new4( 4, ary );
}


/*--------------------------------------------------------------------
   イメージのデータ設定
 ---------------------------------------------------------------------*/
static VALUE Image_setPixel( VALUE obj, VALUE vx, VALUE vy, VALUE color )
{
    struct DXRubyImage *image;
    int a1, a2, a3, a4;
    D3DLOCKED_RECT texrect;
    int x, y;
    RECT rect;

    x = NUM2INT( vx );
    y = NUM2INT( vy );
    Check_Type(color, T_ARRAY);

    Data_Get_Struct( obj, struct DXRubyImage, image );

    if( x < 0 || x >= image->width || y < 0 || y >= image->height )
    {
        return obj;
    }

    rect.left = x + image->x;
    rect.top = y + image->y;
    rect.right = x + image->x + 1;
    rect.bottom = y + image->y + 1;
    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, &rect, 0 );

    *((int*)((char *)texrect.pBits)) = array2color( color );

    image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );

    return obj;
}


/*--------------------------------------------------------------------
   イメージの色比較
 ---------------------------------------------------------------------*/
static VALUE Image_compare( VALUE obj, VALUE vx, VALUE vy, VALUE color )
{
    struct DXRubyImage *image;
    DWORD a;
    D3DLOCKED_RECT texrect;
    int x, y, col;
    int a1, a2, a3, a4;
    RECT rect;

    x = NUM2INT( vx );
    y = NUM2INT( vy );
    Check_Type(color, T_ARRAY);

    Data_Get_Struct( obj, struct DXRubyImage, image );

    if( x < 0 || x >= image->width || y < 0 || y >= image->height )
    {
        return INT2FIX( 0 );
    }

    col = array2color( color );

    rect.left = x + image->x;
    rect.top = y + image->y;
    rect.right = x + image->x + 1;
    rect.bottom = y + image->y + 1;
    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, &rect, D3DLOCK_READONLY );

    a = *(LPDWORD)texrect.pBits;

    image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );

    if( RARRAY_LEN( color ) == 3 )
    {
        
        if( (a & 0x00ffffff) == ((DWORD)col & 0x00ffffff) )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }
    }
    else
    {
        if( a == (DWORD)col )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }
    }
}


/*--------------------------------------------------------------------
    イメージのデータ設定(box描画塗りつぶさない)
 ---------------------------------------------------------------------*/
static VALUE Image_box( VALUE obj, VALUE vx1, VALUE vy1, VALUE vx2, VALUE vy2, VALUE color )
{
    struct DXRubyImage *image;
    D3DLOCKED_RECT texrect;
    int x, y, x1, y1, x2, y2;
    int col;
    RECT rect;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    x1 = NUM2INT( vx1 );
    y1 = NUM2INT( vy1 );
    x2 = NUM2INT( vx2 );
    y2 = NUM2INT( vy2 );

    /* 左上から右下の指定に修正 */
    if( x1 > x2 )
    {
        x = x2;
        x2 = x1;
        x1 = x;
    }
    if( y1 > y2 )
    {
        y = y2;
        y2 = y1;
        y1 = y;
    }

    /* 範囲外の指定は無視 */
    if( x1 > image->width - 1 || x2 < 0 || y1 > image->height - 1 || y2 < 0)
    {
        return obj;
    }

    /* クリップ */
    if( x1 < 0 )
    {
        x1 = 0;
    }
    if( x2 > image->width - 1 )
    {
        x2 = image->width - 1;
    }
    if( y1 < 0 )
    {
        y1 = 0;
    }
    if( y2 > image->height - 1 )
    {
        y2 = image->height - 1;
    }

    Check_Type(color, T_ARRAY);
    col = array2color( color );

    rect.left = x1 + image->x;
    rect.top = y1 + image->y;
    rect.right = x2 + image->x + 1;
    rect.bottom = y2 + image->y + 1;
    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, &rect, 0 );
    for( y = 0; y <= y2 - y1; y++ )
    {
        *((int*)((char *)texrect.pBits + y * texrect.Pitch)) = col;
        *((int*)((char *)texrect.pBits + (x2 - x1)* 4 + y * texrect.Pitch)) = col;
    }
    for( x = 0; x <= x2 - x1; x++ )
    {
        *((int*)((char *)texrect.pBits + x * 4)) = col;
        *((int*)((char *)texrect.pBits + x * 4 + (y2 - y1) * texrect.Pitch)) = col;
    }
    image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );

    return obj;
}


/*--------------------------------------------------------------------
    イメージのデータ設定(box描画塗りつぶす)
 ---------------------------------------------------------------------*/
static VALUE Image_boxFill( VALUE obj, VALUE vx1, VALUE vy1, VALUE vx2, VALUE vy2, VALUE color )
{
    struct DXRubyImage *image;
    int x, y, x1, y1, x2, y2;
    int col;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    x1 = NUM2INT( vx1 );
    y1 = NUM2INT( vy1 );
    x2 = NUM2INT( vx2 );
    y2 = NUM2INT( vy2 );

    /* 左上から右下の指定に修正 */
    if( x1 > x2 )
    {
        x = x2;
        x2 = x1;
        x1 = x;
    }
    if( y1 > y2 )
    {
        y = y2;
        y2 = y1;
        y1 = y;
    }

    /* 範囲外の指定は無視 */
    if( x1 > image->width - 1 || x2 < 0 || y1 > image->height - 1 || y2 < 0)
    {
        return obj;
    }

    /* クリップ */
    if( x1 < 0 )
    {
        x1 = 0;
    }
    if( x2 > image->width - 1 )
    {
        x2 = image->width - 1;
    }
    if( y1 < 0 )
    {
        y1 = 0;
    }
    if( y2 > image->height - 1 )
    {
        y2 = image->height - 1;
    }

    Check_Type(color, T_ARRAY);
    col = array2color( color );

    fill( x1, y1, x2, y2, col, image );

    return obj;
}


/*--------------------------------------------------------------------
    イメージのデータ設定(全部塗りつぶす)
 ---------------------------------------------------------------------*/
static VALUE Image_fill( VALUE obj, VALUE color )
{
    struct DXRubyImage *image;
    int col;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    Check_Type(color, T_ARRAY);
    col = array2color( color );

    fill( 0, 0, image->width-1, image->height-1, col, image );

    return obj;
}


/*--------------------------------------------------------------------
    イメージのカラーキー設定。実体は指定色を透明に。
 ---------------------------------------------------------------------*/
static VALUE Image_setColorKey( VALUE obj, VALUE color )
{
    struct DXRubyImage *image;
    int col;
    D3DLOCKED_RECT texrect;
    int x, y;
    RECT rect;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    Check_Type(color, T_ARRAY);
    col = array2color( color ) & 0x00ffffff;

    rect.left = image->x;
    rect.top = image->y;
    rect.right = image->x + image->width;
    rect.bottom = image->y + image->height;
    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, &rect, 0 );
    for( y = 0; y < image->height; y++ )
    {
        for( x = 0; x < image->width; x++ )
        {
            if( (*((int*)((char *)texrect.pBits + x * 4 + y * texrect.Pitch)) & 0x00ffffff) == col )
            {
                *((int*)((char *)texrect.pBits + x * 4 + y * texrect.Pitch)) = col;
            }
        }
    }
    image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );

    return obj;
}


/*--------------------------------------------------------------------
    イメージのデータ設定(circle用のline描画)
 ---------------------------------------------------------------------*/
static void Image_circle_line( int x1, int x2, int y, RECT *rect, int col, D3DLOCKED_RECT *texrect )
{
    int x;

    if( y < 0 || y >= rect->bottom - rect->top )
    {
        return;
    }

    /* クリップ */
    if( x1 < 0 )
    {
        x1 = 0;
    }
    if( x2 > rect->right - rect->left - 1 )
    {
        x2 = rect->right - rect->left - 1;
    }
    if( x1 > x2 )
    {
        return;
    }

    for( x = x1; x <= x2; x++ )
    {
        *((int*)((char *)texrect->pBits + x * 4 + y * texrect->Pitch)) = col;
    }
}


/*--------------------------------------------------------------------
    イメージのデータ設定(circle描画塗りつぶす)
 ---------------------------------------------------------------------*/
static VALUE Image_circleFill( VALUE obj, VALUE vx0, VALUE vy0, VALUE vr, VALUE color )
{
    struct DXRubyImage *image;
    int x0, y0, r, F, x, y;
    int col;
    D3DLOCKED_RECT texrect;
    RECT rect;
    HRESULT hr;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    x0 = NUM2INT( vx0 );
    y0 = NUM2INT( vy0 );
    r  = NUM2INT( vr );

    Check_Type(color, T_ARRAY);
    col = array2color( color );

    rect.left = x0 < r ? image->x : x0 + image->x - r;
    rect.top = y0 < r ? image->y : y0 + image->y - r;
    rect.right = x0 + r >= image->width ? image->x + image->width : x0 + image->x + r + 1;
    rect.bottom = y0 + r >= image->height ? image->y + image->height : y0 + image->y + r + 1;
    if( rect.left >= rect.right || rect.top >= rect.bottom )
    {
        return obj;
    }

    hr = image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, &rect, 0 );

    x = r;
    y = 0;
    F = -2 * r + 3;

    while ( x >= y )
    {
        int tempx = x0 - rect.left + (int)image->x;
        int tempy = y0 - rect.top + (int)image->y;
        Image_circle_line( tempx - x, tempx + x, tempy + y, &rect, col, &texrect );
        Image_circle_line( tempx - x, tempx + x, tempy - y, &rect, col, &texrect );
        Image_circle_line( tempx - y, tempx + y, tempy + x, &rect, col, &texrect );
        Image_circle_line( tempx - y, tempx + y, tempy - x, &rect, col, &texrect );
        if ( F >= 0 )
        {
          x--;
          F -= 4 * x;
        }
        y++;
        F += 4 * y + 2;
    }

    image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );

    return obj;
}


/*--------------------------------------------------------------------
    イメージのデータ設定(内部処理用pixel描画)
 ---------------------------------------------------------------------*/
static void Image_circle_pixel( int x, int y, RECT *rect, int col, D3DLOCKED_RECT *texrect )
{
    if( x < 0 || x >= rect->right - rect->left || y < 0 || y >= rect->bottom - rect->top )
    {
        return;
    }

    *((int*)((char *)texrect->pBits + x * 4 + y * texrect->Pitch)) = col;

    return;
}


/*--------------------------------------------------------------------
    イメージのデータ設定(circle描画塗りつぶさない)
 ---------------------------------------------------------------------*/
static VALUE Image_circle( VALUE obj, VALUE vx0, VALUE vy0, VALUE vr, VALUE color )
{
    struct DXRubyImage *image;
    D3DLOCKED_RECT texrect;
    int x0, y0, r, F, x, y;
    int col;
    RECT rect;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    x0 = NUM2INT( vx0 );
    y0 = NUM2INT( vy0 );
    r  = NUM2INT( vr );

    Check_Type(color, T_ARRAY);
    col = array2color( color );

    rect.left = x0 < r ? image->x : x0 + image->x - r;
    rect.top = y0 < r ? image->y : y0 + image->y - r;
    rect.right = x0 + r >= image->width ? image->x + image->width : x0 + image->x + r + 1;
    rect.bottom = y0 + r >= image->height ? image->y + image->height : y0 + image->y + r + 1;
    if( rect.left >= rect.right || rect.top >= rect.bottom )
    {
        return obj;
    }

    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, &rect, 0 );

    x = r;
    y = 0;
    F = -2 * r + 3;

    while ( x >= y )
    {
        int tempx = x0 - rect.left + (int)image->x;
        int tempy = y0 - rect.top + (int)image->y;
        Image_circle_pixel( tempx + x, tempy + y, &rect, col, &texrect );
        Image_circle_pixel( tempx - x, tempy + y, &rect, col, &texrect );
        Image_circle_pixel( tempx + x, tempy - y, &rect, col, &texrect );
        Image_circle_pixel( tempx - x, tempy - y, &rect, col, &texrect );
        Image_circle_pixel( tempx + y, tempy + x, &rect, col, &texrect );
        Image_circle_pixel( tempx - y, tempy + x, &rect, col, &texrect );
        Image_circle_pixel( tempx + y, tempy - x, &rect, col, &texrect );
        Image_circle_pixel( tempx - y, tempy - x, &rect, col, &texrect );
        if ( F >= 0 )
        {
          x--;
          F -= 4 * x;
        }
        y++;
        F += 4 * y + 2;
    }

    image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );

    return obj;
}


/*--------------------------------------------------------------------
    イメージのデータ設定(line描画)
 ---------------------------------------------------------------------*/
static VALUE Image_line( VALUE obj, VALUE vx1, VALUE vy1, VALUE vx2, VALUE vy2, VALUE color )
{
    struct DXRubyImage *image;
    D3DLOCKED_RECT texrect;
    int x1, y1, x2, y2, xp, yp;
    int col;
    int c, d, dx, dy, i;
    RECT rect;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    x1 = NUM2INT( vx1 );
    y1 = NUM2INT( vy1 );
    x2 = NUM2INT( vx2 );
    y2 = NUM2INT( vy2 );

    Check_Type(color, T_ARRAY);
    col = array2color( color );

    rect.left = (x1 < x2 ? x1 : x2) < image->x ? image->x : (x1 < x2 ? x1 : x2);
    rect.top = (y1 < y2 ? y1 : y2) < image->y ? image->y : (y1 < y2 ? y1 : y2);
    rect.right = (x1 > x2 ? x1 : x2) >= image->width ? image->x + image->width : image->x + (x1 > x2 ? x1 : x2) + 1;
    rect.bottom = (y1 > y2 ? y1 : y2) >= image->height ? image->y + image->height : image->y + (y1 > y2 ? y1 : y2) + 1;
    if( rect.left >= rect.right || rect.top >= rect.bottom )
    {
        return obj;
    }

    x1 = x1 - rect.left + image->x;
    x2 = x2 - rect.left + image->x;
    y1 = y1 - rect.top + image->y;
    y2 = y2 - rect.top + image->y;

    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, &rect, 0 );

    dx = x2 > x1 ? x2 - x1 : x1 - x2;
    dy = y2 > y1 ? y2 - y1 : y1 - y2;

    /* ブレゼンハムアルゴリズムによる線分描画 */
    if( dx < dy )
    {
        xp = x1 < x2 ? 1 : -1;
        d = y1 < y2 ? 1 : -1;
        c = dy / 2;
        for( i = 0; i <= dy; i++ )
        {
            if( x1 >= 0 && x1 < (int)rect.right - rect.left  && y1 >= 0 && y1 < (int)rect.bottom - rect.top )
            {
                *((int*)((char *)texrect.pBits + x1 * 4 + y1  * texrect.Pitch)) = col;
            }
            y1 = y1 + d;
            c = c + dx;
            if( c >= dy )
            {
                c = c - dy;
                x1 = x1 + xp;
            }
        }
    }
    else
    {
        yp = y1 < y2 ? 1 : -1;
        d = x1 < x2 ? 1 : -1;
        c = dx / 2;
        for( i = 0; i <= dx; i++ )
        {
            if( x1 >= 0 && x1 < (int)rect.right - rect.left  && y1 >= 0 && y1 < (int)rect.bottom - rect.top )
            {
                *((int*)((char *)texrect.pBits + x1 * 4 + y1  * texrect.Pitch)) = col;
            }
            x1 = x1 + d;
            c = c + dy;
            if( c >= dx )
            {
                c = c - dx;
                y1 = y1 + yp;
            }
        }
    }

    image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );

    return obj;
}


/*--------------------------------------------------------------------
   イメージオブジェクト間データ転送
 ---------------------------------------------------------------------*/
static VALUE Image_copyRect( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *srcimage;
    struct DXRubyImage *dstimage;
    D3DLOCKED_RECT srctrect;
    D3DLOCKED_RECT dsttrect;
    VALUE vx, vy, data, vx1, vy1, vwidth, vheight;
    int x, y, x1, y1, width, height;
    int i, j;
    RECT srcrect;
    RECT dstrect;

    rb_scan_args( argc, argv, "34", &vx, &vy, &data, &vx1, &vy1, &vwidth, &vheight );

    Data_Get_Struct( obj, struct DXRubyImage, dstimage );
    Data_Get_Struct( data, struct DXRubyImage, srcimage );

    if( dstimage == srcimage ) rb_raise( eDXRubyError, "描画元と先に同じImageオブジェクトが指定されています - Image_copyRect" );

    x = NUM2INT( vx );
    y = NUM2INT( vy );
    x1 = vx1 == Qnil ? 0 : NUM2INT( vx1 );
    y1 = vy1 == Qnil ? 0 : NUM2INT( vy1 );
    width = vwidth == Qnil ? srcimage->width - x1 : NUM2INT( vwidth );
    height = vheight == Qnil ? srcimage->height - y1 : NUM2INT( vheight );

    /* 画像のクリッピング */
    if( x < 0 )
    {
        x1 -= x;
        width -= x;
        x = 0;
    }
    if( y < 0 )
    {
        y1 -= y;
        height -= y;
        y = 0;
    }
    if( x1 < 0 )
    {
        x -=x1;
        width -= x1;
        x1 = 0;
    }
    if( y1 < 0 )
    {
        y -=y1;
        height -= y1;
        y1 = 0;
    }
    if( x + width > dstimage->width )
    {
        width -= x + width - dstimage->width;
    }
    if( y + height > dstimage->height )
    {
        height -= y + height - dstimage->height;
    }
    if( x1 + width > srcimage->width )
    {
        width -= x1 + width - srcimage->width;
    }
    if( y1 + height > srcimage->height )
    {
        height -= y1 + height - srcimage->height;
    }

    /* 範囲外 */
    if( x >= dstimage->width || y >= dstimage->height || x1 >= srcimage->width || y1 >= srcimage->height ||
        width < 0 || height < 0 )
    {
        return obj;
    }

    dstrect.left = x + dstimage->x;
    dstrect.top = y + dstimage->y;
    dstrect.right = x + dstimage->x + width;
    dstrect.bottom = y + dstimage->y + height;
    srcrect.left = x1 + srcimage->x;
    srcrect.top = y1 + srcimage->y;
    srcrect.right = x1 + srcimage->x + width;
    srcrect.bottom = y1 + srcimage->y + height;

    dstimage->texture->pD3DTexture->lpVtbl->LockRect( dstimage->texture->pD3DTexture, 0, &dsttrect, &dstrect, 0 );
    srcimage->texture->pD3DTexture->lpVtbl->LockRect( srcimage->texture->pD3DTexture, 0, &srctrect, &srcrect, D3DLOCK_READONLY );

    for( i = 0; i < height; i++)
    {
        for( j = 0; j < width; j++)
        {
            *((int*)((char *)dsttrect.pBits + j * 4 + i * dsttrect.Pitch)) =
                *((int*)((char *)srctrect.pBits + j * 4 + i * srctrect.Pitch));
        }
    }

    dstimage->texture->pD3DTexture->lpVtbl->UnlockRect( dstimage->texture->pD3DTexture, 0 );
    srcimage->texture->pD3DTexture->lpVtbl->UnlockRect( srcimage->texture->pD3DTexture, 0 );

    return obj;
}


/*--------------------------------------------------------------------
   イメージオブジェクトdup
 ---------------------------------------------------------------------*/
static VALUE Image_dup( VALUE self )
{
    struct DXRubyImage *srcimage;
    struct DXRubyImage *dstimage;
    struct DXRubyTexture *texture;
    D3DLOCKED_RECT srctrect;
    D3DLOCKED_RECT dsttrect;
    int i, j;
    RECT srcrect;
    RECT dstrect;
    VALUE obj;
    HRESULT hr;
    D3DSURFACE_DESC desc;

    Data_Get_Struct( self, struct DXRubyImage, srcimage );

    /* Imageオブジェクト作成 */
    dstimage = malloc(sizeof(struct DXRubyImage));
    if( dstimage == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Image_dup" );
    obj = Data_Wrap_Struct(cImage, 0, release_Image, dstimage);

    /* テクスチャメモリ取得 */
    texture = (struct  DXRubyTexture *)malloc( sizeof( struct DXRubyTexture ) );
    if( texture == NULL )
    {
        rb_raise( eDXRubyError, "画像用メモリの取得に失敗しました - Image_dup" );
    }

    /* テクスチャオブジェクトを作成する */
    hr = D3DXCreateTexture( g_pD3DDevice, srcimage->width, srcimage->height,
                            1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
                            &texture->pD3DTexture);
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "テクスチャの作成に失敗しました - Image_dup" );
    }

    texture->refcount = 1;

    texture->pD3DTexture->lpVtbl->GetLevelDesc(texture->pD3DTexture, 0, &desc );
    texture->width = desc.Width;
    texture->height = desc.Height;

    dstimage->texture = texture;
    dstimage->x = 0;
    dstimage->y = 0;
    dstimage->width = srcimage->width;
    dstimage->height =srcimage-> height;

    /* イメージコピー */
    dstrect.left = 0;
    dstrect.top = 0;
    dstrect.right = srcimage->width;
    dstrect.bottom = srcimage->height;
    srcrect.left = srcimage->x;
    srcrect.top = srcimage->y;
    srcrect.right = srcimage->x + srcimage->width;
    srcrect.bottom = srcimage->y + srcimage->height;

    dstimage->texture->pD3DTexture->lpVtbl->LockRect( dstimage->texture->pD3DTexture, 0, &dsttrect, &dstrect, 0 );
    srcimage->texture->pD3DTexture->lpVtbl->LockRect( srcimage->texture->pD3DTexture, 0, &srctrect, &srcrect, D3DLOCK_READONLY );

    for( i = 0; i < srcimage->height; i++)
    {
        for( j = 0; j < srcimage->width; j++)
        {
            *((int*)((char *)dsttrect.pBits + j * 4 + i * dsttrect.Pitch)) =
                *((int*)((char *)srctrect.pBits + j * 4 + i * srctrect.Pitch));
        }
    }

    dstimage->texture->pD3DTexture->lpVtbl->UnlockRect( dstimage->texture->pD3DTexture, 0 );
    srcimage->texture->pD3DTexture->lpVtbl->UnlockRect( srcimage->texture->pD3DTexture, 0 );

    g_iRefAll++;

    return obj;
}


/*--------------------------------------------------------------------
   イメージオブジェクト間αつきデータ転送
 ---------------------------------------------------------------------*/
static VALUE Image_draw( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *srcimage;
    struct DXRubyImage *dstimage;
    D3DLOCKED_RECT srctrect;
    D3DLOCKED_RECT dsttrect;
    VALUE vx, vy, data, vx1, vy1, vwidth, vheight;
    int x, y, x1, y1, width, height;
    int i, j;
    RECT srcrect;
    RECT dstrect;

    rb_scan_args( argc, argv, "34", &vx, &vy, &data, &vx1, &vy1, &vwidth, &vheight );

    Data_Get_Struct( obj, struct DXRubyImage, dstimage );
    Data_Get_Struct( data, struct DXRubyImage, srcimage );

    if( dstimage == srcimage ) rb_raise( eDXRubyError, "描画元と先に同じImageオブジェクトが指定されています - Image_draw" );

    x = NUM2INT( vx );
    y = NUM2INT( vy );
    x1 = vx1 == Qnil ? 0 : NUM2INT( vx1 );
    y1 = vy1 == Qnil ? 0 : NUM2INT( vy1 );
    width = vwidth == Qnil ? srcimage->width - x1 : NUM2INT( vwidth );
    height = vheight == Qnil ? srcimage->height - y1 : NUM2INT( vheight );

    /* 画像のクリッピング */
    if( x < 0 )
    {
        x1 -= x;
        width -= x;
        x = 0;
    }
    if( y < 0 )
    {
        y1 -= y;
        height -= y;
        y = 0;
    }
    if( x1 < 0 )
    {
        x -=x1;
        width -= x1;
        x1 = 0;
    }
    if( y1 < 0 )
    {
        y -=y1;
        height -= y1;
        y1 = 0;
    }
    if( x + width > dstimage->width )
    {
        width -= x + width - dstimage->width;
    }
    if( y + height > dstimage->height )
    {
        height -= y + height - dstimage->height;
    }
    if( x1 + width > srcimage->width )
    {
        width -= x1 + width - srcimage->width;
    }
    if( y1 + height > srcimage->height )
    {
        height -= y1 + height - srcimage->height;
    }

    /* 範囲外 */
    if( x >= dstimage->width || y >= dstimage->height || x1 >= srcimage->width || y1 >= srcimage->height ||
        width < 0 || height < 0 )
    {
        return obj;
    }

    dstrect.left = x + dstimage->x;
    dstrect.top = y + dstimage->y;
    dstrect.right = x + dstimage->x + width;
    dstrect.bottom = y + dstimage->y + height;
    srcrect.left = x1 + srcimage->x;
    srcrect.top = y1 + srcimage->y;
    srcrect.right = x1 + srcimage->x + width;
    srcrect.bottom = y1 + srcimage->y + height;

    dstimage->texture->pD3DTexture->lpVtbl->LockRect( dstimage->texture->pD3DTexture, 0, &dsttrect, &dstrect, 0 );
    srcimage->texture->pD3DTexture->lpVtbl->LockRect( srcimage->texture->pD3DTexture, 0, &srctrect, &srcrect, D3DLOCK_READONLY );

    for( i = 0; i < height; i++)
    {
        for( j = 0; j < width; j++)
        {
            struct DXRubyColor s = *((struct DXRubyColor*)((char *)srctrect.pBits + j * 4 + i * srctrect.Pitch));

            if( s.alpha == 255 )
            {
                *((struct DXRubyColor*)((char *)dsttrect.pBits + j * 4 + i * dsttrect.Pitch)) = s;
            }
            else if( s.alpha != 0 )
            {
                struct DXRubyColor d = *((struct DXRubyColor*)((char *)dsttrect.pBits + j * 4 + i * dsttrect.Pitch));

                s.red = ((int)s.red * (int)s.alpha * 255 + (int)d.red * (int)d.alpha * (255 - (int)s.alpha) ) / 255 / 255;
                s.green = ((int)s.green * (int)s.alpha * 255 + (int)d.green * (int)d.alpha * (255 - (int)s.alpha) ) / 255 / 255;
                s.blue = ((int)s.blue * (int)s.alpha * 255 + (int)d.blue * (int)d.alpha * (255 - (int)s.alpha) ) / 255 / 255;

                if( d.alpha > s.alpha)
                {
                    s.alpha = d.alpha;
                }

                *((struct DXRubyColor*)((char *)dsttrect.pBits + j * 4 + i * dsttrect.Pitch)) = s;
            }
        }
    }

    dstimage->texture->pD3DTexture->lpVtbl->UnlockRect( dstimage->texture->pD3DTexture, 0 );
    srcimage->texture->pD3DTexture->lpVtbl->UnlockRect( srcimage->texture->pD3DTexture, 0 );

    return obj;
}


/*--------------------------------------------------------------------
   イメージにフォント描画
 ---------------------------------------------------------------------*/
static VALUE Image_drawFont( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyFont *font;
    struct DXRubyImage *image;
    VALUE vx, vy, vcolor, str, data;
    int cr, cg, cb, x, y;
    LPDIRECT3DSURFACE9 pD3DSurface;
    HDC hDC;
    RECT rc;
    HRESULT hr;
    D3DLOCKED_RECT srctrect;
    D3DLOCKED_RECT dsttrect;
    int i, j;
    int h;
    RECT srcrect;
    RECT dstrect;

    /* 引数取得 */
    rb_scan_args( argc, argv, "41", &vx, &vy, &str, &data, &vcolor);

    Check_Type(str, T_STRING);

    /* 引数のフォントオブジェクトから中身を取り出す */
    Data_Get_Struct( obj, struct DXRubyImage, image );
    Data_Get_Struct( data, struct DXRubyFont, font );
    if (RDATA(data)->dfree != (RUBY_DATA_FUNC)release_Font)
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Font)", rb_obj_classname( data ));
    }

    x = NUM2INT( vx );
    y = NUM2INT( vy );
    if( vcolor != Qnil )
    {
        Check_Type(vcolor, T_ARRAY);
        if( RARRAY_LEN(vcolor) == 4 )
        {
            cr = NUM2INT(rb_ary_entry(vcolor, 1));
            cg = NUM2INT(rb_ary_entry(vcolor, 2));
            cb = NUM2INT(rb_ary_entry(vcolor, 3));
        }
        else
        {
            cr = NUM2INT(rb_ary_entry(vcolor, 0));
            cg = NUM2INT(rb_ary_entry(vcolor, 1));
            cb = NUM2INT(rb_ary_entry(vcolor, 2));
        }
    }
    else
    {
        cr = 255;
        cg = 255;
        cb = 255;
    }

    if( x >= image->width || y >= image->height )
    {
        return obj;
    }

    /* オフスクリーンサーフェイスに文字描画 */
    hr = g_pD3DDevice->lpVtbl->CreateOffscreenPlainSurface ( g_pD3DDevice, image->width, image->height
                                      , D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, &pD3DSurface, NULL );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "サーフェイスの生成に失敗しました - CreateOffscreenPlainSurface" );
    }
    hr = pD3DSurface->lpVtbl->GetDC( pD3DSurface, &hDC );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DCの取得に失敗しました - GetDC" );
    }
    SelectObject( hDC, font->hFont );
    SetRect( &rc, x, y, x + 1, y + 1 );
    SetTextColor( hDC, RGB( 255, 255, 255 ) );
    SetBkColor( hDC, RGB( 0, 0, 0 ) );
    h = DrawText( hDC, RSTRING_PTR(str), -1, &rc, DT_LEFT | DT_NOCLIP | DT_NOPREFIX | DT_NOFULLWIDTHCHARBREAK);
    pD3DSurface->lpVtbl->ReleaseDC( pD3DSurface, hDC );

    if( y + h < 0 )
    {
        RELEASE( pD3DSurface );
        return obj;
    }

    /* Imageオブジェクトに転送 */
    if( x < 0 )
    {
        x = 0;
    }
    if( y < 0 )
    {
        h += y;
        y = 0;
    }

    srcrect.left = x;
    srcrect.top = y;
    srcrect.right = image->width;
    srcrect.bottom = image->height < y + h ? image->height : y + h;
    dstrect.left = x + image->x;
    dstrect.top = y + image->y;
    dstrect.right = image->x + image->width;
    dstrect.bottom = image->y + (image->height < y + h ? image->height : y + h);

    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &dsttrect, &dstrect, 0 );
    pD3DSurface->lpVtbl->LockRect( pD3DSurface, &srctrect, &srcrect, D3DLOCK_READONLY );

    for( i = 0; i < srcrect.bottom - srcrect.top; i++)
    {
        for( j = 0; j < srcrect.right - srcrect.left; j++)
        {
            struct DXRubyColor s = *((struct DXRubyColor*)((char *)srctrect.pBits + j * 4 + i * srctrect.Pitch));

            if( s.red == 255 )
            {
                *((int *)((char *)dsttrect.pBits + j * 4 + i * dsttrect.Pitch)) = D3DCOLOR_ARGB(0xff, cr, cg, cb);
            }
            else if( s.red != 0 )
            {
                struct DXRubyColor d = *((struct DXRubyColor*)((char *)dsttrect.pBits + j * 4 + i * dsttrect.Pitch));
                struct DXRubyColor data;

                data.alpha = s.red > d.alpha ? s.red : d.alpha;
                data.red = ((int)s.red * cr * 255 + (int)d.red * (int)d.alpha * (255 - (int)s.red) ) / 255 / 255;
                data.green = ((int)s.red * cg * 255 + (int)d.green * (int)d.alpha * (255 - (int)s.red) ) / 255 / 255;
                data.blue = ((int)s.red * cb * 255 + (int)d.blue * (int)d.alpha * (255 - (int)s.red) ) / 255 / 255;

                *((struct DXRubyColor*)((char *)dsttrect.pBits + j * 4 + i * dsttrect.Pitch)) = data;
            }
        }
    }

    image->texture->pD3DTexture->lpVtbl->UnlockRect( image->texture->pD3DTexture, 0 );
    pD3DSurface->lpVtbl->UnlockRect( pD3DSurface );

    RELEASE( pD3DSurface );
    return obj;
}


/*--------------------------------------------------------------------
   イメージをファイルに保存する。
 ---------------------------------------------------------------------*/
static VALUE Image_save( int argc, VALUE *argv, VALUE obj )
{
    HRESULT hr;
    struct DXRubyImage *image;
    VALUE filename, format;

    rb_scan_args( argc, argv, "11", &filename, &format );

    Data_Get_Struct( obj, struct DXRubyImage, image );

    hr = D3DXSaveTextureToFile(
            RSTRING_PTR( filename ),                            /* 保存ファイル名 */
            format == Qnil ? D3DXIFF_JPG : FIX2INT( format ),   /* ファイルフォーマット */
            (IDirect3DBaseTexture9*)image->texture->pD3DTexture,/* 保存するサーフェス */
            NULL);                                              /* パレット */
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ファイルの保存に失敗しました - Image_save" );
    }

    return obj;
}


/*--------------------------------------------------------------------
   イメージのサイズ（幅）を返す。
 ---------------------------------------------------------------------*/
static VALUE Image_getWidth( VALUE obj )
{
    struct DXRubyImage *image;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    return INT2FIX( image->width );
}


/*--------------------------------------------------------------------
   イメージのサイズ（高さ）を返す。
 ---------------------------------------------------------------------*/
static VALUE Image_getHeight( VALUE obj )
{
    struct DXRubyImage *image;

    Data_Get_Struct( obj, struct DXRubyImage, image );

    return INT2FIX( image->height );
}



/*********************************************************************
 * Soundクラス
 *
 * DirectMusicを使用して音を鳴らす。
 * とりあえず音を出そうと頑張っている。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void release_Sound( struct DXRubySound* sound )
{
    HRESULT hr;

	/* サウンドオブジェクトの開放 */
    if( sound )
    {
	    /* バンド解放 */
    	if ( sound->pDMSegment != NULL )
    	{
	    	hr = sound->pDMSegment->lpVtbl->Unload( sound->pDMSegment, (IUnknown* )sound->pDMDefAudioPath );
		    if( FAILED( hr ) )
		    {
		        rb_raise( eDXRubyError, "バンドの解放に失敗しました - Unload" );
		    }
			/* セグメントを開放 */
	    	RELEASE( sound->pDMSegment );
    	}

    	/* デフォルトオーディオパスを開放 */
	    RELEASE( sound->pDMDefAudioPath );

    	g_iRefDM--;

    	if( g_iRefDM <= 0 )
    	{
	    	/* 演奏停止 */
		    if ( g_pDMPerformance )
		    {
		        hr = g_pDMPerformance->lpVtbl->Stop( g_pDMPerformance, NULL, NULL, 0, 0 );
		        if ( FAILED( hr ) )
		    	{
		            rb_raise( eDXRubyError, "パフォーマンスの終了に失敗しました - Stop" );
		    	}
		    }
	        g_pDMPerformance->lpVtbl->CloseDown( g_pDMPerformance );
    		RELEASE(g_pDMPerformance);

		    /* ローダを開放 */
		    RELEASE(g_pDMLoader);
    	}

    	free( sound );
        sound = NULL;

    	g_iRefAll--;
    	if( g_iRefAll == 0 )
    	{
    		CoUninitialize();
    	}
    }
}


/*--------------------------------------------------------------------
   Soundクラスのallocate。メモリを確保する為にinitialize前に呼ばれる。
 ---------------------------------------------------------------------*/
static VALUE Sound_allocate( VALUE klass )
{
    VALUE obj;
    struct DXRubySound *sound;

    /* DXRubyImageのメモリ取得＆Imageオブジェクト生成 */
    sound = malloc(sizeof(struct DXRubySound));
    if( sound == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Sound_allocate" );
    obj = Data_Wrap_Struct(klass, 0, release_Sound, sound);

    /* とりあえずサウンドオブジェクトはNULLにしておく */
    sound->pDMSegment = NULL;

    return obj;
}


/*--------------------------------------------------------------------
   Soundクラスのinitialize。ファイルをロードする。
 ---------------------------------------------------------------------*/
static VALUE Sound_initialize( VALUE obj, VALUE filename )
{
    HRESULT hr;
    WCHAR wstrFileName[MAX_PATH];
    struct DXRubySound *sound;
    CHAR strPath[MAX_PATH];
    DWORD i;
    WCHAR wstrSearchPath[MAX_PATH];

	Check_Type(filename, T_STRING);

	if( g_iRefDM == 0 )
	{
	    /* パフォーマンスの作成 */
	    hr = CoCreateInstance( &CLSID_DirectMusicPerformance, NULL,
	                           CLSCTX_INPROC_SERVER, &IID_IDirectMusicPerformance8,
	                           (void**)&g_pDMPerformance );
	    if( FAILED( hr ) )
	    {
	        rb_raise( eDXRubyError, "DirectMusicの初期化に失敗しました - CoCreateInstance" );
	    }

	    /* パフォーマンスの初期化 */
	    hr = g_pDMPerformance->lpVtbl->InitAudio( g_pDMPerformance,
	                                              NULL,                  /* IDirectMusicインターフェイスは不要 */
	                                              NULL,                  /* IDirectSoundインターフェイスは不要 */
	                                              g_hWind,               /* ウィンドウのハンドル */
	                                              DMUS_APATH_SHARED_STEREOPLUSREVERB,  /* デフォルトのオーディオパス・タイプ */
	                                              64,                    /* パフォーマンス・チャンネルの数 */
	                                              DMUS_AUDIOF_ALL,       /* シンセサイザの機能 */
	                                              NULL );                /* オーディオ・パラメータにはデフォルトを使用 */
	    if( FAILED( hr ) )
	    {
	        rb_raise( eDXRubyError, "DirectMusicの初期化に失敗しました - InitAudio" );
	    }

	    /* ローダーの作成 */
	    hr = CoCreateInstance( &CLSID_DirectMusicLoader, NULL, 
	                           CLSCTX_INPROC_SERVER, &IID_IDirectMusicLoader8,
	                           (void**)&g_pDMLoader );
	    if( FAILED( hr ) )
	    {
	        rb_raise( eDXRubyError, "DirectMusicの初期化に失敗しました - CoCreateInstance" );
	    }

	    /* ローダーの初期化（検索パスをカレント・ディレクトリに設定） */
	    i = GetCurrentDirectory( MAX_PATH, strPath );
	    if ( i == 0 || MAX_PATH < i )
	    {
	        rb_raise( eDXRubyError, "カレント・ディレクトリの取得に失敗 - GetCurrentDirectory" );
	    }

	    /* マルチ・バイト文字をUNICODEに変換 */
	    MultiByteToWideChar( CP_ACP, 0, strPath, -1, wstrSearchPath, MAX_PATH );

	    /* ローダーに検索パスを設定 */
	    hr = g_pDMLoader->lpVtbl->SetSearchDirectory( g_pDMLoader, &GUID_DirectMusicAllTypes,
	                                                  wstrSearchPath, FALSE );
	    if( FAILED( hr ) )
	    {
	        rb_raise( eDXRubyError, "検索パスの設定に失敗 - SetSearchDirectory" );
	    }
	}
    g_iRefDM++;

	/* サウンドオブジェクト取得 */
    Data_Get_Struct( obj, struct DXRubySound, sound );

	/* オーディオ・パス作成 */
	hr = g_pDMPerformance->lpVtbl->CreateStandardAudioPath( g_pDMPerformance,
		DMUS_APATH_DYNAMIC_STEREO,      /* パスの種類。 */
		64,                             /* パフォーマンス チャンネルの数。 */
		TRUE,                           /* ここでアクティブになる。 */
		&sound->pDMDefAudioPath );      /* オーディオパスを受け取るポインタ。 */

	if ( FAILED( hr ) )
	{
        rb_raise( eDXRubyError, "オーディオパスの作成に失敗しました - CreateStandardAudioPath" );
	}

    /* ファイルロード */
    MultiByteToWideChar( CP_ACP, 0, RSTRING_PTR( filename ), -1, wstrFileName, MAX_PATH );
	hr = g_pDMLoader->lpVtbl->LoadObjectFromFile( g_pDMLoader, &CLSID_DirectMusicSegment,
                                                  &IID_IDirectMusicSegment8,
                                                  wstrFileName,
                                                  (LPVOID*)&sound->pDMSegment );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ファイルのロードに失敗しました - LoadObjectFromFile" );
    }

    sound->start = 0;
    sound->loopstart = 0;
    sound->loopend = 0;

    /* MIDIの場合 */
    if( strstr( RSTRING_PTR( filename ), ".mid" ) != NULL )
    {
        hr = sound->pDMSegment->lpVtbl->SetParam( sound->pDMSegment, &GUID_StandardMIDIFile,
                                                  0xFFFFFFFF, 0, 0, NULL);
        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "ファイルのロードに失敗しました - SetParam" );
        }
        sound->loopcount = DMUS_SEG_REPEAT_INFINITE;
        sound->midwavflag = 0;
        /* ループ回数設定 */
        hr = sound->pDMSegment->lpVtbl->SetRepeats( sound->pDMSegment, sound->loopcount );

        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "ループ回数の設定に失敗しました - SetRepeats" );
        }
    }
    else
    {
        sound->loopcount = 1;
        sound->midwavflag = 1;
    }

    /* バンドダウンロード */
    hr = sound->pDMSegment->lpVtbl->Download( sound->pDMSegment, (IUnknown* )sound->pDMDefAudioPath );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "バンドのロードに失敗しました - Download" );
    }

    g_iRefAll++;

    /* 音量設定 */
    hr = sound->pDMDefAudioPath->lpVtbl->SetVolume( sound->pDMDefAudioPath, 230 * 9600 / 255 - 9600 , 0 );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音量の設定に失敗しました - SetVolume" );
    }

    return obj;
}


/*--------------------------------------------------------------------
   開始位置を設定する
 ---------------------------------------------------------------------*/
static VALUE Sound_setStart( VALUE obj, VALUE vstart )
{
    HRESULT hr;
    struct DXRubySound *sound;

    Data_Get_Struct( obj, struct DXRubySound, sound );
    sound->start = NUM2INT( vstart );
    /* セグメント再生スタート位置設定 */
    hr = sound->pDMSegment->lpVtbl->SetStartPoint( sound->pDMSegment, sound->start / 768 * DMUS_PPQ  );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "セグメント開始位置の設定に失敗しました - SetStartPoint" );
    }

    return obj;
}


/*--------------------------------------------------------------------
   ループ開始位置を設定する
 ---------------------------------------------------------------------*/
static VALUE Sound_setLoopStart( VALUE obj, VALUE vloopstart )
{
    HRESULT hr;
    struct DXRubySound *sound;

    Data_Get_Struct( obj, struct DXRubySound, sound );
    sound->loopstart = NUM2INT( vloopstart );

    /* ループ範囲設定 */
    hr = sound->pDMSegment->lpVtbl->SetLoopPoints( sound->pDMSegment, sound->loopstart / 768 * DMUS_PPQ
                                                                    , sound->loopend / 768 * DMUS_PPQ );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ループ範囲の設定に失敗しました - SetLoopPoints" );
    }

    return obj;
}


/*--------------------------------------------------------------------
   ループ終了位置を設定する
 ---------------------------------------------------------------------*/
static VALUE Sound_setLoopEnd( VALUE obj, VALUE vloopend )
{
    HRESULT hr;
    struct DXRubySound *sound;

    Data_Get_Struct( obj, struct DXRubySound, sound );
    sound->loopend = NUM2INT( vloopend );

    /* ループ範囲設定 */
    hr = sound->pDMSegment->lpVtbl->SetLoopPoints( sound->pDMSegment, sound->loopstart / 768 * DMUS_PPQ
                                                                    , sound->loopend / 768 * DMUS_PPQ );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ループ範囲の設定に失敗しました - SetLoopPoints" );
    }

    return obj;
}


/*--------------------------------------------------------------------
   ループ回数を設定する
 ---------------------------------------------------------------------*/
static VALUE Sound_setLoopCount( VALUE obj, VALUE vloopcount )
{
    HRESULT hr;
    struct DXRubySound *sound;

    Data_Get_Struct( obj, struct DXRubySound, sound );
    sound->loopcount = NUM2INT( vloopcount );

    /* ループ回数設定 */
    hr = sound->pDMSegment->lpVtbl->SetRepeats( sound->pDMSegment, sound->loopcount );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "ループ回数の設定に失敗しました - SetRepeats" );
    }

    return obj;
}


/*--------------------------------------------------------------------
   音量を設定する
 ---------------------------------------------------------------------*/
static VALUE Sound_setVolume( int argc, VALUE *argv, VALUE obj )
{
    HRESULT hr;
    struct DXRubySound *sound;
    VALUE vvolume, vtime;
    int volume, time;

    rb_scan_args( argc, argv, "11", &vvolume, &vtime );

    time = vtime == Qnil ? 0 : NUM2INT( vtime );
    volume = NUM2INT( vvolume ) > 255 ? 255 : NUM2INT( vvolume );
    Data_Get_Struct( obj, struct DXRubySound, sound );

    /* 音量設定 */
    hr = sound->pDMDefAudioPath->lpVtbl->SetVolume( sound->pDMDefAudioPath, volume * 9600 / 255 - 9600, time );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音量の設定に失敗しました - SetVolume" );
    }

	return obj;
}


/*--------------------------------------------------------------------
   音を鳴らす
 ---------------------------------------------------------------------*/
static VALUE Sound_play( VALUE obj )
{
    HRESULT hr;
    struct DXRubySound *sound;

    Data_Get_Struct( obj, struct DXRubySound, sound );

    /* 再生 */
    if( sound->midwavflag == 0 )
    {
        hr = g_pDMPerformance->lpVtbl->PlaySegmentEx( g_pDMPerformance, (IUnknown* )sound->pDMSegment, NULL, NULL,
                                                      DMUS_SEGF_CONTROL, 0, NULL, NULL, (IUnknown* )sound->pDMDefAudioPath );
    }
    else
    {
        hr = g_pDMPerformance->lpVtbl->PlaySegmentEx( g_pDMPerformance, (IUnknown* )sound->pDMSegment, NULL, NULL,
                                                      DMUS_SEGF_SECONDARY, 0, NULL, NULL, (IUnknown* )sound->pDMDefAudioPath );
    }
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音の再生に失敗しました - PlaySegmentEx" );
    }

    return obj;
}


/*--------------------------------------------------------------------
   音を止める
 ---------------------------------------------------------------------*/
static VALUE Sound_stop( VALUE obj )
{
    HRESULT hr;
    struct DXRubySound *sound;

    Data_Get_Struct( obj, struct DXRubySound, sound );

    /* 再生 */
    hr = g_pDMPerformance->lpVtbl->StopEx( g_pDMPerformance, (IUnknown* )sound->pDMSegment, 0, 0 );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音の停止に失敗しました - StopEx" );
    }

    return obj;

}


/*********************************************************************
 * SoundEffectクラス
 *
 * DirectSoundを使用して音を鳴らす。
 * とりあえず音を出そうと頑張っている。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void release_SoundEffect( struct DXRubySoundEffect* soundeffect )
{
    /* サウンドオブジェクトの開放 */
    if( soundeffect )
    {
    	/* サウンドバッファを開放 */
	    RELEASE( soundeffect->pDSBuffer );

    	g_iRefDS--;

    	if( g_iRefDS == 0 )
    	{
    		RELEASE( g_pDSound );
    	}

    	free( soundeffect );
        soundeffect = NULL;

    	g_iRefAll--;
    	if( g_iRefAll == 0 )
    	{
    		CoUninitialize();
    	}
    }
}


/*--------------------------------------------------------------------
   SoundEffectクラスのallocate。メモリを確保する為にinitialize前に呼ばれる。
 ---------------------------------------------------------------------*/
static VALUE SoundEffect_allocate( VALUE klass )
{
    VALUE obj;
    struct DXRubySoundEffect *soundeffect;

    /* DXRubySoundEffectのメモリ取得＆SoundEffectオブジェクト生成 */
    soundeffect = malloc(sizeof(struct DXRubySoundEffect));
    if( soundeffect == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - SoundEffect_allocate" );
    obj = Data_Wrap_Struct(klass, 0, release_SoundEffect, soundeffect);

    /* とりあえずサウンドオブジェクトはNULLにしておく */
    soundeffect->pDSBuffer = NULL;

    return obj;
}


static short calcwave(int type, double vol, double count, double p)
{
	switch( type )
	{
	case 1: /* サイン波 */
		return (sin( (3.141592653589793115997963468544185161590576171875f * 2) * (double)count / (double)p )) * (double)vol * 128;
		break;
	case 2: /* ノコギリ波 */
		return ((double)count / (double)p - 0.5) * (double)vol * 256;
		break;
	case 3: /* 三角波 */
		if( count < p / 4 )			/* 1/4 */
		{
			return (double)count / ((double)p / 4) * (double)vol * 128;
		}
		else if( count < p / 2 )		/* 2/4 */
		{
			return ((double)p / 2 - (double)count) / ((double)p / 4) * (double)vol * 128;
		}
		else if( count < p * 3 / 4 )	/* 3/4 */
		{
			return -((double)count - (double)p / 2)/ ((double)p / 4) * (double)vol * 128;
		}
		else											/* 最後 */
		{
			return -((double)p - (double)count) / ((double)p / 4) * (double)vol * 128;
		}
		break;
	case 0: /* 矩形波 */
	default: /* デフォルト */
		if( count < p / 2 )	/* 前半 */
		{
			return vol * 128;
		}
		else									/* 後半 */
		{
			return -vol * 128;
		}
		break;
	}
}
/*--------------------------------------------------------------------
   SoundEffectクラスのinitialize。波形を生成する。
 ---------------------------------------------------------------------*/
static VALUE SoundEffect_initialize( int argc, VALUE *argv, VALUE obj )
{
    HRESULT hr;
    struct DXRubySoundEffect *soundeffect;
	DSBUFFERDESC desc;
	WAVEFORMATEX pcmwf;
	int i;
	short *pointer, *pointer2;
	DWORD size, size2;
	VALUE vf;
	double count;
    int vol;
    double f;
	VALUE vsize, vtype, vresolution;
	int type, resolution;

    rb_scan_args( argc, argv, "12", &vsize, &vtype, &vresolution );

	type       = vtype       == Qnil ? 0    : NUM2INT( vtype );
	resolution = vresolution == Qnil ? 1000 : (NUM2INT( vresolution ) > 44100 ? 44100 : NUM2INT( vresolution ));

    /* DirectSoundオブジェクトの作成 */
	if( g_iRefDS == 0 )
	{
		hr = DirectSoundCreate8( &DSDEVID_DefaultPlayback, &g_pDSound, NULL );
	    if( FAILED( hr ) )
	    {
	        rb_raise( eDXRubyError, "DirectSoundの初期化に失敗しました - DirectSoundCreate8" );
	    }

		hr = g_pDSound->lpVtbl->SetCooperativeLevel( g_pDSound, g_hWind, DSSCL_PRIORITY );
	    if( FAILED( hr ) )
	    {
	        rb_raise( eDXRubyError, "DirectSoundの初期化に失敗しました - SetCooperativeLevel" );
	    }
	}
	g_iRefDS++;

	/* サウンドオブジェクト作成 */
    Data_Get_Struct( obj, struct DXRubySoundEffect, soundeffect );

	/* サウンドバッファ作成 */
	pcmwf.wFormatTag = WAVE_FORMAT_PCM;
	pcmwf.nChannels = 1;
	pcmwf.nSamplesPerSec = 44100 * 2;
	pcmwf.wBitsPerSample = 16;
	pcmwf.nBlockAlign = pcmwf.nChannels * pcmwf.wBitsPerSample / 8;
	pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
	pcmwf.cbSize = 0;

	desc.dwSize = sizeof(desc);
	desc.dwFlags = 0;
	desc.dwBufferBytes = pcmwf.nAvgBytesPerSec / 100 * NUM2INT(vsize) / 10;
	desc.dwReserved = 0;
	desc.lpwfxFormat = &pcmwf;
	desc.guid3DAlgorithm = DS3DALG_DEFAULT;

	hr = g_pDSound->lpVtbl->CreateSoundBuffer( g_pDSound, &desc, &soundeffect->pDSBuffer, NULL );

	if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "サウンドバッファの作成に失敗しました - CreateSoundBuffer" );
    }

	/* ロック */
	hr = soundeffect->pDSBuffer->lpVtbl->Lock( soundeffect->pDSBuffer, 0, 0, &pointer, &size, &pointer2, &size2, DSBLOCK_ENTIREBUFFER );
    if( FAILED( hr ) || size2 != 0 )
    {
        rb_raise( eDXRubyError, "サウンドバッファのロックに失敗しました - Lock" );
    }

	/* 音バッファ初期化 */
	for( i = 0; i < desc.dwBufferBytes / (pcmwf.wBitsPerSample / 8); i++ )
	{
		pointer[i] = 0;
	}

	count = 0;

	/* 波形生成 */
	for( i = 0; i < desc.dwBufferBytes / (pcmwf.wBitsPerSample / 8); i++ )
	{
		/* 指定時間単位でブロックを呼び出す */
		if ( i % (pcmwf.nSamplesPerSec / resolution) == 0 )
		{
			vf = rb_yield( obj );
			if( TYPE( vf ) != T_ARRAY )
			{
				soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
			    rb_raise(rb_eTypeError, "not valid value - SoundEffect_initialize");
				break;
			}
			switch (TYPE( rb_ary_entry(vf, 0) )) {
			  case T_FIXNUM:
			  case T_FLOAT:
				f = NUM2DBL( rb_ary_entry(vf, 0) );
			    break;
			  default:
				soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
			    rb_raise(rb_eTypeError, "not valid value - SoundEffect_initialize");
			    break;
			}
			switch (TYPE( rb_ary_entry(vf, 1) )) {
			  case T_FIXNUM:
			  case T_FLOAT:
				vol = NUM2DBL(rb_ary_entry(vf, 1));
			    break;
			  default:
				soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
			    rb_raise(rb_eTypeError, "not valid value - SoundEffect_initialize");
			    break;
			}
			/* 最大/最低周波数と最大ボリュームの制限 */
			f = f > pcmwf.nSamplesPerSec / 2.0f ? pcmwf.nSamplesPerSec / 2.0f : f;
			f = f < 20 ? 20 : f;
			vol = vol > 255 ? 255 : vol;
			vol = vol < 0 ? 0 : vol;
		}
		count = count + f;
		if( count >= pcmwf.nSamplesPerSec )
		{
			count = count - pcmwf.nSamplesPerSec;
		}
		pointer[i] = calcwave(type, vol, count, pcmwf.nSamplesPerSec);
	}

	/* アンロック */
	hr = soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "サウンドバッファのロックに失敗しました - Lock" );
    }

	g_iRefAll++;

	return obj;

}


/*--------------------------------------------------------------------
   波形を合成する。
 ---------------------------------------------------------------------*/
static VALUE SoundEffect_add( int argc, VALUE *argv, VALUE obj )
{
    HRESULT hr;
    struct DXRubySoundEffect *soundeffect;
	DSBUFFERDESC desc;
	WAVEFORMATEX pcmwf;
	int i;
	short *pointer, *pointer2;
	DWORD size, size2;
	VALUE vf, vtype, vresolution;
	double count;
	int vol;
    double f;
	int type, resolution;
	int data;

	rb_scan_args( argc, argv, "02", &vtype, &vresolution );

	type       = vtype       == Qnil ? 0    : NUM2INT( vtype );
	resolution = vresolution == Qnil ? 1000 : (NUM2INT( vresolution ) > 44100 ? 44100 : NUM2INT( vresolution ));

    /* サウンドオブジェクト取得 */
    Data_Get_Struct( obj, struct DXRubySoundEffect, soundeffect );

	/* ロック */
	hr = soundeffect->pDSBuffer->lpVtbl->Lock( soundeffect->pDSBuffer, 0, 0, &pointer, &size, &pointer2, &size2, DSBLOCK_ENTIREBUFFER );
    if( FAILED( hr ) || size2 != 0 )
    {
        rb_raise( eDXRubyError, "サウンドバッファのロックに失敗しました - Lock" );
    }

	pcmwf.nChannels = 1;
	pcmwf.nSamplesPerSec = 44100 * 2;
	pcmwf.wBitsPerSample = 16;
	pcmwf.nBlockAlign = pcmwf.nChannels * pcmwf.wBitsPerSample / 8;
	pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
	desc.dwBufferBytes = size;

	count = 0;

	/* 波形生成 */
	for( i = 0; i < desc.dwBufferBytes / (pcmwf.wBitsPerSample / 8); i++ )
	{
		/* 指定時間単位でブロックを呼び出す */
		if ( i % (pcmwf.nSamplesPerSec / resolution) == 0 )
		{
			vf = rb_yield( obj );
			if( TYPE( vf ) != T_ARRAY )
			{
				soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
			    rb_raise(rb_eTypeError, "not valid value - SoundEffect_initialize");
				break;
			}
			switch (TYPE( rb_ary_entry(vf, 0) )) {
			  case T_FIXNUM:
			  case T_FLOAT:
				f = NUM2DBL( rb_ary_entry(vf, 0) );
			    break;
			  default:
				soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
			    rb_raise(rb_eTypeError, "not valid value - SoundEffect_initialize");
			    break;
			}
			switch (TYPE( rb_ary_entry(vf, 1) )) {
			  case T_FIXNUM:
			  case T_FLOAT:
				vol = NUM2DBL(rb_ary_entry(vf, 1));
			    break;
			  default:
				soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
			    rb_raise(rb_eTypeError, "not valid value - SoundEffect_initialize");
			    break;
			}
			/* 最大/最低周波数と最大ボリュームの制限 */
			f = f > pcmwf.nSamplesPerSec / 2.0f ? pcmwf.nSamplesPerSec / 2.0f : f;
			f = f < 20 ? 20 : f;
			vol = vol > 255 ? 255 : vol;
			vol = vol < 0 ? 0 : vol;
		}
		count = count + f;
		if( count >= pcmwf.nSamplesPerSec )
		{
			count = count - pcmwf.nSamplesPerSec;
		}

		data = calcwave(type, vol, count, pcmwf.nSamplesPerSec);

		if( data + (int)pointer[i] > 32767 )
		{
			pointer[i] = 32767;
		}
		else if( data + (int)pointer[i] < -32768 )
		{
			pointer[i] = -32768;
		}
		else
		{
			pointer[i] += data;
		}
	}

	/* アンロック */
	hr = soundeffect->pDSBuffer->lpVtbl->Unlock( soundeffect->pDSBuffer, pointer, size, pointer2, size2 );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "サウンドバッファのロックに失敗しました - Lock" );
    }

    return obj;

}


/*--------------------------------------------------------------------
   音を鳴らす
 ---------------------------------------------------------------------*/
static VALUE SoundEffect_play( VALUE obj )
{
    HRESULT hr;
    struct DXRubySoundEffect *soundeffect;
	
	Data_Get_Struct( obj, struct DXRubySoundEffect, soundeffect );

	/* とめる */
    hr = soundeffect->pDSBuffer->lpVtbl->Stop( soundeffect->pDSBuffer );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音の再生に失敗しました - SoundEffect_play" );
    }

	hr = soundeffect->pDSBuffer->lpVtbl->SetCurrentPosition( soundeffect->pDSBuffer, 0 );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音の再生に失敗しました - SoundEffect_play" );
    }

	/* 再生 */
    hr = soundeffect->pDSBuffer->lpVtbl->Play( soundeffect->pDSBuffer, 0, 0, 0 );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音の再生に失敗しました - SoundEffect_play" );
	}
    return obj;
}


/*--------------------------------------------------------------------
   音を止める
 ---------------------------------------------------------------------*/
static VALUE SoundEffect_stop( VALUE obj )
{
    HRESULT hr;
    struct DXRubySoundEffect *soundeffect;

    Data_Get_Struct( obj, struct DXRubySoundEffect, soundeffect );

    /* とめる */
    hr = soundeffect->pDSBuffer->lpVtbl->Stop( soundeffect->pDSBuffer );
    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "音の停止に失敗しました - Stop" );
    }

    return obj;
}



/*********************************************************************
 * Inputモジュール
 *
 * DirectInputを使用してキーボード・パッドの入力を行う。
 *********************************************************************/

/*--------------------------------------------------------------------
   Windowsメッセージ処理込みの入力更新
 ---------------------------------------------------------------------*/
static VALUE Input_update( VALUE obj )
{
    MSG msg;
    msg.message = WM_NULL;

    /* メッセージループ */
    /* WM_QUITが届くまでメッセージ処理をし続ける */
    /* メッセージが無い時はブロックをyieldする */
    while (1)
    {
        if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) != 0)
        {
            /* メッセージがある時 */
            TranslateMessage( &msg );
            DispatchMessage( &msg );
            if( msg.message == WM_QUIT )
            {
                return Qtrue;
            }
        }
        else
        {
            break;
        }
    }

    /* メッセージが無い時 */
    /* 入力状態更新 */
    inputupdate();

    return Qfalse;
}


/*--------------------------------------------------------------------
   Inputモジュールの入力処理
 ---------------------------------------------------------------------*/
static void inputupdate( void )
{
    int i, j;
    DIJOYSTATE paddata;

    /* フォーカスがはずれた場合、キーとパッド、マウスボタンの入力を受け付けなくする */
    if( g_bActive == 0 )
    {
        /* キーボードのデータをクリアする */
        ZeroMemory( &g_diKeyState, sizeof(g_diKeyState) );
        ZeroMemory( &g_diKeyStateOld, sizeof(g_diKeyStateOld) );

        /* パッドデータをクリアする */
        for( i = 0; i < g_JoystickCount; i++ )
        {
            for( j = 0; j < 20; j++ )
            {
                g_PadStateOld[i].button[j] = g_PadState[i].button[j];
                g_PadState[i].button[j] = 0;
            }
        }

        /* マウスボタンのデータをクリアする */
        g_byMouseStateOld_L = g_byMouseState_L;
        g_byMouseStateOld_M = g_byMouseState_M;
        g_byMouseStateOld_R = g_byMouseState_R;
        g_byMouseState_L = 0;
        g_byMouseState_M = 0;
        g_byMouseState_R = 0;

        return;
    }

    /* デバイスアクセス権を再取得する */
    g_pDIDKeyBoard->lpVtbl->Acquire( g_pDIDKeyBoard );

    for( i = 0; i < g_JoystickCount; i++ )
    {
        g_pDIDJoyPad[i]->lpVtbl->Poll( g_pDIDJoyPad[i] );
        g_pDIDJoyPad[i]->lpVtbl->Acquire( g_pDIDJoyPad[i] );
    }

    /* キーボードの直接データを取得する */
    memcpy( g_diKeyStateOld, g_diKeyState, sizeof(g_diKeyState) );
    g_pDIDKeyBoard->lpVtbl->GetDeviceState( g_pDIDKeyBoard, 256, g_diKeyState );

    /* ゲームパッドのデータを取得する */
    for( i = 0; i < g_JoystickCount; i++ )
    {
        int j;

        g_pDIDJoyPad[i]->lpVtbl->GetDeviceState( g_pDIDJoyPad[i], sizeof(DIJOYSTATE), &paddata );

        for( j = 0; j < 20; j++ )
        {
            g_PadStateOld[i].button[j] = g_PadState[i].button[j];
            g_PadState[i].button[j] = 0;
        }

        /* 左 */
        if( paddata.lX < g_PadInfo[i].left )
        {
            g_PadState[i].button[P_LEFT] = 1;
        }
        /* 右 */
        else if( paddata.lX > g_PadInfo[i].right )
        {
            g_PadState[i].button[P_RIGHT] = 1;
        }

        /* 上 */
        if( paddata.lY < g_PadInfo[i].up )
        {
            g_PadState[i].button[P_UP] = 1;
        }
        /* 下 */
        else if( paddata.lY > g_PadInfo[i].down )
        {
            g_PadState[i].button[P_DOWN] = 1;
        }

        /* ボタン状態 */
        for( j = 0; j < 16; j++)
        {
            g_PadState[i].button[j + 4] = paddata.rgbButtons[j] >> 7;
        }
    }

    /* マウスボタンの状態 */
    g_byMouseStateOld_L = g_byMouseState_L;
    g_byMouseStateOld_M = g_byMouseState_M;
    g_byMouseStateOld_R = g_byMouseState_R;
    g_byMouseState_L = GetKeyState( VK_LBUTTON );
    g_byMouseState_M = GetKeyState( VK_MBUTTON );
    g_byMouseState_R = GetKeyState( VK_RBUTTON );

    return;
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   横方向の入力をxの増分で返す
 ---------------------------------------------------------------------*/
static VALUE Input_x( int argc, VALUE *argv, VALUE obj )
{
    int x = 0;
	VALUE vnumber;
	int number;

    rb_scan_args( argc, argv, "01", &vnumber);

	number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));

	if( number < 0 || number >= PADMAX )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_x" );
    }

	/* ひだり */
    if( g_diKeyState[g_PadState[number].PadConfig[P_LEFT]] & 0x80  || g_PadState[number].button[P_LEFT] == 1 )
    {
        x = x - 1;
    }

    /* みぎ */
    if( g_diKeyState[g_PadState[number].PadConfig[P_RIGHT]] & 0x80  || g_PadState[number].button[P_RIGHT] == 1 )
    {
        x = x + 1;
    }

    return INT2FIX( x );
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   縦方向の入力をyの増分で返す
 ---------------------------------------------------------------------*/
static VALUE Input_y( int argc, VALUE *argv, VALUE obj )
{
    int y = 0;
	VALUE vnumber;
	int number;

    rb_scan_args( argc, argv, "01", &vnumber);

	number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));

	if( number < 0 || number >= PADMAX )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_y" );
    }


    /* うえ */
    if( g_diKeyState[g_PadState[number].PadConfig[P_UP]] & 0x80  || g_PadState[number].button[P_UP] == 1 )
    {
        y = y - 1;
    }

    /* した */
    if( g_diKeyState[g_PadState[number].PadConfig[P_DOWN]] & 0x80  || g_PadState[number].button[P_DOWN] == 1 )
    {
        y = y + 1;
    }

    return INT2FIX( y );
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押されていたらtrueになる。引数はキーコード。
 ---------------------------------------------------------------------*/
static VALUE Input_keyDown( VALUE obj , VALUE vkey )
{
    int key, padbutton = 0;

    key = NUM2INT( vkey );
    if( key < 0 || key >= 256 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_keyDown" );
    }

    if( g_diKeyConfig[key] != -1 && g_PadState[g_diKeyConfig[key] / 20].button[g_diKeyConfig[key] % 20] == 1 )
    {
        padbutton = 1;
    }
    
    if( g_diKeyState[key] & 0x80 ||  padbutton == 1)
    {
        return Qtrue;
    }

    return Qfalse;
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押した瞬間だけtrueになる。引数はキーコード。
 ---------------------------------------------------------------------*/
static VALUE Input_keyPush( VALUE obj , VALUE vkey )
{
    int key, padbutton = 0, padbuttonold = 0;

    key = NUM2INT( vkey );
    if( key < 0 || key >= 256 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_keyPush" );
    }

    if( g_diKeyConfig[key] != -1 )
    {
        if( g_PadState[g_diKeyConfig[key] / 20].button[g_diKeyConfig[key] % 20] == 1 )
        {
            padbutton = 1;
        }
        if( g_PadStateOld[g_diKeyConfig[key] / 20].button[g_diKeyConfig[key] % 20] == 1 )
        {
            padbuttonold = 1;
        }
    }

    if( (g_diKeyState[key] & 0x80) ||  padbutton == 1 ) /* 入力されている */
    {
        if( !(g_diKeyStateOld[key] & 0x80) && padbuttonold == 0 )    /* 前回OFFである */
        {
            g_diKeyCount[key] = 0;                  /* カウント初期化 */
            return Qtrue;                           /* なにはともあれTrue */
        }
        else                                    /* 前回ONだった */
        {
            g_diKeyCount[key]++;                    /* カウント */
            if( g_diKeyWait[key] <= g_diKeyCount[key] ) /* ウェイトタイムを超えたか */
            {
                if( g_diKeyInterval[key] != 0 &&
                    (g_diKeyCount[key] - g_diKeyWait[key]) % g_diKeyInterval[key] == 0 ) /* インターバルタイムごとに */
                {
                    return Qtrue;                           /* True */
                }
            }
        }
    }

    return Qfalse;
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押されていたらtrueになる。引数はボタン番号。
 ---------------------------------------------------------------------*/
static VALUE Input_padDown( int argc, VALUE *argv, VALUE obj )
{
	VALUE vnumber, vbutton;
	int number, button, key = 0;

    rb_scan_args( argc, argv, "11", &vbutton, &vnumber);

	button = NUM2INT( vbutton );
	number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));

	if( number < 0 || button < 0 || number >= PADMAX || button >= 20 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_padDown" );
    }

    if( g_PadState[number].PadConfig[button] != -1 && g_diKeyState[g_PadState[number].PadConfig[button]] & 0x80 )
    {
        key = 1;
    }

    if( key == 1 || g_PadState[number].button[button] == 1 )
    {
        return Qtrue;
    }

    return Qfalse;
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押した瞬間だけtrueになる。引数はボタン番号。
 ---------------------------------------------------------------------*/
static VALUE Input_padPush( int argc, VALUE *argv, VALUE obj )
{
	VALUE vnumber, vbutton;
	int number, button, key = 0, keyold = 0;

    rb_scan_args( argc, argv, "11", &vbutton, &vnumber);

	button = NUM2INT( vbutton );
	number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));

	if( number < 0 || button < 0 || number >= PADMAX || button >= 20 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_padPush" );
    }

    if( g_PadState[number].PadConfig[button] != -1 )
    {
        if( g_diKeyState[g_PadState[number].PadConfig[button]] & 0x80 )
        {
            key = 1;
        }
        if( g_diKeyStateOld[g_PadState[number].PadConfig[button]] & 0x80 )
        {
            keyold = 1;
        }
    }

    if( key == 1 || g_PadState[number].button[button] == 1 ) /* 入力されている */
    {
        if( keyold == 0 && g_PadStateOld[number].button[button] == 0 ) /* 前回OFFである */
        {
            g_PadState[number].count[button] = 0;                           /* カウント初期化 */
            return Qtrue;                                                   /* なにはともあれTrue */
        }
        else                                                           /* 前回ONだった */
        {
            g_PadState[number].count[button]++;                             /* カウント */
            if( g_PadState[number].wait[button] <= g_PadState[number].count[button] ) /* ウェイトタイムを超えたか */
            {
                if( g_PadState[number].interval[button] != 0 &&
                   (g_PadState[number].count[button] - g_PadState[number].wait[button])
                    % g_PadState[number].interval[button] == 0 )   /* インターバルタイムごとに */
                {
                    return Qtrue;                                                           /* True */
                }
            }
        }
    }

    return Qfalse;
}


/*--------------------------------------------------------------------
   パッドとキーの対応を設定する
 ---------------------------------------------------------------------*/
static void Input_SetConfig( int number, int pad, int key )
{
    int i, j;

    if( pad == -1 )    /* キーに対するパッド割り当て解除処理 */
    {
        g_diKeyConfig[key] = -1;
        for( i = 0; i < PADMAX; i++)
        {
            for( j = 0; j < 20; j++)
            {
                if( g_PadState[i].PadConfig[j] == key )
                {
                    g_PadState[i].PadConfig[j] = -1;
                }
            }
        }
    }
    else if( key == -1 || key == 0 ) /* パッドに対するキー割り当て解除処理 */
    {
        g_PadState[number].PadConfig[pad] = -1;
        for( i = 0; i < 256; i++)
        {
            if( g_diKeyConfig[i] == pad )
            {
                g_diKeyConfig[i] = -1;
            }
        }
    }
    else    /* 割り当て処理 */
    {
        g_PadState[number].PadConfig[pad] = key;
        g_diKeyConfig[key] = number * 20 + pad;
    }
}

static VALUE Input_setconfig( int argc, VALUE *argv, VALUE obj )
{
    VALUE vnumber, vpad, vkey;
    int number, pad, key;

    rb_scan_args( argc, argv, "21", &vpad, &vkey, &vnumber);

    number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));
    pad = (vpad == Qnil ? -1 : NUM2INT( vpad ));
    key = (vkey == Qnil ? -1 : NUM2INT( vkey ));

    if( number < 0 || pad < -1 || key < -1 || number >= PADMAX || pad >= 20 || key >= 256 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_setconfig" );
    }

    if( pad != -1 || key != -1 )
    {
        Input_SetConfig( number, pad, key );
    }

    return obj;
}


/*--------------------------------------------------------------------
   マウスホイールの状態を返す
 ---------------------------------------------------------------------*/
static VALUE Input_getmousewheelpos( VALUE obj )
{
    return INT2FIX(g_WindowInfo.mousewheelpos);
}


/*--------------------------------------------------------------------
   マウスホイールの状態を設定する
 ---------------------------------------------------------------------*/
static VALUE Input_setmousewheelpos( VALUE obj, VALUE wheelpos )
{
    g_WindowInfo.mousewheelpos = NUM2INT( wheelpos );

    return obj;
}


/*--------------------------------------------------------------------
   マウスのボタン状態を返す
 ---------------------------------------------------------------------*/
static VALUE Input_mouseDown( VALUE obj, VALUE button )
{

    switch( NUM2INT( button ) )
    {
    case M_LBUTTON:
        if( g_byMouseState_L & 0x80 )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }

    case M_RBUTTON:
        if( g_byMouseState_R & 0x80 )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }

    case M_MBUTTON:
        if( g_byMouseState_M & 0x80 )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }
    }

    return Qnil;
}


/*--------------------------------------------------------------------
   マウスのボタン状態を返す
 ---------------------------------------------------------------------*/
static VALUE Input_mousePush( VALUE obj, VALUE button )
{

    switch( NUM2INT( button ) )
    {
    case M_LBUTTON:
        if( (g_byMouseState_L & 0x80) && !(g_byMouseStateOld_L & 0x80) )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }

    case M_RBUTTON:
        if( g_byMouseState_R & 0x80 && !(g_byMouseStateOld_R & 0x80) )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }

    case M_MBUTTON:
        if( g_byMouseState_M & 0x80 && !(g_byMouseStateOld_M & 0x80) )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }
    }

    return Qnil;
}


/*--------------------------------------------------------------------
   マウスカーソルの位置を返す
 ---------------------------------------------------------------------*/
static VALUE Input_getmouseposx( VALUE obj )
{
    POINT cursor;

    GetCursorPos( &cursor );
    ScreenToClient( g_hWind, &cursor );
    return INT2FIX( cursor.x );
}


/*--------------------------------------------------------------------
   マウスカーソルの位置を返す
 ---------------------------------------------------------------------*/
static VALUE Input_getmouseposy( VALUE obj )
{
    POINT cursor;

    GetCursorPos( &cursor );
    ScreenToClient( g_hWind, &cursor );

    return INT2FIX( cursor.y );
}


/*--------------------------------------------------------------------
   マウス描画するかどうかの設定
 ---------------------------------------------------------------------*/
static VALUE Input_enablemouse( VALUE obj, VALUE draw )
{
    VALUE flag = Qfalse;
    int c;

    if( draw != Qnil && draw != Qfalse )
    {
        flag = Qtrue;
    }

    if( flag == Qtrue )
    {
        c = ShowCursor( TRUE );
        while( c < 0 ) c = ShowCursor( TRUE );
    }
    else
    {
        c = ShowCursor( FALSE );
        while( c >= 0 ) c = ShowCursor( FALSE );
    }

    g_WindowInfo.enablemouse = flag;

    return draw;
}


/*--------------------------------------------------------------------
   Inputモジュールの全キーオートリピート状態設定
 ---------------------------------------------------------------------*/
static VALUE Input_setrepeat( VALUE obj , VALUE vwait, VALUE vinterval )
{
    int wait, interval, i, j;

    wait = NUM2INT( vwait );
    interval = NUM2INT( vinterval );

    for( i = 0; i < 256; i++)
    {
        g_diKeyWait[i] = wait;
        g_diKeyInterval[i] = interval;
    }

    for( i = 0; i < PADMAX; i++)
    {
        for( j = 0; j < 20; j++)
        {
            g_PadState[i].wait[j] = wait;
            g_PadState[i].interval[j] = interval;
        }
    }

    return obj;
}


/*--------------------------------------------------------------------
   Inputモジュールのキーオートリピート状態設定
 ---------------------------------------------------------------------*/
static VALUE Input_setkeyrepeat( VALUE obj , VALUE vkey, VALUE vwait, VALUE vinterval )
{
    int key, wait, interval;

    key = NUM2INT( vkey );
    wait = NUM2INT( vwait );
    interval = NUM2INT( vinterval );

    if( key < 0 || key >= 256 || wait < 0 || interval < 0 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_setKeyRepeat" );
    }

    g_diKeyWait[key] = wait;
    g_diKeyInterval[key] = interval;

    return obj;
}


/*--------------------------------------------------------------------
   Inputモジュールのパッドオートリピート状態設定
 ---------------------------------------------------------------------*/
static VALUE Input_setpadrepeat( int argc, VALUE *argv, VALUE obj )
{
    VALUE vpad, vwait, vinterval, vnumber;
    int pad, wait, interval, number;

    rb_scan_args( argc, argv, "31", &vpad, &vwait, &vinterval, &vnumber );

    pad = NUM2INT( vpad );
    wait = NUM2INT( vwait );
    interval = NUM2INT( vinterval );
    number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));

    if( number < 0 || pad < 0 || number >= PADMAX || pad >= 20 ||
        wait < 0 || interval < 0 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_setPadRepeat" );
    }

    g_PadState[number].wait[pad] = wait;
    g_PadState[number].interval[pad] = interval;

    return obj;
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押されているキーの配列を返す。
 ---------------------------------------------------------------------*/
static VALUE Input_getKeys( VALUE obj )
{
    int i, j;
    VALUE buf[256];

    for( i = 0, j = 0; i < 256; i++ )
    {
        if( g_diKeyState[i] & 0x80 ) /* 入力されている */
        {
            buf[j++] = INT2FIX( i );
        }
    }

    return rb_ary_new4( j, buf );
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押されているパッドの配列を返す。
 ---------------------------------------------------------------------*/
static VALUE Input_getPads( int argc, VALUE *argv, VALUE obj )
{
    int i, j, number;
    VALUE buf[20];
    VALUE vnumber;

    rb_scan_args( argc, argv, "01", &vnumber);

    number = vnumber == Qnil ? 0 : NUM2INT( vnumber );

    for( i = 0, j = 0; i < 20; i++ )
    {
        if( g_PadState[number].button[i] == 1 )
        {
            buf[j++] = INT2FIX( i );
        }
    }

    return rb_ary_new4( j, buf );
}



/*********************************************************************
 * Fontクラス
 *
 * D3DXFontインターフェースを使用してフォントを描画する。
 * スプライト描画に混ぜ込むことでとりあえず実装。
 *********************************************************************/
/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void release_Font( struct DXRubyFont* font )
{
    /* フォントオブジェクトの開放 */
    if( font )
    {
		RELEASE( font->pD3DXFont );
        free( font );
        font = NULL;

    	g_iRefAll--;
    	if( g_iRefAll == 0 )
    	{
    		CoUninitialize();
    	}
    }
}


/*--------------------------------------------------------------------
   Fontクラスのallocate。メモリを確保する為にinitialize前に呼ばれる。
 ---------------------------------------------------------------------*/
static VALUE Font_allocate( VALUE klass )
{
    VALUE obj;
    struct DXRubyFont *font;

    /* DXRubyFontのメモリ取得＆Fontオブジェクト生成 */
    font = malloc( sizeof(struct DXRubyFont) );
    if( font == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Font_allocate" );
    return Data_Wrap_Struct(klass, 0, release_Font, font);
}


/*--------------------------------------------------------------------
   FontクラスのInitialize
 ---------------------------------------------------------------------*/
static VALUE Font_initialize( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyFont *font;
    HRESULT hr;
    D3DXFONT_DESC desc;
    VALUE size, fontname;
    LOGFONT logfont;

    /* デバイスオブジェクトの初期化チェック */
    if( g_pD3DDevice == NULL )
    {
        rb_raise( eDXRubyError, "DirectX Graphicsが初期化されていません" );
    }

    rb_scan_args( argc, argv, "11", &size, &fontname );

    desc.Height          = NUM2INT( size );
    desc.Width           = 0;
    desc.Weight          = FW_DONTCARE;
    desc.MipLevels       = 0;
    desc.Italic          = FALSE;
    desc.CharSet         = DEFAULT_CHARSET;
//    desc.CharSet         = SHIFTJIS_CHARSET;
    desc.OutputPrecision = 0;
    desc.Quality         = DEFAULT_QUALITY;
    desc.PitchAndFamily  = DEFAULT_PITCH || FF_DONTCARE;

    ZeroMemory( &logfont, sizeof(logfont) );
    logfont.lfHeight          = NUM2INT( size );
    logfont.lfWidth           = 0;
    logfont.lfCharSet         = DEFAULT_CHARSET;
//    logfont.lfCharSet         = SHIFTJIS_CHARSET;
    logfont.lfQuality         = ANTIALIASED_QUALITY;
    if( fontname == Qnil )
    {
        lstrcpy(desc.FaceName, "");
        lstrcpy(logfont.lfFaceName, "");
    }
    else
    {
        Check_Type(fontname, T_STRING);
        lstrcpy(desc.FaceName, RSTRING_PTR( fontname ));
        lstrcpy(logfont.lfFaceName, RSTRING_PTR( fontname ));
    }

    /* フォントオブジェクト取り出し */
    Data_Get_Struct( obj, struct DXRubyFont, font );

    hr = D3DXCreateFontIndirect( g_pD3DDevice, &desc, &font->pD3DXFont );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "フォントの作成に失敗しました - D3DXCreateFontIndirect" );
    }

    font->hFont = CreateFontIndirect( &logfont );

    if( font->hFont == NULL )
    {
        rb_raise( eDXRubyError, "フォントの作成に失敗しました - CreateFontIndirect" );
    }

    font->size = NUM2INT( size );
    g_iRefAll++;

    return obj;
}


/*--------------------------------------------------------------------
   Fontクラスのinstall
 ---------------------------------------------------------------------*/
static VALUE Font_install( VALUE klass, VALUE vstr )
{
    int result;
    Check_Type( vstr, T_STRING );

    result = AddFontResourceEx( RSTRING_PTR( vstr ), 0x10, 0 );
    if( result == 0 )
    {
        rb_raise( eDXRubyError, "フォントのインストールに失敗しました - Font_install" );
    }

    return INT2FIX( result );
}


/*--------------------------------------------------------------------
   フォントの幅を取得する
 ---------------------------------------------------------------------*/
static VALUE Font_getWidth( VALUE obj, VALUE str )
{
    HDC hDC;
    struct DXRubyFont *font;
    RECT rc;

    /* フォントオブジェクト取り出し */
    Data_Get_Struct( obj, struct DXRubyFont, font );

    hDC = GetDC( g_hWind );
    if( hDC == NULL )
    {
        rb_raise( eDXRubyError, "DCの取得に失敗しました - GetDC" );
    }

    SelectObject( hDC, font->hFont );
    SetRect( &rc, 0, 0, 1, 1 );
    DrawText( hDC, RSTRING_PTR( str ), -1, &rc, DT_LEFT | DT_NOCLIP | DT_NOPREFIX | DT_CALCRECT | DT_SINGLELINE);
    ReleaseDC( g_hWind, hDC );

    return INT2FIX( rc.right );
}


/*--------------------------------------------------------------------
   フォントのサイズを取得する
 ---------------------------------------------------------------------*/
static VALUE Font_getSize( VALUE obj )
{
    HDC hDC;
    struct DXRubyFont *font;
    RECT rc;

    /* フォントオブジェクト取り出し */
    Data_Get_Struct( obj, struct DXRubyFont, font );

    return INT2FIX( font->size );
}


/*********************************************************************
 * DXRubyInternalMarkクラス
 *
 * Window.draw系でImageオブジェクトを束縛しないとGCに回収されるのでその対策
 *********************************************************************/
/*--------------------------------------------------------------------
   DXRubyInternalMarkのmark関数
 ---------------------------------------------------------------------*/
static void DXRubyInternalMark_mark( VALUE klass )
{
    int i;

    for( i = 0; i < g_SpriteCount; i++ )
    {
        rb_gc_mark( g_SpriteList[i].sprite->value );
    }
}


/*
***************************************************************
*
*         Global functions
*
***************************************************************/

void Init_dxruby()
{
    HRESULT hr;
    int i, j;

    hr = CoInitialize(NULL);

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "COMの初期化に失敗しました - CoInitialize" );
    }

    /* インスタンスハンドル取得 */
    g_hInstance = (HINSTANCE)GetModuleHandle( NULL );

    /* DXRubyモジュール登録 */
    mDXRuby = rb_define_module( "DXRuby" );

    /* Windowモジュール登録 */
    mWindow = rb_define_module_under( mDXRuby, "Window" );

    /* Windowモジュールにメソッド登録 */
    rb_define_singleton_method( mWindow, "loop"     , Window_loop       , 0  );
    rb_define_singleton_method( mWindow, "draw"     , Window_draw       , -1 );
    rb_define_singleton_method( mWindow, "drawScale", Window_drawScale  , -1 );
    rb_define_singleton_method( mWindow, "drawRot"  , Window_drawRot    , -1 );
    rb_define_singleton_method( mWindow, "drawAlpha", Window_drawAlpha  , -1 );
    rb_define_singleton_method( mWindow, "drawAdd"  , Window_drawAdd    , -1 );
    rb_define_singleton_method( mWindow, "drawSub"  , Window_drawSub    , -1 );
    rb_define_singleton_method( mWindow, "drawEx"   , Window_drawEx     , -1 );
    rb_define_singleton_method( mWindow, "drawFont" , Window_drawFont   , -1 );
    rb_define_singleton_method( mWindow, "drawTile" , Window_drawTile   , -1  );
    rb_define_singleton_method( mWindow, "setViewport"  , Window_setViewport , -1 );
    rb_define_singleton_method( mWindow, "x"        , Window_x          , 0  );
    rb_define_singleton_method( mWindow, "x="       , Window_setx       , 1  );
    rb_define_singleton_method( mWindow, "y"        , Window_y          , 0  );
    rb_define_singleton_method( mWindow, "y="       , Window_sety       , 1  );
    rb_define_singleton_method( mWindow, "width"    , Window_width      , 0  );
    rb_define_singleton_method( mWindow, "width="   , Window_setwidth   , 1  );
    rb_define_singleton_method( mWindow, "height"   , Window_height     , 0  );
    rb_define_singleton_method( mWindow, "height="  , Window_setheight  , 1  );
    rb_define_singleton_method( mWindow, "caption"  , Window_getCaption , 0  );
    rb_define_singleton_method( mWindow, "caption=" , Window_setCaption , 1  );
    rb_define_singleton_method( mWindow, "scale"    , Window_getScale   , 0  );
    rb_define_singleton_method( mWindow, "scale="   , Window_setScale   , 1  );
    rb_define_singleton_method( mWindow, "windowed?", Window_getwindowed, 0  );
    rb_define_singleton_method( mWindow, "windowed=", Window_setwindowed, 1  );
    rb_define_singleton_method( mWindow, "real_fps" , Window_getrealfps , 0  );
    rb_define_singleton_method( mWindow, "fps"      , Window_getfps     , 0  );
    rb_define_singleton_method( mWindow, "fps="     , Window_setfps     , 1  );
    rb_define_singleton_method( mWindow, "frameskip?"   , Window_getframeskip, 0  );
    rb_define_singleton_method( mWindow, "frameskip="   , Window_setframeskip, 1  );
    rb_define_singleton_method( mWindow, "bgcolor"      , Window_getbgcolor , 0  );
    rb_define_singleton_method( mWindow, "bgcolor="     , Window_setbgcolor , 1  );
    rb_define_singleton_method( mWindow, "minFilter"    , Window_getMinFilter , 0 );
    rb_define_singleton_method( mWindow, "minFilter="   , Window_setMinFilter , 1 );
    rb_define_singleton_method( mWindow, "magFilter"    , Window_getMagFilter , 0 );
    rb_define_singleton_method( mWindow, "magFilter="   , Window_setMagFilter , 1 );
    rb_define_singleton_method( mWindow, "getLoad"      , Window_getload, 0  );
    rb_define_singleton_method( mWindow, "openFilename" , Window_openDialog, 2 );
    rb_define_singleton_method( mWindow, "saveFilename" , Window_saveDialog, 2 );
    rb_define_singleton_method( mWindow, "getScreenShot", Window_getScreenShot, -1 );
    rb_define_singleton_method( mWindow, "create"       , Window_create , 0 );
    rb_define_singleton_method( mWindow, "update"       , Window_update , 0 );
    rb_define_singleton_method( mWindow, "sync"         , Window_sync , 0 );
    rb_define_singleton_method( mWindow, "hWnd"         , Window_gethWnd, 0  );
    rb_define_singleton_method( mWindow, "loadIcon"     , Window_loadIcon, 1  );
//    rb_define_singleton_method( mWindow, "playMovie"    , Window_playMovie, 1  );


    /* Imageクラス定義 */
    cImage = rb_define_class_under( mDXRuby, "Image", rb_cObject );

    /* Imageクラスにクラスメソッド登録*/
    rb_define_singleton_method(cImage, "load", Image_load, -1);
//    rb_define_singleton_method(cImage, "slice", Image_slice, -1); /* そのうち消す予定 */
    rb_define_singleton_method(cImage, "loadToArray", Image_loadToArray, 3);
    rb_define_singleton_method(cImage, "createFromArray", Image_createFromArray, 3);
    rb_define_singleton_method(cImage, "loadFromFileInMemory", Image_loadFromFileInMemory, 1);


    /* Imageクラスにメソッド登録*/
    rb_define_method( cImage, "initialize", Image_initialize, -1 );
    rb_define_method( cImage, "width"     , Image_getWidth  , 0 );
    rb_define_method( cImage, "height"    , Image_getHeight , 0 );
    rb_define_method( cImage, "[]="       , Image_setPixel  , 3 );
    rb_define_method( cImage, "[]"        , Image_getPixel  , 2 );
    rb_define_method( cImage, "box"       , Image_box       , 5 );
    rb_define_method( cImage, "boxFill"   , Image_boxFill   , 5 );
    rb_define_method( cImage, "fill"      , Image_fill      , 1 );
    rb_define_method( cImage, "line"      , Image_line      , 5 );
    rb_define_method( cImage, "circle"    , Image_circle    , 4 );
    rb_define_method( cImage, "circleFill", Image_circleFill, 4 );
    rb_define_method( cImage, "compare"   , Image_compare   , 3 );
    rb_define_method( cImage, "copyRect"  , Image_copyRect  , -1 );
    rb_define_method( cImage, "draw"      , Image_draw      , -1 );
    rb_define_method( cImage, "drawFont"  , Image_drawFont  , -1 );
    rb_define_method( cImage, "save"      , Image_save      , -1 );
    rb_define_method( cImage, "slice"     , Image_slice_instance, -1);
    rb_define_method( cImage, "dup"       , Image_dup       , 0 );
    rb_define_method( cImage, "clone"     , Image_dup       , 0 );
    rb_define_method( cImage, "setColorKey", Image_setColorKey , 1 );

    /* Imageオブジェクトを生成した時にinitializeの前に呼ばれるメモリ割り当て関数登録 */
    rb_define_alloc_func( cImage, Image_allocate );


    /* Inputモジュール登録 */
    mInput = rb_define_module_under( mDXRuby, "Input" );

    /* Inputモジュールにメソッド登録 */
    rb_define_singleton_method( mInput, "x"              , Input_x               , -1 );
    rb_define_singleton_method( mInput, "y"              , Input_y               , -1 );
    rb_define_singleton_method( mInput, "mousePosX"      , Input_getmouseposx    , 0  );
    rb_define_singleton_method( mInput, "mousePosY"      , Input_getmouseposy    , 0  );
    rb_define_singleton_method( mInput, "keyDown?"       , Input_keyDown         , 1  );
    rb_define_singleton_method( mInput, "keyPush?"       , Input_keyPush         , 1  );
    rb_define_singleton_method( mInput, "padDown?"       , Input_padDown         , -1 );
    rb_define_singleton_method( mInput, "padPush?"       , Input_padPush         , -1 );
    rb_define_singleton_method( mInput, "mouseDown?"     , Input_mouseDown       , 1  );
    rb_define_singleton_method( mInput, "mousePush?"     , Input_mousePush       , 1  );
    rb_define_singleton_method( mInput, "mouseEnable="   , Input_enablemouse     , 1  );
    rb_define_singleton_method( mInput, "mouseWheelPos"  , Input_getmousewheelpos, 0  );
    rb_define_singleton_method( mInput, "mouseWheelPos=" , Input_setmousewheelpos, 1  );
    rb_define_singleton_method( mInput, "setConfig"      , Input_setconfig       , -1 );
    rb_define_singleton_method( mInput, "setRepeat"      , Input_setrepeat       , 2  );
    rb_define_singleton_method( mInput, "update"         , Input_update          , 0  );
    rb_define_singleton_method( mInput, "setKeyRepeat"   , Input_setkeyrepeat    , 3  );
    rb_define_singleton_method( mInput, "setPadRepeat"   , Input_setpadrepeat    , -1 );
    rb_define_singleton_method( mInput, "keys"           , Input_getKeys         , 0  );
    rb_define_singleton_method( mInput, "pads"           , Input_getPads         , -1 );


    /* Fontクラス登録 */
    cFont = rb_define_class_under( mDXRuby, "Font", rb_cObject );

    /* Fontクラスにクラスメソッド登録*/
    rb_define_singleton_method(cFont, "install", Font_install, 1);

    /* Fontクラスにメソッド登録*/
    rb_define_method( cFont, "initialize", Font_initialize, -1 );
    rb_define_method( cFont, "getWidth"  , Font_getWidth  , 1 );
    rb_define_method( cFont, "size"      , Font_getSize  , 0 );

    /* Fontオブジェクトを生成した時にinitializeの前に呼ばれるメモリ割り当て関数登録 */
    rb_define_alloc_func( cFont, Font_allocate );


	/* Soundクラス定義 */
    cSound = rb_define_class_under( mDXRuby, "Sound", rb_cObject );

    /* Soundクラスにメソッド登録*/
    rb_define_method( cSound, "initialize"   , Sound_initialize   , 1 );
    rb_define_method( cSound, "play"         , Sound_play      , 0 );
//    rb_define_method( cSound, "bgmplay"      , Sound_bgmplay      , 0 );
    rb_define_method( cSound, "stop"         , Sound_stop         , 0 );
    rb_define_method( cSound, "setVolume"    , Sound_setVolume    , -1 );
    rb_define_method( cSound, "start="       , Sound_setStart     , 1 );
    rb_define_method( cSound, "loopStart="   , Sound_setLoopStart , 1 );
    rb_define_method( cSound, "loopEnd="     , Sound_setLoopEnd   , 1 );
    rb_define_method( cSound, "loopCount="   , Sound_setLoopCount , 1 );

    /* Soundオブジェクトを生成した時にinitializeの前に呼ばれるメモリ割り当て関数登録 */
    rb_define_alloc_func( cSound, Sound_allocate );


	/* SoundEffectクラス定義 */
    cSoundEffect = rb_define_class_under( mDXRuby, "SoundEffect", rb_cObject );

    /* SoundEffectクラスにメソッド登録*/
    rb_define_method( cSoundEffect, "initialize", SoundEffect_initialize, -1 );
    rb_define_method( cSoundEffect, "play"      , SoundEffect_play      , 0 );
    rb_define_method( cSoundEffect, "stop"      , SoundEffect_stop      , 0 );
    rb_define_method( cSoundEffect, "add"       , SoundEffect_add       , -1 );

    /* SoundEffectオブジェクトを生成した時にinitializeの前に呼ばれるメモリ割り当て関数登録 */
    rb_define_alloc_func( cSoundEffect, SoundEffect_allocate );


    /* DXRubyInternalMarkクラス定義 */
    cDXRubyInternalMark = rb_define_class_under( mDXRuby, "DXRubyInternalMark", rb_cObject );
    oDXRubyInternalMark = Data_Wrap_Struct(cDXRubyInternalMark, DXRubyInternalMark_mark, 0, 0);
    rb_global_variable( &oDXRubyInternalMark );


    /* キーボードのスキャンコード定数設定 */
    rb_define_const( mDXRuby, "K_ESCAPE"     , INT2FIX(DIK_ESCAPE) );
    rb_define_const( mDXRuby, "K_1"          , INT2FIX(DIK_1) );
    rb_define_const( mDXRuby, "K_2"          , INT2FIX(DIK_2) );
    rb_define_const( mDXRuby, "K_3"          , INT2FIX(DIK_3) );
    rb_define_const( mDXRuby, "K_4"          , INT2FIX(DIK_4) );
    rb_define_const( mDXRuby, "K_5"          , INT2FIX(DIK_5) );
    rb_define_const( mDXRuby, "K_6"          , INT2FIX(DIK_6) );
    rb_define_const( mDXRuby, "K_7"          , INT2FIX(DIK_7) );
    rb_define_const( mDXRuby, "K_8"          , INT2FIX(DIK_8) );
    rb_define_const( mDXRuby, "K_9"          , INT2FIX(DIK_9) );
    rb_define_const( mDXRuby, "K_0"          , INT2FIX(DIK_0) );
    rb_define_const( mDXRuby, "K_MINUS"      , INT2FIX(DIK_MINUS) );
    rb_define_const( mDXRuby, "K_EQUALS"     , INT2FIX(DIK_EQUALS) );
    rb_define_const( mDXRuby, "K_BACK"       , INT2FIX(DIK_BACK) );
    rb_define_const( mDXRuby, "K_TAB"        , INT2FIX(DIK_TAB) );
    rb_define_const( mDXRuby, "K_Q"          , INT2FIX(DIK_Q) );
    rb_define_const( mDXRuby, "K_W"          , INT2FIX(DIK_W) );
    rb_define_const( mDXRuby, "K_E"          , INT2FIX(DIK_E) );
    rb_define_const( mDXRuby, "K_R"          , INT2FIX(DIK_R) );
    rb_define_const( mDXRuby, "K_T"          , INT2FIX(DIK_T) );
    rb_define_const( mDXRuby, "K_Y"          , INT2FIX(DIK_Y) );
    rb_define_const( mDXRuby, "K_U"          , INT2FIX(DIK_U) );
    rb_define_const( mDXRuby, "K_I"          , INT2FIX(DIK_I) );
    rb_define_const( mDXRuby, "K_O"          , INT2FIX(DIK_O) );
    rb_define_const( mDXRuby, "K_P"          , INT2FIX(DIK_P) );
    rb_define_const( mDXRuby, "K_LBRACKET"   , INT2FIX(DIK_LBRACKET) );
    rb_define_const( mDXRuby, "K_RBRACKET"   , INT2FIX(DIK_RBRACKET) );
    rb_define_const( mDXRuby, "K_RETURN"     , INT2FIX(DIK_RETURN) );
    rb_define_const( mDXRuby, "K_LCONTROL"   , INT2FIX(DIK_LCONTROL) );
    rb_define_const( mDXRuby, "K_A"          , INT2FIX(DIK_A) );
    rb_define_const( mDXRuby, "K_S"          , INT2FIX(DIK_S) );
    rb_define_const( mDXRuby, "K_D"          , INT2FIX(DIK_D) );
    rb_define_const( mDXRuby, "K_F"          , INT2FIX(DIK_F) );
    rb_define_const( mDXRuby, "K_G"          , INT2FIX(DIK_G) );
    rb_define_const( mDXRuby, "K_H"          , INT2FIX(DIK_H) );
    rb_define_const( mDXRuby, "K_J"          , INT2FIX(DIK_J) );
    rb_define_const( mDXRuby, "K_K"          , INT2FIX(DIK_K) );
    rb_define_const( mDXRuby, "K_L"          , INT2FIX(DIK_L) );
    rb_define_const( mDXRuby, "K_SEMICOLON"  , INT2FIX(DIK_SEMICOLON) );
    rb_define_const( mDXRuby, "K_APOSTROPHE" , INT2FIX(DIK_APOSTROPHE) );
    rb_define_const( mDXRuby, "K_GRAVE"      , INT2FIX(DIK_GRAVE) );
    rb_define_const( mDXRuby, "K_LSHIFT"     , INT2FIX(DIK_LSHIFT) );
    rb_define_const( mDXRuby, "K_BACKSLASH"  , INT2FIX(DIK_BACKSLASH) );
    rb_define_const( mDXRuby, "K_Z"          , INT2FIX(DIK_Z) );
    rb_define_const( mDXRuby, "K_X"          , INT2FIX(DIK_X) );
    rb_define_const( mDXRuby, "K_C"          , INT2FIX(DIK_C) );
    rb_define_const( mDXRuby, "K_V"          , INT2FIX(DIK_V) );
    rb_define_const( mDXRuby, "K_B"          , INT2FIX(DIK_B) );
    rb_define_const( mDXRuby, "K_N"          , INT2FIX(DIK_N) );
    rb_define_const( mDXRuby, "K_M"          , INT2FIX(DIK_M) );
    rb_define_const( mDXRuby, "K_COMMA"      , INT2FIX(DIK_COMMA) );
    rb_define_const( mDXRuby, "K_PERIOD"     , INT2FIX(DIK_PERIOD) );
    rb_define_const( mDXRuby, "K_SLASH"      , INT2FIX(DIK_SLASH) );
    rb_define_const( mDXRuby, "K_RSHIFT"     , INT2FIX(DIK_RSHIFT) );
    rb_define_const( mDXRuby, "K_MULTIPLY"   , INT2FIX(DIK_MULTIPLY) );
    rb_define_const( mDXRuby, "K_LMENU"      , INT2FIX(DIK_LMENU) );
    rb_define_const( mDXRuby, "K_SPACE"      , INT2FIX(DIK_SPACE) );
    rb_define_const( mDXRuby, "K_CAPITAL"    , INT2FIX(DIK_CAPITAL) );
    rb_define_const( mDXRuby, "K_F1"         , INT2FIX(DIK_F1) );
    rb_define_const( mDXRuby, "K_F2"         , INT2FIX(DIK_F2) );
    rb_define_const( mDXRuby, "K_F3"         , INT2FIX(DIK_F3) );
    rb_define_const( mDXRuby, "K_F4"         , INT2FIX(DIK_F4) );
    rb_define_const( mDXRuby, "K_F5"         , INT2FIX(DIK_F5) );
    rb_define_const( mDXRuby, "K_F6"         , INT2FIX(DIK_F6) );
    rb_define_const( mDXRuby, "K_F7"         , INT2FIX(DIK_F7) );
    rb_define_const( mDXRuby, "K_F8"         , INT2FIX(DIK_F8) );
    rb_define_const( mDXRuby, "K_F9"         , INT2FIX(DIK_F9) );
    rb_define_const( mDXRuby, "K_F10"        , INT2FIX(DIK_F10) );
    rb_define_const( mDXRuby, "K_NUMLOCK"    , INT2FIX(DIK_NUMLOCK) );
    rb_define_const( mDXRuby, "K_SCROLL"     , INT2FIX(DIK_SCROLL) );
    rb_define_const( mDXRuby, "K_NUMPAD7"    , INT2FIX(DIK_NUMPAD7) );
    rb_define_const( mDXRuby, "K_NUMPAD8"    , INT2FIX(DIK_NUMPAD8) );
    rb_define_const( mDXRuby, "K_NUMPAD9"    , INT2FIX(DIK_NUMPAD9) );
    rb_define_const( mDXRuby, "K_SUBTRACT"   , INT2FIX(DIK_SUBTRACT) );
    rb_define_const( mDXRuby, "K_NUMPAD4"    , INT2FIX(DIK_NUMPAD4) );
    rb_define_const( mDXRuby, "K_NUMPAD5"    , INT2FIX(DIK_NUMPAD5) );
    rb_define_const( mDXRuby, "K_NUMPAD6"    , INT2FIX(DIK_NUMPAD6) );
    rb_define_const( mDXRuby, "K_ADD"        , INT2FIX(DIK_ADD) );
    rb_define_const( mDXRuby, "K_NUMPAD1"    , INT2FIX(DIK_NUMPAD1) );
    rb_define_const( mDXRuby, "K_NUMPAD2"    , INT2FIX(DIK_NUMPAD2) );
    rb_define_const( mDXRuby, "K_NUMPAD3"    , INT2FIX(DIK_NUMPAD3) );
    rb_define_const( mDXRuby, "K_NUMPAD0"    , INT2FIX(DIK_NUMPAD0) );
    rb_define_const( mDXRuby, "K_DECIMAL"    , INT2FIX(DIK_DECIMAL) );
    rb_define_const( mDXRuby, "K_OEM_102"    , INT2FIX(DIK_OEM_102) );
    rb_define_const( mDXRuby, "K_F11"        , INT2FIX(DIK_F11) );
    rb_define_const( mDXRuby, "K_F12"        , INT2FIX(DIK_F12) );
    rb_define_const( mDXRuby, "K_F13"        , INT2FIX(DIK_F13) );
    rb_define_const( mDXRuby, "K_F14"        , INT2FIX(DIK_F14) );
    rb_define_const( mDXRuby, "K_F15"        , INT2FIX(DIK_F15) );
    rb_define_const( mDXRuby, "K_KANA"       , INT2FIX(DIK_KANA) );
    rb_define_const( mDXRuby, "K_ABNT_C1"    , INT2FIX(DIK_ABNT_C1) );
    rb_define_const( mDXRuby, "K_CONVERT"    , INT2FIX(DIK_CONVERT) );
    rb_define_const( mDXRuby, "K_NOCONVERT"  , INT2FIX(DIK_NOCONVERT) );
    rb_define_const( mDXRuby, "K_YEN"        , INT2FIX(DIK_YEN) );
    rb_define_const( mDXRuby, "K_ABNT_C2"    , INT2FIX(DIK_ABNT_C2) );
    rb_define_const( mDXRuby, "K_NUMPADEQUALS", INT2FIX(DIK_NUMPADEQUALS) );
    rb_define_const( mDXRuby, "K_PREVTRACK"  , INT2FIX(DIK_PREVTRACK) );
    rb_define_const( mDXRuby, "K_AT"         , INT2FIX(DIK_AT) );
    rb_define_const( mDXRuby, "K_COLON"      , INT2FIX(DIK_COLON) );
    rb_define_const( mDXRuby, "K_UNDERLINE"  , INT2FIX(DIK_UNDERLINE) );
    rb_define_const( mDXRuby, "K_KANJI"      , INT2FIX(DIK_KANJI) );
    rb_define_const( mDXRuby, "K_STOP"       , INT2FIX(DIK_STOP) );
    rb_define_const( mDXRuby, "K_AX"         , INT2FIX(DIK_AX) );
    rb_define_const( mDXRuby, "K_UNLABELED"  , INT2FIX(DIK_UNLABELED) );
    rb_define_const( mDXRuby, "K_NEXTTRACK"  , INT2FIX(DIK_NEXTTRACK) );
    rb_define_const( mDXRuby, "K_NUMPADENTER", INT2FIX(DIK_NUMPADENTER) );
    rb_define_const( mDXRuby, "K_RCONTROL"   , INT2FIX(DIK_RCONTROL) );
    rb_define_const( mDXRuby, "K_MUTE"       , INT2FIX(DIK_MUTE) );
    rb_define_const( mDXRuby, "K_CALCULATOR" , INT2FIX(DIK_CALCULATOR) );
    rb_define_const( mDXRuby, "K_PLAYPAUSE"  , INT2FIX(DIK_PLAYPAUSE) );
    rb_define_const( mDXRuby, "K_MEDIASTOP"  , INT2FIX(DIK_MEDIASTOP) );
    rb_define_const( mDXRuby, "K_VOLUMEDOWN" , INT2FIX(DIK_VOLUMEDOWN) );
    rb_define_const( mDXRuby, "K_VOLUMEUP"   , INT2FIX(DIK_VOLUMEUP) );
    rb_define_const( mDXRuby, "K_WEBHOME"    , INT2FIX(DIK_WEBHOME) );
    rb_define_const( mDXRuby, "K_NUMPADCOMMA", INT2FIX(DIK_NUMPADCOMMA) );
    rb_define_const( mDXRuby, "K_DIVIDE"     , INT2FIX(DIK_DIVIDE) );
    rb_define_const( mDXRuby, "K_SYSRQ"      , INT2FIX(DIK_SYSRQ) );
    rb_define_const( mDXRuby, "K_RMENU"      , INT2FIX(DIK_RMENU) );
    rb_define_const( mDXRuby, "K_PAUSE"      , INT2FIX(DIK_PAUSE) );
    rb_define_const( mDXRuby, "K_HOME"       , INT2FIX(DIK_HOME) );
    rb_define_const( mDXRuby, "K_UP"         , INT2FIX(DIK_UP) );
    rb_define_const( mDXRuby, "K_PRIOR"      , INT2FIX(DIK_PRIOR) );
    rb_define_const( mDXRuby, "K_LEFT"       , INT2FIX(DIK_LEFT) );
    rb_define_const( mDXRuby, "K_RIGHT"      , INT2FIX(DIK_RIGHT) );
    rb_define_const( mDXRuby, "K_END"        , INT2FIX(DIK_END) );
    rb_define_const( mDXRuby, "K_DOWN"       , INT2FIX(DIK_DOWN) );
    rb_define_const( mDXRuby, "K_NEXT"       , INT2FIX(DIK_NEXT) );
    rb_define_const( mDXRuby, "K_INSERT"     , INT2FIX(DIK_INSERT) );
    rb_define_const( mDXRuby, "K_DELETE"     , INT2FIX(DIK_DELETE) );
    rb_define_const( mDXRuby, "K_LWIN"       , INT2FIX(DIK_LWIN) );
    rb_define_const( mDXRuby, "K_RWIN"       , INT2FIX(DIK_RWIN) );
    rb_define_const( mDXRuby, "K_APPS"       , INT2FIX(DIK_APPS) );
    rb_define_const( mDXRuby, "K_POWER"      , INT2FIX(DIK_POWER) );
    rb_define_const( mDXRuby, "K_SLEEP"      , INT2FIX(DIK_SLEEP) );
    rb_define_const( mDXRuby, "K_WAKE"       , INT2FIX(DIK_WAKE) );
    rb_define_const( mDXRuby, "K_WEBSEARCH"  , INT2FIX(DIK_WEBSEARCH) );
    rb_define_const( mDXRuby, "K_WEBFAVORITES", INT2FIX(DIK_WEBFAVORITES) );
    rb_define_const( mDXRuby, "K_WEBREFRESH" , INT2FIX(DIK_WEBREFRESH) );
    rb_define_const( mDXRuby, "K_WEBSTOP"    , INT2FIX(DIK_WEBSTOP) );
    rb_define_const( mDXRuby, "K_WEBFORWARD" , INT2FIX(DIK_WEBFORWARD) );
    rb_define_const( mDXRuby, "K_WEBBACK"    , INT2FIX(DIK_WEBBACK) );
    rb_define_const( mDXRuby, "K_MYCOMPUTER" , INT2FIX(DIK_MYCOMPUTER) );
    rb_define_const( mDXRuby, "K_MAIL"       , INT2FIX(DIK_MAIL) );
    rb_define_const( mDXRuby, "K_MEDIASELECT", INT2FIX(DIK_MEDIASELECT) );
    rb_define_const( mDXRuby, "K_BACKSPACE"  , INT2FIX(DIK_BACK) );
    rb_define_const( mDXRuby, "K_NUMPADSTAR" , INT2FIX(DIK_MULTIPLY) );
    rb_define_const( mDXRuby, "K_LALT"       , INT2FIX(DIK_LMENU) );
    rb_define_const( mDXRuby, "K_CAPSLOCK"   , INT2FIX(DIK_CAPITAL) );
    rb_define_const( mDXRuby, "K_NUMPADMINUS", INT2FIX(DIK_SUBTRACT) );
    rb_define_const( mDXRuby, "K_NUMPADPLUS" , INT2FIX(DIK_ADD) );
    rb_define_const( mDXRuby, "K_NUMPADPERIOD", INT2FIX(DIK_DECIMAL) );
    rb_define_const( mDXRuby, "K_NUMPADSLASH", INT2FIX(DIK_DIVIDE) );
    rb_define_const( mDXRuby, "K_RALT"       , INT2FIX(DIK_RMENU) );
    rb_define_const( mDXRuby, "K_UPARROW"    , INT2FIX(DIK_UP) );
    rb_define_const( mDXRuby, "K_PGUP"       , INT2FIX(DIK_PRIOR) );
    rb_define_const( mDXRuby, "K_LEFTARROW"  , INT2FIX(DIK_LEFT) );
    rb_define_const( mDXRuby, "K_RIGHTARROW" , INT2FIX(DIK_RIGHT) );
    rb_define_const( mDXRuby, "K_DOWNARROW"  , INT2FIX(DIK_DOWN) );
    rb_define_const( mDXRuby, "K_PGDN"       , INT2FIX(DIK_NEXT) );

    rb_define_const( mDXRuby, "P_UP"         , INT2FIX(P_UP)       );
    rb_define_const( mDXRuby, "P_LEFT"       , INT2FIX(P_LEFT)     );
    rb_define_const( mDXRuby, "P_RIGHT"      , INT2FIX(P_RIGHT)    );
    rb_define_const( mDXRuby, "P_DOWN"       , INT2FIX(P_DOWN)     );
    rb_define_const( mDXRuby, "P_BUTTON0"    , INT2FIX(P_BUTTON0)  );
    rb_define_const( mDXRuby, "P_BUTTON1"    , INT2FIX(P_BUTTON1)  );
    rb_define_const( mDXRuby, "P_BUTTON2"    , INT2FIX(P_BUTTON2)  );
    rb_define_const( mDXRuby, "P_BUTTON3"    , INT2FIX(P_BUTTON3)  );
    rb_define_const( mDXRuby, "P_BUTTON4"    , INT2FIX(P_BUTTON4)  );
    rb_define_const( mDXRuby, "P_BUTTON5"    , INT2FIX(P_BUTTON5)  );
    rb_define_const( mDXRuby, "P_BUTTON6"    , INT2FIX(P_BUTTON6)  );
    rb_define_const( mDXRuby, "P_BUTTON7"    , INT2FIX(P_BUTTON7)  );
    rb_define_const( mDXRuby, "P_BUTTON8"    , INT2FIX(P_BUTTON8)  );
    rb_define_const( mDXRuby, "P_BUTTON9"    , INT2FIX(P_BUTTON9)  );
    rb_define_const( mDXRuby, "P_BUTTON10"   , INT2FIX(P_BUTTON10) );
    rb_define_const( mDXRuby, "P_BUTTON11"   , INT2FIX(P_BUTTON11) );
    rb_define_const( mDXRuby, "P_BUTTON12"   , INT2FIX(P_BUTTON12) );
    rb_define_const( mDXRuby, "P_BUTTON13"   , INT2FIX(P_BUTTON13) );
    rb_define_const( mDXRuby, "P_BUTTON14"   , INT2FIX(P_BUTTON14) );
    rb_define_const( mDXRuby, "P_BUTTON15"   , INT2FIX(P_BUTTON15) );

    rb_define_const( mDXRuby, "M_LBUTTON"    , INT2FIX(M_LBUTTON)  );
    rb_define_const( mDXRuby, "M_RBUTTON"    , INT2FIX(M_RBUTTON)  );
    rb_define_const( mDXRuby, "M_MBUTTON"    , INT2FIX(M_MBUTTON)  );

    rb_define_const( mDXRuby, "FORMAT_JPEG"  , INT2FIX(FORMAT_JPEG));
    rb_define_const( mDXRuby, "FORMAT_JPG"   , INT2FIX(FORMAT_JPG) );
//    rb_define_const( mDXRuby, "FORMAT_PNG"   , INT2FIX(FORMAT_PNG) );
    rb_define_const( mDXRuby, "FORMAT_BMP"   , INT2FIX(FORMAT_BMP) );
    rb_define_const( mDXRuby, "FORMAT_DDS"   , INT2FIX(FORMAT_DDS) );

    rb_define_const( mDXRuby, "WAVE_SIN"     , INT2FIX(WAVE_SIN) );
    rb_define_const( mDXRuby, "WAVE_SAW"     , INT2FIX(WAVE_SAW) );
    rb_define_const( mDXRuby, "WAVE_TRI"     , INT2FIX(WAVE_TRI) );
    rb_define_const( mDXRuby, "WAVE_RECT"    , INT2FIX(WAVE_RECT) );

    rb_define_const( mDXRuby, "TEXF_POINT"   , INT2FIX(D3DTEXF_POINT) );
    rb_define_const( mDXRuby, "TEXF_LINEAR"   , INT2FIX(D3DTEXF_LINEAR) );
    rb_define_const( mDXRuby, "TEXF_GAUSSIANQUAD", INT2FIX(D3DTEXF_GAUSSIANQUAD) );

    /* 例外定義 */
    eDXRubyError = rb_define_class_under( mDXRuby, "DXRubyError", rb_eRuntimeError );

    /* 定数登録 */
    rb_define_const( mDXRuby, "VERSION", rb_str_new2( DXRUBY_VERSION ) );

    {
        VALUE temp;
        temp = rb_eval_string( "$dxruby_no_include" );
        if( temp == Qfalse || temp == Qnil )
        {
            rb_include_module( rb_cObject, mDXRuby );
        }
    }

    /* シンボル定義 */
    symbol_blend          = ID2SYM(rb_intern("blend"));
    symbol_angle          = ID2SYM(rb_intern("angle"));
    symbol_alpha          = ID2SYM(rb_intern("alpha"));
    symbol_scalex         = ID2SYM(rb_intern("scalex"));
    symbol_scaley         = ID2SYM(rb_intern("scaley"));
    symbol_centerx        = ID2SYM(rb_intern("centerx"));
    symbol_centery        = ID2SYM(rb_intern("centery"));
    symbol_z              = ID2SYM(rb_intern("z"));
    symbol_color          = ID2SYM(rb_intern("color"));
    symbol_add            = ID2SYM(rb_intern("add"));
    symbol_add2           = ID2SYM(rb_intern("add2"));
    symbol_sub            = ID2SYM(rb_intern("sub"));

    /* 終了時に実行する関数 */
    rb_set_end_proc( Window_shutdown, Qnil );

    /* スプライト構造体の初期値設定 */
	g_SpriteCount = 0;
	g_SpriteAllocateCount = 128;
	g_SpriteList = malloc( g_SpriteAllocateCount * sizeof(struct DXRubySpriteList) );
	g_SpriteStruct = malloc( g_SpriteAllocateCount * sizeof(struct DXRubySprite) );

    g_WindowInfo.width    = 640;
    g_WindowInfo.height   = 480;
    g_WindowInfo.windowed = Qtrue;
    g_WindowInfo.created  = Qfalse;
    g_WindowInfo.scale    = 1;
    g_WindowInfo.enablemouse = Qtrue;
    g_WindowInfo.mousewheelpos = 0;
    g_WindowInfo.fps = 60;
    g_WindowInfo.frameskip = Qfalse;
    g_WindowInfo.r = 0;
    g_WindowInfo.g = 0;
    g_WindowInfo.b = 0;
    g_WindowInfo.minfilter = D3DTEXF_LINEAR;
    g_WindowInfo.magfilter = D3DTEXF_LINEAR;
    g_WindowInfo.hIcon = 0;

    /* 割り当て初期化 */
    for( i = 0; i < 256; i++)
    {
        g_diKeyConfig[i] = -1;
    }
    for( i = 0; i < PADMAX; i++)
    {
        for( j = 0; j < 20; j++)
        {
            g_PadState[i].PadConfig[j] = -1;
        }
    }

    /* デフォルトの割り当て */
    Input_SetConfig( 0, P_LEFT   , DIK_LEFT  );
    Input_SetConfig( 0, P_RIGHT  , DIK_RIGHT );
    Input_SetConfig( 0, P_UP     , DIK_UP    );
    Input_SetConfig( 0, P_DOWN   , DIK_DOWN  );
    Input_SetConfig( 0, P_BUTTON0, DIK_Z     );
    Input_SetConfig( 0, P_BUTTON1, DIK_X     );
    Input_SetConfig( 0, P_BUTTON2, DIK_C     );

	/* ウィンドウ作成(この時点では非表示) */
    InitWindow();

    /* DirectX Graphicsの初期化 */
    InitDXGraphics();

    /* DirectInput初期化 */
    InitDirectInput();
}


/*--------------------------------------------------------------------
  （内部関数）ウィンドウの生成
 ---------------------------------------------------------------------*/
static void InitWindow( void )
{
    WNDCLASSEX wcex;
    RECT rect;

    /* ウインドウ・クラスの登録 */
    wcex.cbSize        = sizeof( WNDCLASSEX );
    wcex.style         = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc   = (WNDPROC)MainWndProc;
    wcex.cbClsExtra    = 0;
    wcex.cbWndExtra    = 0;
    wcex.hInstance     = g_hInstance;
    wcex.hIcon         = NULL;
    wcex.hIconSm       = NULL;
    wcex.hCursor       = LoadCursor( NULL, IDC_ARROW );
    wcex.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1 );
    wcex.lpszMenuName  = NULL;
    wcex.lpszClassName = "DXRuby";

    if( !RegisterClassEx( &wcex ) )
    {
        rb_raise( eDXRubyError, "ウィンドウの初期化に失敗しました - RegusterClassEx" );
    }

    /* メイン・ウインドウ作成(ウインドウ・モード用) */
    rect.top     = 0;
    rect.left    = 0;
    rect.right   = g_WindowInfo.width;
    rect.bottom  = g_WindowInfo.height;

    AdjustWindowRect( &rect, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE );

    rect.right   = rect.right - rect.left;
    rect.bottom  = rect.bottom - rect.top;
    rect.left    = 0;
    rect.top     = 0;

    g_hWind = CreateWindow( TEXT("DXRuby"), TEXT("DXRuby Application"),
                           WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
                           CW_USEDEFAULT, CW_USEDEFAULT,
                           rect.right - rect.left, rect.bottom - rect.top,
                           NULL, NULL, g_hInstance, NULL );
    if( g_hWind == NULL )
    {
        rb_raise( eDXRubyError, "ウィンドウの初期化に失敗しました - CreateWindow" );
    }

    GetWindowRect( g_hWind, &rect );
    g_WindowInfo.x        = rect.left;
    g_WindowInfo.y        = rect.top;
}


/*--------------------------------------------------------------------
  （内部関数）DirectX Graphics初期化
 ---------------------------------------------------------------------*/
static void InitDXGraphics( void )
{
    D3DVIEWPORT9 vp;
    HRESULT hr;

    /* Direct3Dオブジェクトの作成 */
    g_pD3D = Direct3DCreate9( D3D_SDK_VERSION );

    if( g_pD3D == NULL )
    {
        rb_raise( eDXRubyError, "DirectX Graphicsの初期化に失敗しました - Direct3DCreate9" );
    }

    /* D3DDeviceオブジェクトの作成(ウインドウ・モード) */
    ZeroMemory( &g_D3DPP, sizeof( g_D3DPP ) );

    g_D3DPP.BackBufferWidth            = 0;
    g_D3DPP.BackBufferHeight           = 0;
    g_D3DPP.BackBufferFormat           = D3DFMT_UNKNOWN;
    g_D3DPP.BackBufferCount            = 1;
    g_D3DPP.MultiSampleType            = D3DMULTISAMPLE_NONE;
    g_D3DPP.MultiSampleQuality         = 0;
    g_D3DPP.SwapEffect                 = D3DSWAPEFFECT_DISCARD;
    g_D3DPP.hDeviceWindow              = g_hWind;
    g_D3DPP.Windowed                   = TRUE;
    g_D3DPP.EnableAutoDepthStencil     = FALSE;
    g_D3DPP.AutoDepthStencilFormat     = D3DFMT_UNKNOWN;
    g_D3DPP.Flags                      = 0;
    g_D3DPP.FullScreen_RefreshRateInHz = 0;
    g_D3DPP.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;

    hr = g_pD3D->lpVtbl->CreateDevice( g_pD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWind,
                                       D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_D3DPP, &g_pD3DDevice );

    if( FAILED( hr ) )
    {
        hr = g_pD3D->lpVtbl->CreateDevice( g_pD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWind,
                                           D3DCREATE_SOFTWARE_VERTEXPROCESSING, &g_D3DPP, &g_pD3DDevice );

        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "DirectX Graphicsの初期化に失敗しました - CreateDevice" );
        }
    }

    /* ビューポートの設定 */
    vp.X       = 0;
    vp.Y       = 0;
    vp.Width   = g_D3DPP.BackBufferWidth;
    vp.Height  = g_D3DPP.BackBufferHeight;
    vp.MinZ    = 0.0f;
    vp.MaxZ    = 1.0f;

    hr = g_pD3DDevice->lpVtbl->SetViewport( g_pD3DDevice, &vp );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectX Graphicsの初期化に失敗しました - SetViewport" );
    }

    g_pD3DDevice->lpVtbl->Clear( g_pD3DDevice, 0, NULL, D3DCLEAR_TARGET,
                                 D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );

    /* スプライトオブジェクト作成 */
    hr = D3DXCreateSprite( g_pD3DDevice, &g_pD3DXSprite );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectX Graphicsの初期化に失敗しました - CreateSprite" );
    }
}


/*--------------------------------------------------------------------
  （内部関数）フレーム調整初期化
 ---------------------------------------------------------------------*/
static void InitSync( void )
{
    timeBeginPeriod( 1 );

    /* パフォーマンスカウンタの秒間カウント値取得 */
    if( QueryPerformanceFrequency( (LARGE_INTEGER *)&g_OneSecondCount ) )
    {
        /* パフォーマンスカウンタがある場合 */
        g_isPerformanceCounter = 1;
        QueryPerformanceCounter( (LARGE_INTEGER *)&g_OldTime );
    }
    else
    {
        /* パフォーマンスカウンタが無い場合 */
        g_isPerformanceCounter = 0;
        g_OneSecondCount = 1000;
        g_OldTime = timeGetTime();
    }
}


/* デバイス列挙関数 */
BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, void* pContext )
{
    HRESULT hr;

    /* 列挙されたジョイスティックへのインターフェイスを取得する */
    hr = g_pDInput->lpVtbl->CreateDevice( g_pDInput,
                            &pdidInstance->guidInstance,
                            &g_pDIDJoyPad[g_JoystickCount],
                            NULL );
    if( SUCCEEDED( hr ) )
    {
        return (++g_JoystickCount == PADMAX)?(DIENUM_STOP):(DIENUM_CONTINUE);
    }

    return DIENUM_STOP;
}

/* オブジェクト（軸）列挙関数 */
BOOL CALLBACK EnumAxisCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, void *pContext )
{
    HRESULT hr;
    DIPROPRANGE diprg = { sizeof(DIPROPRANGE), sizeof(DIPROPHEADER) };
    struct DXRubyPadInfo *pad;

    pad = (struct DXRubyPadInfo *)pContext;

    diprg.diph.dwHow = DIPH_BYID; 
    diprg.diph.dwObj = pdidoi->dwType;

    /*========================================================*/
    /* 取得 */
    hr =  pad->pDIDJoyPad->lpVtbl->GetProperty( pad->pDIDJoyPad, DIPROP_RANGE, &diprg.diph );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - GetProperty" );
    }

    /* x軸 */
    if( memcmp( &pdidoi->guidType, &GUID_XAxis ,sizeof(GUID)) == 0 )
    {
        int xcenter;
        xcenter = (diprg.lMin + diprg.lMax) / 2;
        pad->left  = (xcenter - diprg.lMin) / 2;
        pad->right = (diprg.lMax - xcenter) / 2 + xcenter;
    }

    /* y軸 */
    if( memcmp( &pdidoi->guidType, &GUID_YAxis ,sizeof(GUID)) == 0 )
    {
        int ycenter;
        ycenter = (diprg.lMin + diprg.lMax) / 2;
        pad->up   = (ycenter - diprg.lMin) / 2;
        pad->down = (diprg.lMax - ycenter) / 2 + ycenter;
    }

    return DIENUM_CONTINUE;
}

/*--------------------------------------------------------------------
  （内部関数）DirectInput初期化
 ---------------------------------------------------------------------*/
static void InitDirectInput( void )
{
    HRESULT hr;
    int i;

/* DirectInput初期化 */

    /* DirectInputオブジェクトの作成 */
    hr = DirectInput8Create( g_hInstance, DIRECTINPUT_VERSION,
                             &IID_IDirectInput8, (void **)&g_pDInput, NULL );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - DirectInput8Create" );
    }

/* キーボード */

    /* デバイス・オブジェクトを作成（キーボード） */
    hr = g_pDInput->lpVtbl->CreateDevice( g_pDInput, &GUID_SysKeyboard, &g_pDIDKeyBoard, NULL );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - CreateDevice" );
    }

    /* データ形式を設定（キーボードですよ） */
    hr = g_pDIDKeyBoard->lpVtbl->SetDataFormat( g_pDIDKeyBoard, &c_dfDIKeyboard );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - SetDataFormat" );
    }

    /* キーボードのモードを設定（フォアグラウンド＆非排他モード） */
    hr = g_pDIDKeyBoard->lpVtbl->SetCooperativeLevel( g_pDIDKeyBoard, g_hWind, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - SetCooperativeLevel" );
    }

    /* 入力制御開始 */
    g_pDIDKeyBoard->lpVtbl->Acquire( g_pDIDKeyBoard );


/* ゲームパッド */

    for( i = 0; i < PADMAX; i++)
    {
        g_pDIDJoyPad[i] = NULL;
    }
    g_JoystickCount = 0;

    /* ゲームデバイス列挙 */
    hr = g_pDInput->lpVtbl->EnumDevices( g_pDInput, DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, NULL, DIEDFL_ATTACHEDONLY );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - EnumDevices" );
    }

    /* ゲームデバイス初期化 */
    for ( i = 0; i < g_JoystickCount; i++ )
    {
        /* データ形式を設定 */
        hr = g_pDIDJoyPad[i]->lpVtbl->SetDataFormat( g_pDIDJoyPad[i], &c_dfDIJoystick );

        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - SetDataFormat" );
        }

        /* キーボードのモードを設定（フォアグラウンド＆非排他モード） */
        hr = g_pDIDJoyPad[i]->lpVtbl->SetCooperativeLevel( g_pDIDJoyPad[i], g_hWind, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND );

        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - SetCooperativeLevel" );
        }

        /* オブジェクト列挙 */
        g_PadInfo[i].pDIDJoyPad = g_pDIDJoyPad[i];
        hr = g_pDIDJoyPad[i]->lpVtbl->EnumObjects( g_pDIDJoyPad[i], EnumAxisCallback, &g_PadInfo[i], DIDFT_AXIS );

        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - EnumObjects" );
        }

        /* 入力制御開始 */
        g_pDIDJoyPad[i]->lpVtbl->Acquire( g_pDIDJoyPad[i] );
    }

}




static void AllocSpriteList( void )
{
	int i;

	if( g_SpriteCount >= g_SpriteAllocateCount )
	{
		g_SpriteAllocateCount = g_SpriteAllocateCount * 3 / 2; /* 1.5倍にする */
		g_SpriteList = realloc(g_SpriteList, g_SpriteAllocateCount * sizeof(struct DXRubySpriteList) );
		g_SpriteStruct = realloc(g_SpriteStruct, g_SpriteAllocateCount * sizeof(struct DXRubySprite) );
		for( i = 0; i < g_SpriteCount; i++)
		{
			g_SpriteList[i].sprite = &g_SpriteStruct[i];
		}
	}
}


/* マージソート */
void merge( struct DXRubySpriteList *list, struct DXRubySpriteList *temp, int left, int mid, int right )
{
    int left_end, num_elements, tmp_pos;

    left_end = mid - 1;
    tmp_pos = left;
    num_elements = right - left + 1;

    while ((left <= left_end) && (mid <= right))
    {
        if (list[left].z <= list[mid].z)
            temp[tmp_pos++] = list[left++];
        else
            temp[tmp_pos++] = list[mid++];
    }

    while (left <= left_end)
    {
        temp[tmp_pos++] = list[left++];
    }
    while (mid <= right)
    {
        temp[tmp_pos++] = list[mid++];
    }

    while (num_elements--)
    {
        list[right] = temp[right--];
    }
}
void m_sort( struct DXRubySpriteList *list, struct DXRubySpriteList *temp, int left, int right )
{
    int mid;

    if( right > left )
    {
        mid = (right + left) / 2;
        m_sort( list, temp, left, mid );
        m_sort( list, temp, mid+1, right );

        merge( list, temp, left, mid+1, right );
    }
}
void SortSpriteList( void )
{
    struct DXRubySpriteList *temp;
    int i;

    for( i = 0; i < g_SpriteCount; i++ )
    {
        if( g_SpriteList[i].z != 0.0f )
        {
            temp = malloc( sizeof(struct DXRubySpriteList)* g_SpriteCount );
            m_sort( g_SpriteList, temp, 0, g_SpriteCount - 1 );
            free(temp);
            break;
        }
    }
}


/***********************
 * 外部インターフェース
 ***********************/

/*--------------------------------------------------------------------
   描画設定（通常描画）
 ---------------------------------------------------------------------*/
void DXRuby_draw( int x, int y, VALUE vimage, float z )
{
    struct DXRubyImage *image;

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( vimage ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    image = (struct DXRubyImage *)DATA_PTR( vimage );
    if( RDATA( vimage )->dfree != (RUBY_DATA_FUNC)release_Image )
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = x;
    g_SpriteStruct[g_SpriteCount].y = y;
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
    g_SpriteStruct[g_SpriteCount].centerx = image->width / 2;
    g_SpriteStruct[g_SpriteCount].centery = image->height / 2;
    g_SpriteStruct[g_SpriteCount].blendflag = 0;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = vimage;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = z;
    g_SpriteCount++;
}


/*--------------------------------------------------------------------
   描画設定（拡大縮小描画） フラグは0にするとcenter無視
 ---------------------------------------------------------------------*/
void DXRuby_drawScale( int x, int y, VALUE vimage, float scalex, float scaley, float centerx, float centery, float z, int flag )
{
    struct DXRubyImage *image;

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( vimage ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    image = (struct DXRubyImage *)DATA_PTR( vimage );
    if( RDATA( vimage )->dfree != (RUBY_DATA_FUNC)release_Image )
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = x;
    g_SpriteStruct[g_SpriteCount].y = y;
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = scalex;
    g_SpriteStruct[g_SpriteCount].scaley = scaley;
    g_SpriteStruct[g_SpriteCount].centerx = flag ? centerx :image->width / 2;
    g_SpriteStruct[g_SpriteCount].centery = flag ? centery : image->height / 2;
    g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
    g_SpriteStruct[g_SpriteCount].blendflag = 0;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = vimage;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = z;
    g_SpriteCount++;
}


/*--------------------------------------------------------------------
   描画設定（回転描画） フラグは0にするとcenter無視
 ---------------------------------------------------------------------*/
void DXRuby_drawRot( int x, int y, VALUE vimage, float angle, int centerx, int centery, int z, int flag)
{
    struct DXRubyImage *image;

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( vimage ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    image = (struct DXRubyImage *)DATA_PTR( vimage );
    if( RDATA( vimage )->dfree != (RUBY_DATA_FUNC)release_Image )
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = x;
    g_SpriteStruct[g_SpriteCount].y = y;
    g_SpriteStruct[g_SpriteCount].angle  = angle;
    g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
    g_SpriteStruct[g_SpriteCount].blendflag = 0;
    g_SpriteStruct[g_SpriteCount].centerx = flag ? centerx : image->width / 2;
    g_SpriteStruct[g_SpriteCount].centery = flag ? centery : image->height / 2;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = vimage;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = z;
    g_SpriteCount++;
}


/*--------------------------------------------------------------------
   描画設定（半透明描画）
 ---------------------------------------------------------------------*/
void DXRuby_drawAlpha( int x, int y, VALUE vimage, int alpha, int z )
{
    struct DXRubyImage *image;

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( vimage ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    image = (struct DXRubyImage *)DATA_PTR( vimage );
    if( RDATA( vimage )->dfree != (RUBY_DATA_FUNC)release_Image )
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = x;
    g_SpriteStruct[g_SpriteCount].y = y;
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = alpha;
    g_SpriteStruct[g_SpriteCount].blendflag = 0;
    g_SpriteStruct[g_SpriteCount].centerx = image->width / 2;
    g_SpriteStruct[g_SpriteCount].centery = image->height / 2;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = vimage;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = z;
    g_SpriteCount++;
}


/*--------------------------------------------------------------------
   描画設定（加算合成描画）
 ---------------------------------------------------------------------*/
void DXRuby_drawAdd( int x, int y, VALUE vimage, int z )
{
    struct DXRubyImage *image;

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( vimage ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    image = (struct DXRubyImage *)DATA_PTR( vimage );
    if( RDATA( vimage )->dfree != (RUBY_DATA_FUNC)release_Image )
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = x;
    g_SpriteStruct[g_SpriteCount].y = y;
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
    g_SpriteStruct[g_SpriteCount].blendflag = 1;
    g_SpriteStruct[g_SpriteCount].centerx = image->width / 2;
    g_SpriteStruct[g_SpriteCount].centery = image->height / 2;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = vimage;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = z;
    g_SpriteCount++;
}


/*--------------------------------------------------------------------
   描画設定（減算合成描画）
 ---------------------------------------------------------------------*/
void DXRuby_drawSub( int x, int y, VALUE vimage, int z )
{
    struct DXRubyImage *image;

    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( vimage ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    image = (struct DXRubyImage *)DATA_PTR( vimage );
    if( RDATA( vimage )->dfree != (RUBY_DATA_FUNC)release_Image )
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = x;
    g_SpriteStruct[g_SpriteCount].y = y;
    g_SpriteStruct[g_SpriteCount].angle  = 0.0f;
    g_SpriteStruct[g_SpriteCount].scalex = 1.0f;
    g_SpriteStruct[g_SpriteCount].scaley = 1.0f;
    g_SpriteStruct[g_SpriteCount].alpha  = 0xff;
    g_SpriteStruct[g_SpriteCount].blendflag = 3;
    g_SpriteStruct[g_SpriteCount].centerx = image->width / 2;
    g_SpriteStruct[g_SpriteCount].centery = image->height / 2;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = vimage;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = z;
    g_SpriteCount++;
}


/*--------------------------------------------------------------------
   描画設定（フルオプション）
 ---------------------------------------------------------------------*/
void DXRuby_drawEx( int x, int y, VALUE vimage, float angle, float scalex, float scaley, float centerx, float centery, int alpha, int blend, int z, int flag )
{
    struct DXRubyImage *image;
    AllocSpriteList();

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( vimage ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    image = (struct DXRubyImage *)DATA_PTR( vimage );
    if( RDATA( vimage )->dfree != (RUBY_DATA_FUNC)release_Image )
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    }

    /* DXRubySpriteオブジェクト設定 */
    g_SpriteStruct[g_SpriteCount].image = image;
    g_SpriteStruct[g_SpriteCount].x = x;
    g_SpriteStruct[g_SpriteCount].y = y;
    g_SpriteStruct[g_SpriteCount].angle   = angle;
    g_SpriteStruct[g_SpriteCount].scalex  = scalex;
    g_SpriteStruct[g_SpriteCount].scaley  = scaley;
    g_SpriteStruct[g_SpriteCount].centerx = flag ? centerx : image->width / 2;
    g_SpriteStruct[g_SpriteCount].centery = flag ? centery : image->height / 2;
    g_SpriteStruct[g_SpriteCount].alpha   = alpha;
    g_SpriteStruct[g_SpriteCount].blendflag = blend;
    g_SpriteStruct[g_SpriteCount].fontflag = 0;
    g_SpriteStruct[g_SpriteCount].vpflag = 0;
    g_SpriteStruct[g_SpriteCount].value = vimage;

    /* リストデータに追加 */
    g_SpriteList[g_SpriteCount].sprite = &g_SpriteStruct[g_SpriteCount];
    g_SpriteList[g_SpriteCount].z = z;
    g_SpriteCount++;
}


/*--------------------------------------------------------------------
    イメージのロック
 ---------------------------------------------------------------------*/
void *DXRuby_lock( VALUE vimage, char **address, int *pitch, int *width, int *height )
{
    struct DXRubyImage *image;
    D3DLOCKED_RECT texrect;
    RECT rect;

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( vimage ) != T_DATA ) rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    image = (struct DXRubyImage *)DATA_PTR( vimage );
    if( RDATA( vimage )->dfree != (RUBY_DATA_FUNC)release_Image )
    {
      rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( vimage ));
    }

    rect.left = (long)image->x;
    rect.top = (long)image->y;
    rect.right = (long)(image->x + image->width);
    rect.bottom = (long)(image->y + image->height);
    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &texrect, &rect, 0 );

    //    *((int*)((char *)texrect.pBits + x * 4 + y * texrect.Pitch)) = col;

    *address = (char *)texrect.pBits;
    *pitch = texrect.Pitch;
    *width = (int)image->width;
    *height = (int)image->height;

    return image->texture->pD3DTexture;
}


/*--------------------------------------------------------------------
    イメージのアンロック
 ---------------------------------------------------------------------*/
void DXRuby_unlock( void *texture )
{
    ((LPDIRECT3DTEXTURE9)texture)->lpVtbl->UnlockRect( (LPDIRECT3DTEXTURE9)texture, 0 );
}

