/*
###################################
#
# dxruby.c Ver. 1.1.20dev
#
###################################
*/
#define DXRUBY_VERSION "1.1.20dev"

#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 <d3d9types.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 ) { if(FAILED(x->lpVtbl->Release( x )))rb_raise(eDXRubyError,"release error"); x = NULL; }
#define DXRUBY_CHECK_TYPE( a, b ) \
        {if( TYPE( b ) != T_DATA || RDATA( b )->dfree != (RUBY_DATA_FUNC)a##_release )\
        rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::"#a")", rb_obj_classname( b ));}
#define DXRUBY_CHECK_IMAGE( a ) \
        {if( TYPE( a ) != T_DATA || (RDATA( a )->dfree != (RUBY_DATA_FUNC)Image_release && RDATA( a )->dfree != (RUBY_DATA_FUNC)RenderTarget_release) )\
        rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image or DXRuby::RenderTarget)", rb_obj_classname( a ));}
#define DXRUBY_GET_STRUCT( c, v ) ((struct DXRuby##c *)DATA_PTR( v ))
#define DXRUBY_IS_TRUE( x ) (x != Qnil && x != Qfalse)
#define DXRUBY_IS_FALSE( x ) (x == Qnil || x == Qfalse)
#define DXRUBY_CHECK_DISPOSE( a, b ) {if( a->b == NULL ) { rb_raise( eDXRubyError, "disposed object" );}}

#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 cRenderTarget; /* レンダーターゲットクラス */
static VALUE cShader;       /* シェーダクラス       */
static VALUE cMesh;         /* メッシュクラス       */
static VALUE cPolygon;      /* ポリゴンクラス       */
static VALUE cMatrix;       /* 行列クラス           */
static VALUE cVector;       /* ベクトルクラス       */
static VALUE cSound;        /* サウンドクラス       */
static VALUE cFont;         /* フォントクラス       */
static VALUE cSoundEffect;  /* 生成効果音クラス     */
static VALUE eDXRubyError;  /* 例外                 */

/* グローバル変数たち */
static HINSTANCE             g_hInstance   = NULL; /* アプリケーションインスタンス   */
static HANDLE                g_hWnd       = 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 __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;      /* コマ落ち制御フラグ */
    HANDLE hIcon;       /* ウィンドウアイコンハンドル */
    int userloop;       /* Window.loopされてたら1 */
    int requestclose;   /* ウィンドウが閉じられたら1 */
    VALUE render_target; /* スクリーンレンダーターゲット */
    int active;         /* ゲーム調整用 */
    VALUE event_hash;   /* イベント処理用ハッシュ */
} g_WindowInfo;

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

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

/* Sprite系基底構造体 */
struct DXRubySprite {
    void (*func)(void*);
    VALUE value;
    unsigned char blendflag; /* 半透明(000)、加算合成1(100)、加算合成2(101)、減算合成1(110)、減算合成2(111)のフラグ */
    char reserve1;
    char reserve2;
    char reserve3;
};

/* メッシュ */
struct DXRubyMesh {
    LPD3DXMESH    pD3DXMesh;
    D3DMATERIAL9 *pD3DMaterials;
    LPDIRECT3DTEXTURE9 *ppD3DTextures;
    int materials;
    struct DXRubyTexture *texture;
};

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

/* ポリゴン */
struct DXRubyPolygon {
    TLVERTX p[3];
    struct DXRubyTexture *texture;
};

/* 行列 */
struct DXRubyMatrix {
    int x;
    int y;
    union {
        struct {
            float m11;float m12; float m13; float m14;
            float m21;float m22; float m23; float m24;
            float m31;float m32; float m33; float m34;
            float m41;float m42; float m43; float m44;
        };
        float m[4][4];
    };
};

/* ベクトル */
struct DXRubyVector {
    int x;
    union {
        struct {
            float v1;float v2; float v3; float v4;
        };
        float v[4];
    };
};

/* ShaderHandle */
struct DXRubyShaderHandle {
    ID id;              /* 変数名 */
    D3DXHANDLE D3DXHandle;  /* ハンドル */
    VALUE obj;          /* 設定するオブジェクト */
};

/* Shader */
struct DXRubyShader {
    LPD3DXEFFECT pD3DXEffect;
    int pass_number;
    int pass_count;
    D3DXHANDLE technique;
    int handle_count;
    struct DXRubyShaderHandle *handle;
};

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

/* スプライト配列 */
static struct DXRubySpriteList {
    float z;                        /* スプライトのZ座標 */
    struct DXRubySprite *sprite;    /* スプライト構造体へのポインタ */
};


/* RenderTargetオブジェクトの中身 */
struct DXRubyRenderTarget {
    struct DXRubyTexture *texture;
    float x;     /* x始点位置      */
    float y;     /* y始点位置      */
    float width; /* イメージの幅   */
    float height;/* イメージの高さ */
    IDirect3DSurface9 *surface;
    IDirect3DSurface9 *zbuffer;

    int SpriteCount;                  /* スプライトの登録数 */
    int SpriteAllocateCount;          /* スプライト登録のメモリ確保数 */
    int SpriteSize;                   /* スプライトデータの使用済みサイズ */
    int SpriteAllocateSize;           /* スプライトデータのメモリ確保サイズ */
    char *SpriteStruct;
    int MeshCount;                    /* メッシュの登録数 */
    int MeshAllocateCount;            /* メッシュ登録のメモリ確保数 */
    int MeshSize;                     /* メッシュデータの使用済みサイズ */
    int MeshAllocateSize;             /* メッシュデータのメモリ確保サイズ */
    char *MeshStruct;

    struct DXRubySpriteList *SpriteList;
    struct DXRubySpriteList *MeshList;

    /* RenderTarget切り替え用 */
//    IDirect3DSurface9 *pD3DSurfScreen;
//    IDirect3DSurface9 *pD3DSurfZ;
 
    /* 3D設定 */
    D3DMATRIX matrix_view; /* 3D用のビュー変換行列 */
    int useview;           /* 3D用のビュー変換行列が設定されたら1 */
    D3DMATRIX matrix_proj; /* 3D用の射影変換行列 */
    int useproj;           /* 3D用の射影変換行列が設定されたら1 */
    float zn, zf;       /* 擬似3D用のクリッピング面 */
    int fog_enable;
    int fog_mode;
    int fog_color;
    int fog_range;
    float fog_density;
    float fog_start;
    float fog_end;

    int minfilter;      /* 縮小フィルタ */
    int magfilter;      /* 拡大フィルタ */

    int a;              /* 背景クリア色 α成分 */
    int r;              /* 背景クリア色 赤成分 */
    int g;              /* 背景クリア色 緑成分 */
    int b;              /* 背景クリア色 青成分 */
};

/* 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 DXRubyLostList {
    void **pointer;
    int allocate_size;
    int count;
} g_RenderTargetList, g_ShaderList;

/* 色 */
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 VALUE symbol_sub2    = Qundef;
static VALUE symbol_shader  = Qundef;
static VALUE symbol_italic  = Qundef;
static VALUE symbol_weight  = Qundef;
static VALUE symbol_dividex = Qundef;
static VALUE symbol_dividey = 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 VALUE Image_allocate( VALUE klass );
static VALUE Image_initialize( int argc, VALUE *argv, VALUE obj );
static VALUE Matrix_allocate( VALUE klass );

static void Image_release( struct DXRubyImage* image );
static void RenderTarget_release( struct DXRubyRenderTarget* image );
static void Shader_release( struct DXRubyShader *shader );
static void Font_release( struct DXRubyFont* font );
static void Mesh_release( struct DXRubyMesh* Mesh );
static void Matrix_release( struct DXRubyMatrix* mat );
static void Vector_release( struct DXRubyVector* vec );

static void AddRenderTargetList( struct DXRubyRenderTarget *rt );
static void AddShaderList( struct DXRubyShader *shader );
static void DeleteRenderTargetList( struct DXRubyRenderTarget *rt );
static void DeleteShaderList( struct DXRubyShader *rt );
static void *RenderTarget_AllocMeshList( struct DXRubyRenderTarget *rt, int size );
static void *RenderTarget_AllocSpriteList( struct DXRubyRenderTarget *rt, int size );
static void RenderTarget_SortMeshList( struct DXRubyRenderTarget *rt );
static void RenderTarget_SortSpriteList( struct DXRubyRenderTarget *rt );

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

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

    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_hWnd, GWL_STYLE, WS_POPUP );
        SetWindowPos( g_hWnd, HWND_TOP, 0, 0, g_WindowInfo.width, g_WindowInfo.height, SWP_NOZORDER);
    }
    else
    {   /* ウィンドウモード */
        SetWindowLong( g_hWnd, 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_hWnd, HWND_TOP, 0, 0, rect.right, rect.bottom, SWP_NOZORDER | SWP_NOMOVE);
//        }
//        else
//        {   /* 位置指定の場合 */
//            SetWindowPos( g_hWnd, HWND_TOP, g_WindowInfo.x, g_WindowInfo.y, rect.right, rect.bottom, SWP_NOZORDER );
//        }
    }

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

    /* ウィンドウ表示 */
    ShowWindow( g_hWnd, SW_SHOWNORMAL );
//    InvalidateRect( g_hWnd, NULL, TRUE );
    InvalidateRect( NULL, NULL, TRUE );
    UpdateWindow( g_hWnd );

    {
        struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );

        /* シーンのクリア */
        g_pD3DDevice->lpVtbl->Clear( g_pD3DDevice, 0, NULL, D3DCLEAR_TARGET,
                                     D3DCOLOR_XRGB( rt->r, rt->g, rt->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;
    int i;

    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 );
    }

    /* レンダーターゲットの解放 */
    {
        struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
        RELEASE( rt->surface );
        RELEASE( rt->zbuffer );
    }

    for( i = 0; i < g_RenderTargetList.count; i++ )
    {
        struct DXRubyRenderTarget *rt = (struct DXRubyRenderTarget *)g_RenderTargetList.pointer[i];
        RELEASE( rt->surface );
        RELEASE( rt->zbuffer );
        RELEASE( rt->texture->pD3DTexture );
    }

    /* シェーダのロスト */
    for( i = 0; i < g_ShaderList.count; i++ )
    {
        struct DXRubyShader *shader = (struct DXRubyShader *)g_ShaderList.pointer[i];
        shader->pD3DXEffect->lpVtbl->OnLostDevice( shader->pD3DXEffect );
    }

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

    /* レンダーターゲットの復元 */
    {
        struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
        g_pD3DDevice->lpVtbl->GetRenderTarget( g_pD3DDevice, 0, &rt->surface );
        g_pD3DDevice->lpVtbl->GetDepthStencilSurface( g_pD3DDevice, &rt->zbuffer );
    }

    for( i = 0; i < g_RenderTargetList.count; i++ )
    {
        struct DXRubyRenderTarget *rt = (struct DXRubyRenderTarget *)g_RenderTargetList.pointer[i];

        /* テクスチャオブジェクトを作成する */
        hr = D3DXCreateTexture( g_pD3DDevice, rt->texture->width, rt->texture->height,
                                1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
                                &rt->texture->pD3DTexture);
        if( FAILED( hr ) ) rb_raise( eDXRubyError, "RenderTargetの復元に失敗しました" );

        hr = rt->texture->pD3DTexture->lpVtbl->GetSurfaceLevel( rt->texture->pD3DTexture, 0, &rt->surface );
        if( FAILED( hr ) ) rb_raise( eDXRubyError, "RenderTargetの復元に失敗しました" );

        /* Zバッファサーフェイスを作成する */
        hr = g_pD3DDevice->lpVtbl->CreateDepthStencilSurface( g_pD3DDevice, 
                                                              rt->texture->width, rt->texture->height, 
                                                              D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, FALSE, &rt->zbuffer, NULL);
        if( FAILED( hr ) ) rb_raise( eDXRubyError, "RenderTargetの復元に失敗しました" );
    }

    /* シェーダの復帰 */
    for( i = 0; i < g_ShaderList.count; i++ )
    {
        struct DXRubyShader *shader = (struct DXRubyShader *)g_ShaderList.pointer[i];
        shader->pD3DXEffect->lpVtbl->OnResetDevice( shader->pD3DXEffect );
    }

    /* スプライト復帰 */
    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" );
    }

    g_WindowInfo.userloop = 0;
    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( mWindow );
        }
    }

    return Qtrue;
}


/*--------------------------------------------------------------------
   イベント定義
 ---------------------------------------------------------------------*/
static VALUE Window_set_event( VALUE klass, VALUE message )
{
    if (!rb_block_given_p())
    {
        rb_hash_delete( g_WindowInfo.event_hash, message );
        return Qnil;
    }

    rb_hash_aset( g_WindowInfo.event_hash, message, rb_block_proc() );

    return message;
}


/*--------------------------------------------------------------------
   イベント取得
 ---------------------------------------------------------------------*/
static VALUE Window_get_event( VALUE klass, VALUE message )
{
    return rb_hash_lookup( g_WindowInfo.event_hash, message );
}


/*--------------------------------------------------------------------
   （内部関数）ウィンドウプロシージャ
 ---------------------------------------------------------------------*/
LRESULT CALLBACK MainWndProc( HWND hWnd, UINT msg, UINT wParam, LONG lParam )
{
    RECT rect;
    VALUE temp;

    switch( msg )
    {
    /* ウィンドウアクティブ／非アクティブ化 */
    case WM_ACTIVATE:
        g_WindowInfo.active = (LOWORD(wParam) != 0);
        break;
    case WM_CLOSE:
        /* 自前ループの場合は無条件でキャセルする */
        if( g_WindowInfo.userloop == 1 )
        {
            g_WindowInfo.requestclose = 1;
            return 0;
        }
        break;
    /* ウィンドウ破棄 */
    case WM_DESTROY:
        /* ウインドウを閉じる */
        PostQuitMessage( 0 );
        g_hWnd = 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;
*/
    }

    /* イベント処理 */
    if( (temp = rb_hash_lookup( g_WindowInfo.event_hash, INT2NUM( msg ) )) != Qnil )
    {
        rb_funcall( temp , rb_intern("yield"), 2, INT2NUM( wParam ), INT2NUM( lParam ) );
    }

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


/*--------------------------------------------------------------------
   画面クリア色指定
 ---------------------------------------------------------------------*/
static VALUE Window_getbgcolor( VALUE obj )
{
    struct DXRubyRenderTarget *rt;

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
        return rb_ary_new3( 3, INT2FIX( rt->r ), INT2FIX( rt->g ), INT2FIX( rt->b ) );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
        return rb_ary_new3( 4, INT2FIX( rt->a ), INT2FIX( rt->r ), INT2FIX( rt->g ), INT2FIX( rt->b ) );
    }
}


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

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

    Check_Type(array, T_ARRAY);

    if( RARRAY_LEN(array) == 4 )
    {
        rt->a = NUM2INT( rb_ary_entry(array, 0) );
        rt->r = NUM2INT( rb_ary_entry(array, 1) );
        rt->g = NUM2INT( rb_ary_entry(array, 2) );
        rt->b = NUM2INT( rb_ary_entry(array, 3) );
    }
    else
    {
        rt->a = 255;
        rt->r = NUM2INT( rb_ary_entry(array, 0) );
        rt->g = NUM2INT( rb_ary_entry(array, 1) );
        rt->b = NUM2INT( rb_ary_entry(array, 2) );
    }

    return Qnil;
}

struct DXRubySprite_draw {
    void (*func)(void*);
    VALUE value;
    unsigned char blendflag; /* 半透明(000)、加算合成1(100)、加算合成2(101)、減算合成1(110)、減算合成2(111)のフラグ */
    unsigned char alpha;     /* アルファ（透明）値 */
    char reserve1;           /* 予約3 */
    char reserve2;           /* 予約4 */
    int x;
    int y;
    float z;
};

static void Window_draw_func( struct DXRubySprite_draw *sprite )
{
    TLVERTX VertexDataTbl[6];
    struct DXRubyImage *image = DXRUBY_GET_STRUCT( Image, sprite->value );
    float basex = sprite->x - 0.5f;
    float basey = sprite->y - 0.5f;
    float width = image->width;
    float height = image->height;
    float tu1;
    float tu2;
    float tv1;
    float tv2;

    DXRUBY_CHECK_DISPOSE( image, texture );
    tu1 = image->x / image->texture->width;
    tu2 = (image->x + width) / image->texture->width;
    tv1 = image->y / image->texture->height;
    tv2 = (image->y + height) / image->texture->height;

    /* 頂点１ */
    VertexDataTbl[0].x = basex;
    VertexDataTbl[0].y = basey;
    /* 頂点２ */
    VertexDataTbl[1].x = VertexDataTbl[3].x = basex + width;
    VertexDataTbl[1].y = VertexDataTbl[3].y = basey;
    /* 頂点３ */
    VertexDataTbl[4].x = basex + width;
    VertexDataTbl[4].y = basey + height;
    /* 頂点４ */
    VertexDataTbl[2].x = VertexDataTbl[5].x = basex;
    VertexDataTbl[2].y = VertexDataTbl[5].y = basey + height;
    /* 頂点色 */
    VertexDataTbl[0].color = VertexDataTbl[1].color =
    VertexDataTbl[2].color = VertexDataTbl[3].color =
    VertexDataTbl[4].color = VertexDataTbl[5].color = D3DCOLOR_ARGB(sprite->alpha,255,255,255);
    /* Ｚ座標 */
    VertexDataTbl[0].z  = VertexDataTbl[1].z =
    VertexDataTbl[2].z  = VertexDataTbl[3].z =
    VertexDataTbl[4].z  = VertexDataTbl[5].z = sprite->z;
    /* テクスチャ座標 */
    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));
}

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

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

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

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_IMAGE( argv[2] );
    image = DXRUBY_GET_STRUCT( Image, argv[2] );
    DXRUBY_CHECK_DISPOSE( image, texture );

    sprite = (struct DXRubySprite_draw *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_draw ) );

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_draw_func;
    sprite->x = NUM2INT( argv[0] );
    sprite->y = NUM2INT( argv[1] );
    sprite->value = argv[2];
    sprite->alpha = 0xff;
    sprite->blendflag = 0;

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    z = argc < 4 || argv[3] == Qnil ? 0.0f : NUM2DBL( argv[3] );
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return obj;
}

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

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

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

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_IMAGE( argv[2] );
    image = DXRUBY_GET_STRUCT( Image, argv[2] );
    DXRUBY_CHECK_DISPOSE( image, texture );

    sprite = (struct DXRubySprite_draw *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_draw ) );

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_draw_func;
    sprite->x = NUM2INT( argv[0] );
    sprite->y = NUM2INT( argv[1] );
    sprite->value = argv[2];
    sprite->alpha = NUM2INT( argv[3] );
    sprite->blendflag = 0;

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    z = argc < 5 || argv[4] == Qnil ? 0.0f : NUM2DBL( argv[4] );
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return obj;
}


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

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

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

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_IMAGE( argv[2] );
    image = DXRUBY_GET_STRUCT( Image, argv[2] );
    DXRUBY_CHECK_DISPOSE( image, texture );

    sprite = (struct DXRubySprite_draw *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_draw ) );

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_draw_func;
    sprite->x = NUM2INT( argv[0] );
    sprite->y = NUM2INT( argv[1] );
    sprite->value = argv[2];
    sprite->alpha = 0xff;
    sprite->blendflag = 4;

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    z = argc < 4 || argv[3] == Qnil ? 0.0f : NUM2DBL( argv[3] );
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return obj;
}


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

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

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

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_IMAGE( argv[2] );
    image = DXRUBY_GET_STRUCT( Image, argv[2] );
    DXRUBY_CHECK_DISPOSE( image, texture );

    sprite = (struct DXRubySprite_draw *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_draw ) );

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_draw_func;
    sprite->x = NUM2INT( argv[0] );
    sprite->y = NUM2INT( argv[1] );
    sprite->value = argv[2];
    sprite->alpha = 0xff;
    sprite->blendflag = 6;

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    z = argc < 4 || argv[3] == Qnil ? 0.0f : NUM2DBL( argv[3] );
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return obj;
}


struct DXRubySprite_drawShader {
    void (*func)(void*);
    VALUE value;
    unsigned char blendflag; /* 半透明(000)、加算合成1(100)、加算合成2(101)、減算合成1(110)、減算合成2(111)のフラグ */
    unsigned char alpha;     /* アルファ（透明）値 */
    char reserve1;           /* 予約3 */
    char reserve2;           /* 予約4 */
    int x;
    int y;
    float z;
};

static void Window_drawShader_func( struct DXRubySprite_drawShader *sprite )
{
    TLVERTX VertexDataTbl[6];
    struct DXRubyImage *image;
    struct DXRubyShader *shader;
    float basex = sprite->x;
    float basey = sprite->y;
    float width;
    float height;
    float tu1;
    float tu2;
    float tv1;
    float tv2;
    int i, j, k;

    image = DXRUBY_GET_STRUCT( Image, RARRAY_PTR( sprite->value )[0] );
    shader = DXRUBY_GET_STRUCT( Shader, RARRAY_PTR( sprite->value )[ RARRAY_LEN( sprite->value ) - 2 ] );
    DXRUBY_CHECK_DISPOSE( image, texture );
    DXRUBY_CHECK_DISPOSE( shader, pD3DXEffect );

    width = image->width;
    height = image->height;
    tu1 = image->x / image->texture->width;
    tu2 = (image->x + width) / image->texture->width;
    tv1 = image->y / image->texture->height;
    tv2 = (image->y + height) / image->texture->height;

    /* 頂点１ */
    VertexDataTbl[0].x = basex-0.5f;
    VertexDataTbl[0].y = basey-0.5f;
    /* 頂点２ */
    VertexDataTbl[1].x = VertexDataTbl[3].x = basex + width-0.5f;
    VertexDataTbl[1].y = VertexDataTbl[3].y = basey-0.5f;
    /* 頂点３ */
    VertexDataTbl[4].x = basex + width-0.5f;
    VertexDataTbl[4].y = basey + height-0.5f;
    /* 頂点４ */
    VertexDataTbl[2].x = VertexDataTbl[5].x = basex-0.5f;
    VertexDataTbl[2].y = VertexDataTbl[5].y = basey + height-0.5f;
    /* 頂点色 */
    VertexDataTbl[0].color = VertexDataTbl[1].color =
    VertexDataTbl[2].color = VertexDataTbl[3].color =
    VertexDataTbl[4].color = VertexDataTbl[5].color = D3DCOLOR_ARGB(sprite->alpha,255,255,255);
    /* Ｚ座標 */
    VertexDataTbl[0].z  = VertexDataTbl[1].z =
    VertexDataTbl[2].z  = VertexDataTbl[3].z =
    VertexDataTbl[4].z  = VertexDataTbl[5].z = sprite->z;
    /* テクスチャ座標 */
    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;

    for( i = 0; i < shader->handle_count; i++ )
    {
        VALUE data = RARRAY_PTR( RARRAY_PTR( sprite->value )[ RARRAY_LEN( sprite->value ) - 1 ] )[i];
        if( TYPE( data ) == T_FLOAT )
        {
            shader->pD3DXEffect->lpVtbl->SetFloat( shader->pD3DXEffect, shader->handle[i].D3DXHandle, NUM2DBL( data ) );
        }
        else if( TYPE( data ) == T_DATA )
        {
            if ( RDATA( data )->dfree == (RUBY_DATA_FUNC)Vector_release )
            {
                shader->pD3DXEffect->lpVtbl->SetVector( shader->pD3DXEffect, shader->handle[i].D3DXHandle, (D3DXVECTOR4*)&DXRUBY_GET_STRUCT( Vector, data )->v );
            }
            else if( RDATA( data )->dfree == (RUBY_DATA_FUNC)Matrix_release )
            {
                shader->pD3DXEffect->lpVtbl->SetMatrix( shader->pD3DXEffect, shader->handle[i].D3DXHandle, (D3DXMATRIX*)&DXRUBY_GET_STRUCT( Matrix, data )->m );
            }
            else
            {
                rb_raise( eDXRubyError, "Shaderのパラメータが異常です - %s", rb_obj_classname( data ) );
            }
        }
        else if( TYPE( data ) == T_ARRAY )
        {
            if( TYPE( RARRAY_PTR( data )[0] ) == T_FLOAT )
            {
                float *array = alloca( sizeof( float ) * RARRAY_LEN( data ) );
                for( j = 0; j < RARRAY_LEN( data ); j++ )
                {
                    array[j] = NUM2DBL( RARRAY_PTR( data )[j] );
                }
                shader->pD3DXEffect->lpVtbl->SetFloatArray( shader->pD3DXEffect, shader->handle[i].D3DXHandle, array, RARRAY_LEN( data ) );
            }
            else if( TYPE( RARRAY_PTR( data )[0] ) == T_DATA )
            {
                if ( RDATA( RARRAY_PTR( data )[0] )->dfree == (RUBY_DATA_FUNC)Vector_release )
                {
                    D3DXVECTOR4* array = alloca( sizeof( D3DXVECTOR4 ) * RARRAY_LEN( data ) );
                    for( j = 0; j < RARRAY_LEN( data ); j++ )
                    {
                        memcpy( array + j, DXRUBY_GET_STRUCT( Vector, RARRAY_PTR( data )[j] )->v, sizeof( D3DXVECTOR4 ) );
                    }
                    shader->pD3DXEffect->lpVtbl->SetVectorArray( shader->pD3DXEffect, shader->handle[i].D3DXHandle, array, RARRAY_LEN( data ) );
                }
                else if( RDATA( RARRAY_PTR( data )[0] )->dfree == (RUBY_DATA_FUNC)Matrix_release )
                {
                    D3DXMATRIX* array = alloca( sizeof( D3DXMATRIX ) * RARRAY_LEN( data ) );
                    for( j = 0; j < RARRAY_LEN( data ); j++ )
                    {
                        memcpy( array + j, DXRUBY_GET_STRUCT( Matrix, RARRAY_PTR( data )[j] )->m, sizeof( D3DXMATRIX ) );
                    }
                    shader->pD3DXEffect->lpVtbl->SetMatrixArray( shader->pD3DXEffect, shader->handle[i].D3DXHandle, array, RARRAY_LEN( data ) );
                }
                else
                {
                    rb_raise( eDXRubyError, "Shaderのパラメータが異常です - %s", rb_obj_classname( RARRAY_PTR( data )[0] ) );
                }
            }
        }
        else
        {
            rb_raise( eDXRubyError, "Shaderのパラメータが異常です - %s", rb_obj_classname( data ) );
        }
    }

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

    for( i = 0; i < RARRAY_LEN( sprite->value ) - 2; i++ )
    {
        char buf[5] = "Tex ";
        buf[3] = i + 0x30;
        DXRUBY_CHECK_DISPOSE( DXRUBY_GET_STRUCT( Image, RARRAY_PTR( sprite->value )[i] ), texture );
        shader->pD3DXEffect->lpVtbl->SetTexture( shader->pD3DXEffect, buf,
                                                 (IDirect3DBaseTexture9*)(DXRUBY_GET_STRUCT( Image, RARRAY_PTR( sprite->value )[i] )->texture->pD3DTexture) );
    }

    shader->pD3DXEffect->lpVtbl->SetTechnique( shader->pD3DXEffect, shader->technique );
    shader->pD3DXEffect->lpVtbl->Begin( shader->pD3DXEffect, NULL, 0 );
    shader->pD3DXEffect->lpVtbl->BeginPass( shader->pD3DXEffect, 0 );
    /* 描画 */
    g_pD3DDevice->lpVtbl->DrawPrimitiveUP( g_pD3DDevice, D3DPT_TRIANGLELIST, 2, VertexDataTbl, sizeof(TLVERTX) );
    shader->pD3DXEffect->lpVtbl->EndPass( shader->pD3DXEffect );
    shader->pD3DXEffect->lpVtbl->End( shader->pD3DXEffect );
}

/*--------------------------------------------------------------------
   描画設定（シェーダ描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawShader( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubySprite_draw *sprite;
    struct DXRubyShader *shader;
    float z;
    VALUE ary;
    volatile VALUE temp;
    int i;
    struct DXRubyRenderTarget *rt;

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

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

    sprite = (struct DXRubySprite_draw *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_drawShader ) );

    DXRUBY_CHECK_TYPE( Shader, argv[3] );
    shader = DXRUBY_GET_STRUCT( Shader, argv[3] );
    DXRUBY_CHECK_DISPOSE( shader, pD3DXEffect );

    /* 引数のイメージオブジェクトから中身を取り出す */
    if( TYPE( argv[2] ) == T_ARRAY )
    {
        sprite->value = temp = rb_ary_new(); /* tempはGCによる回収回避 */
        for( i = 0; i < RARRAY_LEN( argv[2] ); i++ )
        {
            DXRUBY_CHECK_IMAGE( RARRAY_PTR( argv[2] )[i] );
            DXRUBY_CHECK_DISPOSE( DXRUBY_GET_STRUCT( Image, RARRAY_PTR( argv[2] )[i] ), texture );
            rb_ary_push( sprite->value, RARRAY_PTR( argv[2] )[i] );
        }
        rb_ary_push( sprite->value, argv[3] );
    }
    else
    {
        DXRUBY_CHECK_IMAGE( argv[2] );
        DXRUBY_CHECK_DISPOSE( DXRUBY_GET_STRUCT( Image, argv[2] ), texture );
        sprite->value = temp = rb_ary_new3( 2, argv[2], argv[3] ); /* tempはGCによる回収回避 */
    }

    ary = rb_ary_new();
    for( i = 0; i < shader->handle_count; i++ )
    {
        rb_ary_push( ary, shader->handle[i].obj );
    }
    rb_ary_push( sprite->value, ary );

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_drawShader_func;
    sprite->x = NUM2INT( argv[0] );
    sprite->y = NUM2INT( argv[1] );
    sprite->alpha = 0xff;
    sprite->blendflag = 0;

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    z = argc < 5 || argv[4] == Qnil ? 0.0f : NUM2DBL( argv[4] );
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return obj;
}


struct DXRubySprite_drawEx {
    void (*func)(void*);
    VALUE value;
    unsigned char blendflag; /* 半透明(000)、加算合成1(100)、加算合成2(101)、減算合成1(110)、減算合成2(111)のフラグ */
    unsigned char alpha;     /* アルファ（透明）値 */
    char reserve1;           /* 予約3 */
    char reserve2;           /* 予約4 */
    int x;
    int y;
    float z;
    float scalex;
    float scaley;
    float centerx;
    float centery;
    float angle;
};

static void Window_drawEx_func( struct DXRubySprite_drawEx *sprite )
{
    TLVERTX VertexDataTbl[6];
    struct DXRubyImage *image = DXRUBY_GET_STRUCT( Image, sprite->value );
    float angle = 3.141592653589793115997963468544185161590576171875f / 180.0f * sprite->angle;
    float sina = sin(angle);
    float cosa = cos(angle);
    float data1x = sprite->scalex * cosa;
    float data2x = sprite->scalex * sina;
    float data1y = sprite->scaley * sina;
    float data2y = sprite->scaley * cosa;
    float tu1;
    float tu2;
    float tv1;
    float tv2;
    float centerx = -sprite->centerx;
    float centery = -sprite->centery;
    float width = image->width;
    float height = image->height;
    float basex = sprite->x - centerx;
    float basey = sprite->y - centery;

    DXRUBY_CHECK_DISPOSE( image, texture );
    tu1 = (image->x) / image->texture->width;
    tu2 = (image->x + image->width) / image->texture->width;
    tv1 = (image->y) / image->texture->height;
    tv2 = (image->y + image->height) / image->texture->height;

    /* 頂点１ */
    VertexDataTbl[0].x =  centerx * data1x - centery * data1y + basex - 0.5f;
    VertexDataTbl[0].y =  centerx * data2x + centery * data2y + basey - 0.5f;
    /* 頂点２ */
    VertexDataTbl[1].x = VertexDataTbl[3].x =  (centerx+width) * data1x - centery * data1y + basex - 0.5f;
    VertexDataTbl[1].y = VertexDataTbl[3].y =  (centerx+width) * data2x + centery * data2y + basey - 0.5f;
    /* 頂点３ */
    VertexDataTbl[4].x =  (centerx+width) * data1x - (centery+height) * data1y + basex - 0.5f;
    VertexDataTbl[4].y =  (centerx+width) * data2x + (centery+height) * data2y + basey - 0.5f;
    /* 頂点４ */
    VertexDataTbl[2].x = VertexDataTbl[5].x =  centerx * data1x - (centery+height) * data1y + basex - 0.5f;
    VertexDataTbl[2].y = VertexDataTbl[5].y =  centerx * data2x + (centery+height) * data2y + basey - 0.5f;
    /* 頂点色 */
    VertexDataTbl[0].color = VertexDataTbl[1].color =
    VertexDataTbl[2].color = VertexDataTbl[3].color =
    VertexDataTbl[4].color = VertexDataTbl[5].color = D3DCOLOR_ARGB(sprite->alpha,255,255,255);
    /* Ｚ座標 */
    VertexDataTbl[0].z  = VertexDataTbl[1].z =
    VertexDataTbl[2].z  = VertexDataTbl[3].z =
    VertexDataTbl[4].z  = VertexDataTbl[5].z = sprite->z;
    /* テクスチャ座標 */
    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));
}

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

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

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

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_IMAGE( argv[2] );
    image = DXRUBY_GET_STRUCT( Image, argv[2] );
    DXRUBY_CHECK_DISPOSE( image, texture );

    sprite = (struct DXRubySprite_drawEx *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_drawEx ) );

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

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    z = argc < 8 || argv[7] == Qnil ? 0.0f : NUM2DBL( argv[7] );
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return obj;
}

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

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

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

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_IMAGE( argv[2] );
    image = DXRUBY_GET_STRUCT( Image, argv[2] );
    DXRUBY_CHECK_DISPOSE( image, texture );

    sprite = (struct DXRubySprite_drawEx *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_drawEx ) );

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

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    z = argc < 7 || argv[6] == Qnil ? 0.0f : NUM2DBL( argv[6] );
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return obj;
}

/*--------------------------------------------------------------------
   描画設定（フルオプション）
 ---------------------------------------------------------------------*/
static VALUE Window_drawEx( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;
    VALUE vx, vy, vz, val, vangle, vscalex, vscaley, valpha, vcenterx, vcentery, vblend, vshader;
    VALUE option;
    float z;
    volatile temp;
    struct DXRubyRenderTarget *rt;

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

    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];
    }

    vblend = rb_hash_lookup( option, symbol_blend );
    vangle = rb_hash_lookup( option, symbol_angle );
    valpha = rb_hash_lookup( option, symbol_alpha );
    vscalex = rb_hash_lookup( option, symbol_scalex );
    vscaley = rb_hash_lookup( option, symbol_scaley );
    vcenterx = rb_hash_lookup( option, symbol_centerx );
    vcentery = rb_hash_lookup( option, symbol_centery );
    vshader = rb_hash_lookup( option, symbol_shader );
    vz = rb_hash_lookup( option, symbol_z );

    if( vshader != Qnil && vscalex == Qnil && vscaley == Qnil && vangle == Qnil )    /* シェーダ指定あり、変形なし */
    {
        struct DXRubyShader *shader;
        struct DXRubySprite_drawShader *sprite;
        int i;
        VALUE ary;

        sprite = (struct DXRubySprite_drawShader *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_drawShader ) );

        DXRUBY_CHECK_TYPE( Shader, vshader );
        shader = DXRUBY_GET_STRUCT( Shader, vshader );
        DXRUBY_CHECK_DISPOSE( shader, pD3DXEffect );

        /* 引数のイメージオブジェクトから中身を取り出す */
        if( TYPE( argv[2] ) == T_ARRAY )
        {
            sprite->value = temp = rb_ary_new(); /* tempはGCによる回収回避 */
            for( i = 0; i < RARRAY_LEN( argv[2] ); i++ )
            {
                DXRUBY_CHECK_IMAGE( RARRAY_PTR( argv[2] )[i] );
                DXRUBY_CHECK_DISPOSE( DXRUBY_GET_STRUCT( Image, RARRAY_PTR( argv[2] )[i] ), texture );
                rb_ary_push( sprite->value, RARRAY_PTR( argv[2] )[i] );
            }
            rb_ary_push( sprite->value, vshader );
        }
        else
        {
            DXRUBY_CHECK_IMAGE( argv[2] );
            DXRUBY_CHECK_DISPOSE( DXRUBY_GET_STRUCT( Image, argv[2] ), texture );
            sprite->value = temp = rb_ary_new3( 2, argv[2], vshader ); /* tempはGCによる回収回避 */
        }

        ary = rb_ary_new();
        for( i = 0; i < shader->handle_count; i++ )
        {
            rb_ary_push( ary, shader->handle[i].obj );
        }
        rb_ary_push( sprite->value, ary );

        /* DXRubySpriteオブジェクト設定 */
        sprite->func = Window_drawShader_func;
        sprite->x = NUM2INT( argv[0] );
        sprite->y = NUM2INT( argv[1] );
        sprite->alpha   = (valpha   == Qnil ? 0xff : NUM2INT( valpha ));
        sprite->blendflag = (vblend == symbol_add ? 4 :
                             (vblend == symbol_add2 ? 5 :
                             (vblend == symbol_sub ? 6 :
                             (vblend == symbol_sub2 ? 7 : 0))));

        /* リストデータに追加 */
        rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
        z = vz == Qnil ? 0.0f : NUM2DBL( vz );
        rt->SpriteList[rt->SpriteCount].z = z;
        sprite->z = z;
    }
    else
    {
        struct DXRubySprite_drawEx *sprite;

        /* 引数のイメージオブジェクトから中身を取り出す */
        DXRUBY_CHECK_IMAGE( argv[2] );
        image = DXRUBY_GET_STRUCT( Image, argv[2] );
        DXRUBY_CHECK_DISPOSE( image, texture );

        sprite = (struct DXRubySprite_drawEx *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_drawEx ) );

        /* DXRubySpriteオブジェクト設定 */
        sprite->func = Window_drawEx_func;
        sprite->x = NUM2INT( argv[0] );
        sprite->y = NUM2INT( argv[1] );
        sprite->angle   = (vangle   == Qnil ? 0.0f             : NUM2DBL( vangle   ));
        sprite->scalex  = (vscalex  == Qnil ? 1.0f             : NUM2DBL( vscalex  ));
        sprite->scaley  = (vscaley  == Qnil ? 1.0f             : NUM2DBL( vscaley  ));
        sprite->centerx = (vcenterx == Qnil ? image->width / 2 : NUM2DBL( vcenterx ));
        sprite->centery = (vcentery == Qnil ? image->height / 2 : NUM2DBL( vcentery ));
        sprite->alpha   = (valpha   == Qnil ? 0xff             : NUM2INT( valpha   ));
        sprite->blendflag = (vblend == symbol_add ? 4 :
                             (vblend == symbol_add2 ? 5 :
                             (vblend == symbol_sub ? 6 :
                             (vblend == symbol_sub2 ? 7 : 0))));
        sprite->value = argv[2];
        /* リストデータに追加 */
        rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
        z = vz == Qnil ? 0.0f : NUM2DBL( vz );
        rt->SpriteList[rt->SpriteCount].z = z;
        sprite->z = z;
    }

    rt->SpriteCount++;

    return obj;
}


struct DXRubySprite_drawMesh {
    void (*func)(void*);
    VALUE value;
    D3DMATRIX matrix_world;
};

static void Window_drawMesh_func( struct DXRubySprite_drawMesh *sprite )
{
    TLVERTX VertexDataTbl[6];
    struct DXRubyMesh *mesh = DXRUBY_GET_STRUCT( Mesh, sprite->value );
    int i;

    DXRUBY_CHECK_DISPOSE( mesh, ppD3DTextures );

    /* ワールド変換行列セット */
    g_pD3DDevice->lpVtbl->SetTransform( g_pD3DDevice, D3DTS_WORLDMATRIX(0), &sprite->matrix_world );

    /* メッシュ描画 */
    for( i=0; i < mesh->materials; i++ )
    {
        g_pD3DDevice->lpVtbl->SetMaterial( g_pD3DDevice, &mesh->pD3DMaterials[i] );
        g_pD3DDevice->lpVtbl->SetTexture( g_pD3DDevice, 0, (IDirect3DBaseTexture9 *)mesh->ppD3DTextures[i] );
        mesh->pD3DXMesh->lpVtbl->DrawSubset( mesh->pD3DXMesh, i );
    }
}

/*--------------------------------------------------------------------
   描画設定（メッシュ描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawMesh( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyMesh *mesh;
    struct DXRubySprite_drawMesh *sprite;
    D3DMATRIX matrix, matrix_t;
    float angle;
    struct DXRubyRenderTarget *rt;

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

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

    sprite = (struct DXRubySprite_drawMesh *)RenderTarget_AllocMeshList( rt, sizeof( struct DXRubySprite_drawMesh ) );
    sprite->func = Window_drawMesh_func;

    if( TYPE( argv[0] ) == T_ARRAY ) /* 引数の1つ目が配列の場合は個別指定 */
    {
        if( argc < 4 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 4);

        /* 引数のメッシュオブジェクトから中身を取り出す */
        DXRUBY_CHECK_TYPE( Mesh, argv[3] );
        mesh = DXRUBY_GET_STRUCT( Mesh, argv[3] );
        DXRUBY_CHECK_DISPOSE( mesh, ppD3DTextures );

        sprite->value = argv[3];

        if( argv[2] != Qnil )
        {
            Check_Type( argv[2], T_ARRAY );
            if( RARRAY_LEN( argv[2] ) < 3 ) rb_raise( eDXRubyError, "スケーリング配列の要素が足りません - Window_drawMesh");
            D3DXMatrixScaling( &matrix, (float)NUM2DBL( RARRAY_PTR( argv[2] )[0] ),
                                        (float)NUM2DBL( RARRAY_PTR( argv[2] )[1] ),
                                        (float)NUM2DBL( RARRAY_PTR( argv[2] )[2] ) );
        }
        else
        {
            D3DXMatrixScaling( &matrix, 1, 1, 1 );
        }

        if( argv[1] != Qnil )
        {
            Check_Type( argv[1], T_ARRAY );
            if( RARRAY_LEN( argv[1] ) < 3 ) rb_raise( eDXRubyError, "回転配列の要素が足りません - Window_drawMesh");
            angle = 3.141592653589793115997963468544185161590576171875f / 180.0f;
            D3DXMatrixRotationX  ( &matrix_t  , angle * (float)NUM2DBL( RARRAY_PTR( argv[1] )[0] ) );
            D3DXMatrixMultiply   ( &matrix  , &matrix, &matrix_t );
            D3DXMatrixRotationY  ( &matrix_t  , angle * (float)NUM2DBL( RARRAY_PTR( argv[1] )[1] ) );
            D3DXMatrixMultiply   ( &matrix  , &matrix, &matrix_t );
            D3DXMatrixRotationZ  ( &matrix_t  , angle * (float)NUM2DBL( RARRAY_PTR( argv[1] )[2] ) );
            D3DXMatrixMultiply   ( &matrix  , &matrix, &matrix_t );
        }

        if( RARRAY_LEN( argv[0] ) < 3 ) rb_raise( eDXRubyError, "移動配列の要素が足りません - Window_drawMesh");
        D3DXMatrixTranslation( &matrix_t, (float)NUM2DBL( RARRAY_PTR( argv[0] )[0] ), 
                                          (float)NUM2DBL( RARRAY_PTR( argv[0] )[1] ), 
                                          (float)NUM2DBL( RARRAY_PTR( argv[0] )[2] ) );
        D3DXMatrixMultiply   ( &sprite->matrix_world  , &matrix, &matrix_t );
        /* リストデータに追加 */
        rt->MeshList[rt->MeshCount].sprite = (struct DXRubySprite *)sprite;
        rt->MeshList[rt->MeshCount].z = argc < 5 || argv[4] == Qnil ? 0.0f : NUM2DBL( argv[4] );
        rt->MeshCount++;
    }
    else /* そうじゃなければ行列のはず */
    {
        DXRUBY_CHECK_TYPE( Mesh, argv[1] );
        sprite->value = argv[1];
        DXRUBY_CHECK_DISPOSE( DXRUBY_GET_STRUCT( Mesh, argv[1] ), texture );
        if( argv[0] == Qnil )
        {
            D3DXMatrixTranslation( &sprite->matrix_world, 0, 0, 0 );
        }
        else
        {
            struct DXRubyMatrix *mat;
            DXRUBY_CHECK_TYPE( Matrix, argv[0] );

            mat = DXRUBY_GET_STRUCT( Matrix, argv[0] );
            memcpy( &sprite->matrix_world, mat->m, sizeof( float ) * 16 );
        }

        /* リストデータに追加 */
        rt->MeshList[rt->MeshCount].sprite = (struct DXRubySprite *)sprite;
        rt->MeshList[rt->MeshCount].z = argc < 3 || argv[2] == Qnil ? 0.0f : NUM2DBL( argv[2] );
        rt->MeshCount++;
    }

    return obj;
}


struct DXRubySprite_draw3D {
    void (*func)(void*);
    VALUE value;
    D3DMATRIX matrix_world;
};

static void Window_draw3D_func( struct DXRubySprite_draw3D *sprite )
{
    TLVERTX VertexDataTbl[6];
    struct DXRubyImage *image = DXRUBY_GET_STRUCT( Image, sprite->value );
    float width = image->width;
    float height = image->height;
    float tu1;
    float tu2;
    float tv1;
    float tv2;
    D3DMATRIX matrix, matrix_t;

    DXRUBY_CHECK_DISPOSE( image, texture );
    tu1 = (image->x + 0.5f) / image->texture->width;
    tu2 = (image->x + width + 0.5f) / image->texture->width;
    tv1 = (image->y + 0.5f) / image->texture->height;
    tv2 = (image->y + height + 0.5f) / image->texture->height;

    /* 頂点１ */
    VertexDataTbl[0].x = -width/2;
    VertexDataTbl[0].y = -height/2;
    /* 頂点２ */
    VertexDataTbl[1].x = VertexDataTbl[3].x = width/2;
    VertexDataTbl[1].y = VertexDataTbl[3].y = -height/2;
    /* 頂点３ */
    VertexDataTbl[4].x = width/2;
    VertexDataTbl[4].y = height/2;
    /* 頂点４ */
    VertexDataTbl[2].x = VertexDataTbl[5].x = -width/2;
    VertexDataTbl[2].y = VertexDataTbl[5].y = height/2;
    /* 頂点色 */
    VertexDataTbl[0].color = VertexDataTbl[1].color =
    VertexDataTbl[2].color = VertexDataTbl[3].color =
    VertexDataTbl[4].color = VertexDataTbl[5].color = D3DCOLOR_ARGB(255,255,255,255);
    /* Ｚ座標 */
    VertexDataTbl[0].z  = VertexDataTbl[1].z =
    VertexDataTbl[2].z  = VertexDataTbl[3].z =
    VertexDataTbl[4].z  = VertexDataTbl[5].z = 0;
    /* テクスチャ座標 */
    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;

    D3DXMatrixTranslation( &matrix_t, width/2, height/2, 0 );
    D3DXMatrixMultiply   ( &matrix, &sprite->matrix_world, &matrix_t );

    /* ワールド変換行列セット */
    g_pD3DDevice->lpVtbl->SetTransform( g_pD3DDevice, D3DTS_WORLDMATRIX(0), &matrix );

    /* テクスチャをセット */
    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));
}

/*--------------------------------------------------------------------
   描画設定（3D描画）
 ---------------------------------------------------------------------*/
static VALUE Window_draw3D( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubySprite_draw3D *sprite;
    struct DXRubyMatrix *mat;
    float z;
    D3DMATRIX matrix_t;
    struct DXRubyRenderTarget *rt;

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

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

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_TYPE( Matrix, argv[0] );
    DXRUBY_CHECK_IMAGE( argv[1] );
    DXRUBY_CHECK_DISPOSE( DXRUBY_GET_STRUCT( Image, argv[1]), texture );
    mat = DXRUBY_GET_STRUCT( Matrix, argv[0] );

    sprite = (struct DXRubySprite_draw3D *)RenderTarget_AllocMeshList( rt, sizeof( struct DXRubySprite_draw3D ) );

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_draw3D_func;
    sprite->value = argv[1];
    memcpy( &sprite->matrix_world, mat->m, sizeof( float ) * 16 );

    /* リストデータに追加 */
    rt->MeshList[rt->MeshCount].sprite = (struct DXRubySprite *)sprite;
    rt->MeshList[rt->MeshCount].z = argc < 3 || argv[2] == Qnil ? 0.0f : NUM2DBL( argv[2] );
    rt->MeshCount++;

    return obj;
}


struct DXRubySprite_drawFont {
    void (*func)(void*);
    VALUE value;
    unsigned char blendflag; /* 半透明(000)、加算合成1(100)、加算合成2(101)、減算合成1(110)、減算合成2(111)のフラグ */
    unsigned char alpha;     /* アルファ（透明）値 */
    char reserve1;           /* 予約3 */
    char reserve2;           /* 予約4 */
    int x;
    int y;
    int z;
    float scalex;
    float scaley;
    float centerx;
    float centery;
    float angle;
    int color;                  /* フォントの色 */
    char str[0];                /* 文字列オブジェクト */
};

static void Window_drawFont_func( struct DXRubySprite_drawFont *sprite )
{
    D3DVECTOR vector;
    D3DXMATRIX matrix;
    D3DXMATRIX matrix_t;
    RECT rect;
    struct DXRubyFont *font = DXRUBY_GET_STRUCT( Font, sprite->value );
    float angle = 3.141592653589793115997963468544185161590576171875f / 180.0f * sprite->angle;
    DXRUBY_CHECK_DISPOSE( font, pD3DXFont );

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

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

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

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

    rect.left   = -sprite->centerx;
    rect.top    = -sprite->centery;
    rect.right  = sprite->centerx;
    rect.bottom = sprite->centery;
    font->pD3DXFont->lpVtbl->DrawText( font->pD3DXFont, g_pD3DXSprite, sprite->str, -1, &rect, DT_LEFT | DT_NOCLIP,
                                       ((int)sprite->alpha << 24) | sprite->color & 0x00ffffff);
    g_pD3DXSprite->lpVtbl->Flush( g_pD3DXSprite );

    /* スプライトの描画終了 */
    g_pD3DXSprite->lpVtbl->End( g_pD3DXSprite );
}
/*--------------------------------------------------------------------
   フォント描画
 ---------------------------------------------------------------------*/
static VALUE Window_drawFont( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubySprite_drawFont *sprite;
    struct DXRubyFont *font;
    VALUE color;
    int cr, cg, cb;
    VALUE z, angle, scalex, scaley, alpha, centerx, centery, blend;
    VALUE option;
    struct DXRubyRenderTarget *rt;

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

    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_lookup( option, symbol_blend );
    angle = rb_hash_lookup( option, symbol_angle );
    alpha = rb_hash_lookup( option, symbol_alpha );
    scalex = rb_hash_lookup( option, symbol_scalex );
    scaley = rb_hash_lookup( option, symbol_scaley );
    centerx = rb_hash_lookup( option, symbol_centerx );
    centery = rb_hash_lookup( option, symbol_centery );
    z = rb_hash_lookup( option, symbol_z );
    color = rb_hash_lookup( option, symbol_color );

    DXRUBY_CHECK_TYPE( Font, argv[3] );
    font = DXRUBY_GET_STRUCT( Font, argv[3] );
    DXRUBY_CHECK_DISPOSE( font, pD3DXFont );

    sprite = (struct DXRubySprite_drawFont *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_drawFont ) + RSTRING_LEN(argv[2]) + 2 );
    if( sprite == NULL )
    {
        rb_raise( eDXRubyError, "フォント用メモリの確保に失敗しました" );
    }

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_drawFont_func;
    sprite->x = NUM2INT( argv[0] );
    sprite->y = NUM2INT( argv[1] );
    sprite->angle   = (angle   == Qnil ? 0.0f : NUM2DBL( angle ));
    sprite->scalex  = (scalex  == Qnil ? 1.0f : NUM2DBL( scalex ));
    sprite->scaley  = (scaley  == Qnil ? 1.0f : NUM2DBL( scaley ));
    sprite->centerx = (centerx == Qnil ? 0.0f : NUM2DBL( centerx ));;
    sprite->centery = (centery == Qnil ? 0.0f : NUM2DBL( centery ));;
    sprite->alpha   = (alpha   == Qnil ? 0xff : NUM2INT( alpha ));
    sprite->blendflag = (blend == symbol_add ? 4 :
                            (blend == symbol_add2 ? 5 :
                                (blend == symbol_sub ? 6 :
                                    (blend == symbol_sub2 ? 7 : 0))));
    sprite->value = argv[3];

    lstrcpy( sprite->str, RSTRING_PTR( argv[2] ) );  /* 文字列の保存 */
    sprite->str[RSTRING_LEN(argv[2])] = ' ';    /* イタリック対策にスペース追加 */
    sprite->str[RSTRING_LEN(argv[2])+1] = 0;

    if( color != Qnil )
    {
        Check_Type( color, T_ARRAY );
        if( RARRAY_LEN( color ) == 4 )
        {
            sprite->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;
    }
    sprite->color = D3DCOLOR_XRGB(cr, cg, cb);
    sprite->value = argv[3];
    sprite->z = 0;

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    rt->SpriteList[rt->SpriteCount].z = z == Qnil ? 0.0f : NUM2DBL( z );
    rt->SpriteCount++;

    return obj;
}


struct DXRubySprite_drawMorph {
    void (*func)(void*);
    VALUE value;
    unsigned char blendflag; /* 半透明(000)、加算合成1(100)、加算合成2(101)、減算合成1(110)、減算合成2(111)のフラグ */
    unsigned char alpha;     /* アルファ（透明）値 */
    char reserve1;           /* 予約3 */
    char reserve2;           /* 予約4 */
    float x1;
    float y1;
    float x2;
    float y2;
    float x3;
    float y3;
    float x4;
    float y4;
    float z;
    int dividex;
    int dividey;
};

static void Window_drawMorph_func( struct DXRubySprite_drawMorph *sprite )
{
    TLVERTX *VertexDataTbl;
    struct DXRubyImage *image = DXRUBY_GET_STRUCT( Image, sprite->value );
    float width = image->width;
    float height = image->height;
    float x1 = sprite->x1;
    float y1 = sprite->y1;
    float x2 = sprite->x2;
    float y2 = sprite->y2;
    float x3 = sprite->x3;
    float y3 = sprite->y3;
    float x4 = sprite->x4;
    float y4 = sprite->y4;
    float tu1;
    float tu2;
    float tv1;
    float tv2;
    int count_x, count_y;


    DXRUBY_CHECK_DISPOSE( image, texture );
    tu1 = image->x / image->texture->width;
    tu2 = (image->x + width) / image->texture->width;
    tv1 = image->y / image->texture->height;
    tv2 = (image->y + height) / image->texture->height;

    VertexDataTbl = alloca( sizeof(TLVERTX) * sprite->dividex * sprite->dividey * 6 );

    for( count_y = 0; count_y < sprite->dividey; count_y++ )
    {
        for( count_x = 0; count_x < sprite->dividex; count_x++ )
        {
            int cur = (count_x + sprite->dividex * count_y) * 6;
            float wx1 = (float)count_x / (float)sprite->dividex;
            float wy1 = (float)count_y / (float)sprite->dividey;
            float wx2 = (float)(count_x+1) / (float)sprite->dividex;
            float wy2 = (float)(count_y+1) / (float)sprite->dividey;

            float tx1 = (x2 - x1) * wx1 + x1;
            float ty1 = (y2 - y1) * wy1 + y1;
            float bx1 = (x3 - x4) * wx1 + x4;
            float by1 = (y3 - y4) * wy1 + y4;
            float lx1 = (x4 - x1) * wx1 + x1;
            float ly1 = (y4 - y1) * wy1 + y1;
            float rx1 = (x3 - x2) * wx1 + x2;
            float ry1 = (y3 - y2) * wy1 + y2;
            float tx2 = (x2 - x1) * wx2 + x1;
            float ty2 = (y2 - y1) * wy2 + y1;
            float bx2 = (x3 - x4) * wx2 + x4;
            float by2 = (y3 - y4) * wy2 + y4;
            float lx2 = (x4 - x1) * wx2 + x1;
            float ly2 = (y4 - y1) * wy2 + y1;
            float rx2 = (x3 - x2) * wx2 + x2;
            float ry2 = (y3 - y2) * wy2 + y2;

            /* 頂点１ */
            VertexDataTbl[cur+0].x = (bx1 - tx1) * wy1 + tx1 - 0.5f;
            VertexDataTbl[cur+0].y = (ry1 - ly1) * wx1 + ly1 - 0.5f;
            /* 頂点２ */
            VertexDataTbl[cur+1].x = VertexDataTbl[cur+3].x = (bx2 - tx2) * wy1 + tx2 - 0.5f;
            VertexDataTbl[cur+1].y = VertexDataTbl[cur+3].y = (ry1 - ly1) * wx2 + ly1 - 0.5f;
            /* 頂点３ */
            VertexDataTbl[cur+4].x = (bx2 - tx2) * wy2 + tx2 - 0.5f;
            VertexDataTbl[cur+4].y = (ry2 - ly2) * wx2 + ly2 - 0.5f;
            /* 頂点４ */
            VertexDataTbl[cur+2].x = VertexDataTbl[cur+5].x = (bx1 - tx1) * wy2 + tx1 - 0.5f;
            VertexDataTbl[cur+2].y = VertexDataTbl[cur+5].y = (ry2 - ly2) * wx1 + ly2 - 0.5f;
            /* 頂点色 */
            VertexDataTbl[cur+0].color = VertexDataTbl[cur+1].color =
            VertexDataTbl[cur+2].color = VertexDataTbl[cur+3].color =
            VertexDataTbl[cur+4].color = VertexDataTbl[cur+5].color = D3DCOLOR_ARGB(sprite->alpha,255,255,255);
            /* Ｚ座標 */
            VertexDataTbl[cur+0].z  = VertexDataTbl[cur+1].z =
            VertexDataTbl[cur+2].z  = VertexDataTbl[cur+3].z =
            VertexDataTbl[cur+4].z  = VertexDataTbl[cur+5].z = sprite->z;
            /* テクスチャ座標 */
            VertexDataTbl[cur+0].tu = VertexDataTbl[cur+5].tu = VertexDataTbl[cur+2].tu = tu1 + ((tu2 - tu1) / sprite->dividex) * count_x;
            VertexDataTbl[cur+0].tv = VertexDataTbl[cur+1].tv = VertexDataTbl[cur+3].tv = tv1 + ((tv2 - tv1) / sprite->dividey) * count_y;
            VertexDataTbl[cur+1].tu = VertexDataTbl[cur+3].tu = VertexDataTbl[cur+4].tu = tu1 + ((tu2 - tu1) / sprite->dividex) * (count_x+1);
            VertexDataTbl[cur+4].tv = VertexDataTbl[cur+5].tv = VertexDataTbl[cur+2].tv = tv1 + ((tv2 - tv1) / sprite->dividey) * (count_y+1);
        }
    }

    /* テクスチャをセット */
    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, sprite->dividex * sprite->dividey * 2, VertexDataTbl, sizeof(TLVERTX));
}

/*--------------------------------------------------------------------
   描画設定（4点指定）
 ---------------------------------------------------------------------*/
static VALUE Window_drawMorph( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;
    struct DXRubySprite_drawMorph *sprite;
    float z;
    struct DXRubyRenderTarget *rt;
    VALUE voption;
    VALUE vz, vdividex, vdividey, valpha, vblend;

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

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

    if( argc < 10 || argv[9] == Qnil )
    {
        voption = rb_hash_new();
    }
    else
    {
        Check_Type( argv[9], T_HASH );
        voption = argv[9];
    }

    vblend = rb_hash_lookup( voption, symbol_blend );
    valpha = rb_hash_lookup( voption, symbol_alpha );
    vdividex = rb_hash_lookup( voption, symbol_dividex );
    vdividey = rb_hash_lookup( voption, symbol_dividey );
    vz = rb_hash_lookup( voption, symbol_z );

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_IMAGE( argv[8] );
    image = DXRUBY_GET_STRUCT( Image, argv[8] );
    DXRUBY_CHECK_DISPOSE( image, texture );

    sprite = (struct DXRubySprite_drawMorph *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_drawMorph ) );

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_drawMorph_func;
    sprite->x1 = (float)NUM2DBL( argv[0] );
    sprite->y1 = (float)NUM2DBL( argv[1] );
    sprite->x2 = (float)NUM2DBL( argv[2] );
    sprite->y2 = (float)NUM2DBL( argv[3] );
    sprite->x3 = (float)NUM2DBL( argv[4] );
    sprite->y3 = (float)NUM2DBL( argv[5] );
    sprite->x4 = (float)NUM2DBL( argv[6] );
    sprite->y4 = (float)NUM2DBL( argv[7] );
    sprite->value = argv[8];
    sprite->dividex = vdividex == Qnil ? 1 : NUM2INT( vdividex );
    sprite->dividey = vdividey == Qnil ? 1 : NUM2INT( vdividey );
    sprite->alpha   = valpha   == Qnil ? 0xff : NUM2INT( valpha );
    sprite->blendflag = (vblend == symbol_add ? 4 :
                            (vblend == symbol_add2 ? 5 :
                                (vblend == symbol_sub ? 6 :
                                    (vblend == symbol_sub2 ? 7 : 0))));

    if( sprite->dividex <= 0 || sprite->dividey <= 0 )
    {
        rb_raise( eDXRubyError, "分割数に0以下は指定できません");
    }

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    rt->SpriteList[rt->SpriteCount].z = vz == Qnil ? 0.0f : NUM2DBL( vz );
    sprite->z = vz == Qnil ? 0.0f : NUM2DBL( vz );
    rt->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;
    struct DXRubyRenderTarget *rt;

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

    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)Image_release )
        {
          rb_raise(rb_eTypeError, "wrong argument type %s (expected DXRuby::Image)", rb_obj_classname( mapdata[i] ));
        }
         DXRUBY_CHECK_DISPOSE( image, texture );
   }

    /* 幅と高さ取得 */
    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 )
            {
                struct DXRubySprite_draw *sprite;

                index = NUM2INT( arr2[mx] );

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

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

                sprite = (struct DXRubySprite_draw *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_draw ) );

                /* DXRubySpriteオブジェクト設定 */
                sprite->func = Window_draw_func;
                sprite->x = x;
                sprite->y = y;
                sprite->value = mapdata[index];
                sprite->alpha = 0xff;
                sprite->blendflag = 0;

                /* リストデータに追加 */
                rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
                rt->SpriteList[rt->SpriteCount].z = z;
                sprite->z = 0;
                rt->SpriteCount++;
            }

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

    return obj;
}


/*--------------------------------------------------------------------
   （内部関数）画面更新
 ---------------------------------------------------------------------*/
static VALUE Window_update( VALUE obj )
{
    HRESULT hr;
    static int max;
    __int64 drawStartCounter;
    VALUE vrt;
    int x_2d, width_2d;
    int y_2d, height_2d;
    struct DXRubyRenderTarget *rt; /* 出力先 */

    while( 1 )
    {
        hr = g_pD3DDevice->lpVtbl->TestCooperativeLevel( g_pD3DDevice );
        if( FAILED( hr ) )
        {
            if( hr == D3DERR_DEVICELOST )  /* デバイスはロスト状態である */
            {
                MSG msg;
                if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) != 0)
                {
                    /* メッセージがある時 */
                    TranslateMessage( &msg );
                    DispatchMessage( &msg );
                }
                Sleep( 100 );
                continue;
            }
            else if( hr == D3DERR_DEVICENOTRESET ) /* デバイスはロスト状態であるがリセット可能である */
            {
                int i;
                /* ここへ来た時はデバイスがリセット可能状態である */
                /* スプライトをロストさせる */
                if( g_pD3DXSprite )
                {
                    g_pD3DXSprite->lpVtbl->OnLostDevice( g_pD3DXSprite );
                }

                /* レンダーターゲットの解放 */
                {
                    struct DXRubyRenderTarget *rt = (struct DXRubyRenderTarget *)g_WindowInfo.render_target;
                    RELEASE( rt->surface );
                    RELEASE( rt->zbuffer );
                }
                for( i = 0; i < g_RenderTargetList.count; i++ )
                {
                    struct DXRubyRenderTarget *rt = (struct DXRubyRenderTarget *)g_RenderTargetList.pointer[i];
                    RELEASE( rt->surface );
                    RELEASE( rt->zbuffer );
                    RELEASE( rt->texture->pD3DTexture );
                }
                /* シェーダのロスト */
                for( i = 0; i < g_ShaderList.count; i++ )
                {
                    struct DXRubyShader *shader = (struct DXRubyShader *)g_ShaderList.pointer[i];
                    shader->pD3DXEffect->lpVtbl->OnLostDevice( shader->pD3DXEffect );
                }

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

                /* レンダーターゲットの復元 */
                {
                    struct DXRubyRenderTarget *rt = (struct DXRubyRenderTarget *)g_WindowInfo.render_target;
                    g_pD3DDevice->lpVtbl->GetRenderTarget( g_pD3DDevice, 0, &rt->surface );
                    g_pD3DDevice->lpVtbl->GetDepthStencilSurface( g_pD3DDevice, &rt->zbuffer );
                }
                for( i = 0; i < g_RenderTargetList.count; i++ )
                {
                    struct DXRubyRenderTarget *rt = (struct DXRubyRenderTarget *)g_RenderTargetList.pointer[i];

                    /* テクスチャオブジェクトを作成する */
                    hr = D3DXCreateTexture( g_pD3DDevice, rt->texture->width, rt->texture->height,
                                            1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
                                            &rt->texture->pD3DTexture);
                    if( FAILED( hr ) ) rb_raise( eDXRubyError, "RenderTargetの復元に失敗しました" );

                    hr = rt->texture->pD3DTexture->lpVtbl->GetSurfaceLevel( rt->texture->pD3DTexture, 0, &rt->surface );
                    if( FAILED( hr ) ) rb_raise( eDXRubyError, "RenderTargetの復元に失敗しました" );

                    /* Zバッファサーフェイスを作成する */
                    hr = g_pD3DDevice->lpVtbl->CreateDepthStencilSurface( g_pD3DDevice, 
                                                                          rt->texture->width, rt->texture->height, 
                                                                          D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, FALSE, &rt->zbuffer, NULL);
                    if( FAILED( hr ) ) rb_raise( eDXRubyError, "RenderTargetの復元に失敗しました" );
                }

                /* シェーダの復帰 */
                for( i = 0; i < g_ShaderList.count; i++ )
                {
                    struct DXRubyShader *shader = (struct DXRubyShader *)g_ShaderList.pointer[i];
                    shader->pD3DXEffect->lpVtbl->OnResetDevice( shader->pD3DXEffect );
                }

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

    if( obj == mWindow )
    {
        vrt = Qnil;
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        DXRUBY_CHECK_TYPE( RenderTarget, obj );
        vrt = obj;
        DXRUBY_CHECK_DISPOSE( DXRUBY_GET_STRUCT( RenderTarget, vrt ), texture );
        rt = DXRUBY_GET_STRUCT( RenderTarget, vrt );
    }

    drawStartCounter = GetSystemCounter();

//    /* 動画再生中 */
//    if( g_PlayingMovie == 1 )
//    {
//        return obj;
//    }

//    /* 描画対象が1つも無ければ描画処理そのものを行わない */
//    if( rt->SpriteCount == 0 && rt->MeshCount == 0 )
//    {
//        if( vrt == Qnil )
//        {
//            /* シーンの表示 */
//            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;
//    }

    /* シーンのクリア */
    if( vrt == Qnil )
    {
        D3DVIEWPORT9 vp;
        vp.X       = x_2d = 0;
        vp.Y       = y_2d = 0;
        vp.Width   = width_2d = g_D3DPP.BackBufferWidth;
        vp.Height  = height_2d = g_D3DPP.BackBufferHeight;
        vp.MinZ    = 0.0f;
        vp.MaxZ    = 1.0f;
        g_pD3DDevice->lpVtbl->SetRenderTarget( g_pD3DDevice, 0, rt->surface );
        g_pD3DDevice->lpVtbl->SetDepthStencilSurface( g_pD3DDevice, rt->zbuffer );
        g_pD3DDevice->lpVtbl->SetViewport( g_pD3DDevice, &vp );
        g_pD3DDevice->lpVtbl->Clear( g_pD3DDevice, 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
                                     D3DCOLOR_XRGB( rt->r, rt->g, rt->b ), 1.0f, 0 );
    }
    else
    {
        D3DVIEWPORT9 vp;
        vp.X       = x_2d = 0;
        vp.Y       = y_2d = 0;
        vp.Width   = width_2d = rt->texture->width;
        vp.Height  = height_2d = rt->texture->height;
        vp.MinZ    = 0.0f;
        vp.MaxZ    = 1.0f;
        g_pD3DDevice->lpVtbl->SetRenderTarget( g_pD3DDevice, 0, rt->surface );
        g_pD3DDevice->lpVtbl->SetDepthStencilSurface( g_pD3DDevice, rt->zbuffer );
        g_pD3DDevice->lpVtbl->SetViewport( g_pD3DDevice, &vp );
        g_pD3DDevice->lpVtbl->Clear( g_pD3DDevice, 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
                                     D3DCOLOR_ARGB( rt->a, rt->r, rt->g, rt->b ), 1.0f, 0 );
    }

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

        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_ALPHABLENDENABLE, TRUE );
        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_ZENABLE,D3DZB_TRUE );
        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_ZWRITEENABLE, TRUE );
        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_LIGHTING,FALSE);
        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_FOGENABLE, FALSE );
        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_LIGHTING, FALSE );
        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SEPARATEALPHABLENDENABLE, FALSE );
        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SHADEMODE, D3DSHADE_GOURAUD );
        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SRGBWRITEENABLE, FALSE );
        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_VERTEXBLEND, FALSE );
        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_WRAP0, 0 );
//g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_ZFUNC, D3DCMP_LESS );
//        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_MULTISAMPLEANTIALIAS, TRUE );

        g_pD3DDevice->lpVtbl->SetTextureStageState( g_pD3DDevice, 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
        g_pD3DDevice->lpVtbl->SetTextureStageState( g_pD3DDevice, 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
        g_pD3DDevice->lpVtbl->SetTextureStageState( g_pD3DDevice, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
        g_pD3DDevice->lpVtbl->SetTextureStageState( g_pD3DDevice, 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
        g_pD3DDevice->lpVtbl->SetTextureStageState( g_pD3DDevice, 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
        g_pD3DDevice->lpVtbl->SetTextureStageState( g_pD3DDevice, 0, D3DTSS_COLOROP, D3DTOP_MODULATE );

        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_FOGENABLE, FALSE );

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

        /* 3D空間の向こうへの2D描画 */
        if( rt->SpriteCount > 0 )
        {
            D3DMATRIX matrix, matrix_t;
            int oldflag = 0;

            RenderTarget_SortSpriteList( rt );

            if( rt->SpriteList[0].z < rt->zf )
            {
                /* 2D描画 */
                D3DXMatrixScaling    ( &matrix, 1, 1, 1 );
                g_pD3DDevice->lpVtbl->SetTransform( g_pD3DDevice, D3DTS_WORLDMATRIX(0), &matrix);

                D3DXMatrixScaling    ( &matrix, 1, -1, 1 );
                D3DXMatrixTranslation( &matrix_t, (float)-(width_2d)/2.0f, (float)(height_2d)/2.0f, 0 );
                D3DXMatrixMultiply( &matrix, &matrix, &matrix_t );
                g_pD3DDevice->lpVtbl->SetTransform( g_pD3DDevice, D3DTS_VIEW, &matrix );
                matrix._11 = 2.0f / width_2d;
                matrix._12 = matrix._13 = matrix._14 = 0;
                matrix._22 = 2.0f / height_2d;
                matrix._21 = matrix._23 = matrix._24 = 0;
                matrix._31 = matrix._32 = 0;matrix._33 = 0; matrix._34 = 0;
                matrix._41 = matrix._42 = 0;matrix._43 = 1;matrix._44 = 1;
                g_pD3DDevice->lpVtbl->SetTransform( g_pD3DDevice, D3DTS_PROJECTION, &matrix );

                g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_ZENABLE,D3DZB_FALSE );
                g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_ZWRITEENABLE, FALSE );
                g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_CULLMODE,D3DCULL_NONE);

                for( i = 0; i < rt->SpriteCount && rt->SpriteList[i].z < rt->zf; i++ )
                {
                    struct DXRubySprite_draw *temp = (struct DXRubySprite_draw *)rt->SpriteList[i].sprite;

                    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 4:          /* 加算合成1の設定 */
                            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
                            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_ONE );
                            break;
                        case 5:          /* 加算合成2の設定 */
                            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_ONE );
                            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
                            break;
                        case 6:          /* 減算合成1の設定 */
                            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_ZERO );
                            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
                            break;
                        case 7:          /* 減算合成2の設定 */
                            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_ZERO );
                            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR );
                            break;
                        }
                    }

                    if( temp->z < 0 )
                    {
                        if( rt->useview == 0 && rt->useproj == 0 )
                        {
                            temp->z = temp->z + rt->zn;
                        }
                    }

                    oldflag = temp->blendflag;
                    temp->func( temp );
                }
            }
        }

        /* 3D描画の準備 */
        {
            D3DMATRIX matrix, matrix_t;
            float fog_start   = rt->fog_start;
            float EndPos     = rt->fog_end;
            float density    = rt->fog_density;

            RenderTarget_SortMeshList( rt );

            if( rt->useview == 1 )
            { /* ビュー行列がユーザーによって指定されている場合 */
                g_pD3DDevice->lpVtbl->SetTransform( g_pD3DDevice, D3DTS_VIEW, &rt->matrix_view );
            }
            else
            { /* 擬似3Dモードの場合 */
                D3DXMatrixTranslation( &matrix, (float)-(width_2d)/2.0f, (float)-(height_2d)/2.0f, 0 );
                g_pD3DDevice->lpVtbl->SetTransform( g_pD3DDevice, D3DTS_VIEW, &matrix );
//                D3DXMatrixTranslation( &matrix, (float)-(g_WindowInfo.width)/2.0f, (float)-(g_WindowInfo.height)/2.0f, 0 );
//                g_pD3DDevice->lpVtbl->SetTransform( g_pD3DDevice, D3DTS_VIEW, &matrix );
            }

            if( rt->useproj == 1 )
            { /* 射影変換行列がユーザーによって指定されている場合 */
                g_pD3DDevice->lpVtbl->SetTransform( g_pD3DDevice, D3DTS_PROJECTION, &rt->matrix_proj );
            }
            else
            { /* 擬似3Dモードの場合 */
                D3DXMatrixScaling( &matrix_t, 1, -1, -1 );
                D3DXMatrixPerspectiveLH( &matrix, width_2d, height_2d, -rt->zn, -rt->zf );
                D3DXMatrixMultiply( &matrix, &matrix_t, &matrix );
                g_pD3DDevice->lpVtbl->SetTransform( g_pD3DDevice, D3DTS_PROJECTION, &matrix );
            }
            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_ZENABLE,D3DZB_TRUE );
            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_ZWRITEENABLE, TRUE );
            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_CULLMODE,D3DCULL_CCW);

            //フォグの設定
            if( rt->fog_enable == 1 )
            {
                g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_FOGENABLE, TRUE);
                g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_FOGCOLOR, rt->fog_color );
                g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_FOGVERTEXMODE, rt->fog_mode );
                if( rt->fog_mode == D3DFOG_LINEAR )
                {
                    g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_FOGSTART, *(DWORD*) (&rt->fog_start) );
                    g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_FOGEND, *(DWORD*) (&rt->fog_end) );
                }
                else
                {
                    g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_FOGDENSITY, *(DWORD*) (&rt->fog_density) );
                }
                if( rt->fog_range == 1 )
                {
                    g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_RANGEFOGENABLE, TRUE );
                }
                else
                {
                    g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_RANGEFOGENABLE, FALSE );
                }
            }
        }

        /* 3D描画 */
        for( m = 0; m < rt->MeshCount; m++ )
        {
            struct DXRubySprite_drawMesh *temp = (struct DXRubySprite_drawMesh *)rt->MeshList[m].sprite;
            if( rt->useview == 0 && rt->useproj == 0 )
            {
                D3DMATRIX matrix_t;
                D3DXMatrixTranslation( &matrix_t, 0, 0, rt->zn );
                D3DXMatrixMultiply   ( &temp->matrix_world, &temp->matrix_world, &matrix_t );
            }
            temp->func( temp );
        }

        if( rt->SpriteCount > 0 )
        {
            D3DMATRIX matrix, matrix_t;
            int oldflag = 0;

            /* 3D空間への2D描画 */
            D3DXMatrixScaling    ( &matrix, 1, 1, 1 );
            g_pD3DDevice->lpVtbl->SetTransform( g_pD3DDevice, D3DTS_WORLDMATRIX(0), &matrix);
            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_CULLMODE,D3DCULL_NONE);
            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_ZWRITEENABLE, FALSE );

            for( ; i < rt->SpriteCount && rt->SpriteList[i].z < 0; i++ )
            {
                struct DXRubySprite_draw *temp = (struct DXRubySprite_draw *)rt->SpriteList[i].sprite;

                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 4:          /* 加算合成1の設定 */
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_ONE );
                        break;
                    case 5:          /* 加算合成2の設定 */
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_ONE );
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
                        break;
                    case 6:          /* 減算合成1の設定 */
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_ZERO );
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
                        break;
                    case 7:          /* 減算合成2の設定 */
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_ZERO );
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR );
                        break;
                    }
                }

                if( temp->z < 0 )
                {
                    if( rt->useview == 0 && rt->useproj == 0 )
                    {
                        temp->z = temp->z + rt->zn;
                    }
                }

                oldflag = temp->blendflag;
                temp->func( temp );
            }

            /* 2D描画 */
            D3DXMatrixScaling    ( &matrix, 1, -1, 1 );
            D3DXMatrixTranslation( &matrix_t, (float)-(width_2d)/2.0f, (float)(height_2d)/2.0f, 0 );
            D3DXMatrixMultiply( &matrix, &matrix, &matrix_t );
            g_pD3DDevice->lpVtbl->SetTransform( g_pD3DDevice, D3DTS_VIEW, &matrix );
            matrix._11 = 2.0f / width_2d;
            matrix._12 = matrix._13 = matrix._14 = 0;
            matrix._22 = 2.0f / height_2d;
            matrix._21 = matrix._23 = matrix._24 = 0;
            matrix._31 = matrix._32 = 0;matrix._33 = 0; matrix._34 = 0;
            matrix._41 = matrix._42 = 0;matrix._43 = 1;matrix._44 = 1;
            g_pD3DDevice->lpVtbl->SetTransform( g_pD3DDevice, D3DTS_PROJECTION, &matrix );

            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_ZENABLE,D3DZB_FALSE );
            g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_FOGENABLE, FALSE );

            for( ; i < rt->SpriteCount; i++ )
            {
                struct DXRubySprite_draw *temp = (struct DXRubySprite_draw *)rt->SpriteList[i].sprite;

                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 4:          /* 加算合成1の設定 */
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_ONE );
                        break;
                    case 5:          /* 加算合成2の設定 */
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_ONE );
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
                        break;
                    case 6:          /* 減算合成1の設定 */
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_ZERO );
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
                        break;
                    case 7:          /* 減算合成2の設定 */
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_SRCBLEND, D3DBLEND_ZERO );
                        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR );
                        break;
                    }
                }

                if( temp->z < 0 )
                {
                    if( rt->useview == 0 && rt->useproj == 0 )
                    {
                        temp->z = temp->z + rt->zn;
                    }
                }

                oldflag = temp->blendflag;
                temp->func( temp );
            }
        }

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

    if( vrt == Qnil )
    {
        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();
        }
    }

    rt->MeshCount = 0;
    rt->MeshSize = 0;
    rt->SpriteCount = 0;
    rt->SpriteSize = 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 /* する場合 */
                {
                    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
                    /* 今回はウェイトも描画もしない */
                    skip++;
                    rt->MeshCount = 0;
                    rt->MeshSize = 0;
                    rt->SpriteCount = 0;
                    rt->SpriteSize = 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 )
        {
            struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
            /* 今回はウェイトも描画もしない */
            skip++;
            rt->MeshCount = 0;
            rt->MeshSize = 0;
            rt->SpriteCount = 0;
            rt->SpriteSize = 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 klass, VALUE windowed )
{
    g_WindowInfo.windowed = windowed;

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

    return g_WindowInfo.windowed;
}


/*--------------------------------------------------------------------
   ウィンドウのサイズを変更する。
 ---------------------------------------------------------------------*/
static VALUE Window_resize( VALUE klass, VALUE vwidth, VALUE vheight )
{
    g_WindowInfo.width = NUM2INT( vwidth );
    g_WindowInfo.height = NUM2INT( vheight );
    if( g_WindowInfo.created == Qtrue )
    {
        g_WindowInfo.created = Qfalse;
        Window_create( klass );
    }
    return Qnil;
}


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


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

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


/*--------------------------------------------------------------------
   ウィンドウのサイズ（幅）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Window_setwidth( VALUE klass, 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 klass , 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 klass )
{
    return INT2FIX( g_WindowInfo.x );
}


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


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


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


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

    GetWindowText( g_hWnd, buf, 256 );

    return rb_str_new2( buf );
}


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

    SetWindowText( g_hWnd, RSTRING_PTR( caption ) );

    return caption;
}


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


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

    return scale;
}


/*--------------------------------------------------------------------
  zn設定
 ---------------------------------------------------------------------*/
static VALUE Window_set_zn( VALUE self, VALUE vzn )
{
    struct DXRubyRenderTarget *rt;

    if( self == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    }
    rt->zn = NUM2DBL( vzn );

    return vzn;
}


/*--------------------------------------------------------------------
  zn取得
 ---------------------------------------------------------------------*/
static VALUE Window_get_zn( VALUE self )
{
    struct DXRubyRenderTarget *rt;

    if( self == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    }

    return rb_float_new( rt->zn );
}


/*--------------------------------------------------------------------
  zf設定
 ---------------------------------------------------------------------*/
static VALUE Window_set_zf( VALUE self, VALUE vzf )
{
    struct DXRubyRenderTarget *rt;

    if( self == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    }

    rt->zf = NUM2DBL( vzf );

    return vzf;
}


/*--------------------------------------------------------------------
  zf取得
 ---------------------------------------------------------------------*/
static VALUE Window_get_zf( VALUE self )
{
    struct DXRubyRenderTarget *rt;

    if( self == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    }

    return rb_float_new( rt->zf );
}


/*--------------------------------------------------------------------
  ビュー変換行列を設定する
 ---------------------------------------------------------------------*/
static VALUE Window_set_view( VALUE self, VALUE vmatrix )
{
    struct DXRubyMatrix *mat;
    struct DXRubyRenderTarget *rt;

    if( self == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    }


    if( vmatrix == Qnil )
    {
        rt->useview = 0;
    }
    else
    {
        DXRUBY_CHECK_TYPE( Matrix, vmatrix );
        mat = DXRUBY_GET_STRUCT( Matrix, vmatrix );
        memcpy( &rt->matrix_view, mat->m, sizeof( float ) * 16 );
        rt->useview = 1;
    }

    return vmatrix;
}


/*--------------------------------------------------------------------
  ビュー変換行列を取得する
 ---------------------------------------------------------------------*/
static VALUE Window_get_view( VALUE self )
{
    struct DXRubyMatrix *result;
    VALUE vresult;
    struct DXRubyRenderTarget *rt;

    if( self == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    }


    vresult = Matrix_allocate( cMatrix );
    result = DXRUBY_GET_STRUCT( Matrix, vresult );

    memcpy( result->m, &rt->matrix_view, sizeof( float ) * 16 );

    return vresult;
}


/*--------------------------------------------------------------------
  射影変換行列を設定する
 ---------------------------------------------------------------------*/
static VALUE Window_set_proj( VALUE self, VALUE vmatrix )
{
    struct DXRubyMatrix *mat;
    struct DXRubyRenderTarget *rt;

    if( self == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    }


    if( vmatrix == Qnil )
    {
        rt->useproj = 0;
    }
    else
    {
        DXRUBY_CHECK_TYPE( Matrix, vmatrix );
        mat = DXRUBY_GET_STRUCT( Matrix, vmatrix );
        memcpy( &rt->matrix_proj, mat->m, sizeof( float ) * 16 );
        rt->useproj = 1;
    }

    return vmatrix;
}


/*--------------------------------------------------------------------
  射影変換行列を取得する
 ---------------------------------------------------------------------*/
static VALUE Window_get_proj( VALUE self )
{
    struct DXRubyMatrix *result;
    VALUE vresult;
    struct DXRubyRenderTarget *rt;

    if( self == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    }


    vresult = Matrix_allocate( cMatrix );
    result = DXRUBY_GET_STRUCT( Matrix, vresult );

    memcpy( result->m, &rt->matrix_proj, sizeof( float ) * 16 );

    return vresult;
}


/*--------------------------------------------------------------------
  フォグ設定
 ---------------------------------------------------------------------*/
static VALUE Window_set_fog( int argc, VALUE *argv, VALUE self )
{
    int col;
    struct DXRubyRenderTarget *rt;

    if( self == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    }

    if( argc == 0 ) rb_raise( eDXRubyError, "引数が足りません" );

    if( argv[0] == Qnil )
    {
        rt->fog_enable = 0;
    }
    else if( NUM2INT( argv[0] ) == D3DFOG_LINEAR )
    {
        if( argc < 4 ) rb_raise( eDXRubyError, "引数が足りません" );
        Check_Type(argv[1], T_ARRAY);
        col = array2color( argv[1] );
        rt->fog_enable = 1;
        rt->fog_color = col;
        rt->fog_mode = D3DFOG_LINEAR;
        rt->fog_start = NUM2DBL( argv[2] );
        rt->fog_end = NUM2DBL( argv[3] );
        rt->fog_range = argc == 4 ? FALSE : DXRUBY_IS_TRUE( argv[4] );
    }
    else
    {
        Check_Type(argv[1], T_ARRAY);
        col = array2color( argv[1] );
        rt->fog_enable = 1;
        rt->fog_color = col;
        rt->fog_mode = NUM2INT( argv[0] );
        rt->fog_density = NUM2DBL( argv[2] );
        rt->fog_range = argc == 3 ? FALSE : DXRUBY_IS_TRUE( argv[3] );
    }

    return Qnil;
}

/*--------------------------------------------------------------------
  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 )
{
    struct DXRubyRenderTarget *rt;

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

    return INT2FIX( rt->minfilter );
}


/*--------------------------------------------------------------------
   縮小フィルタ設定
 ---------------------------------------------------------------------*/
static VALUE Window_setMinFilter( VALUE obj, VALUE minfilter )
{
    struct DXRubyRenderTarget *rt;

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

    rt->minfilter = FIX2INT( minfilter );
    return minfilter;
}


/*--------------------------------------------------------------------
   拡大フィルタ取得
 ---------------------------------------------------------------------*/
static VALUE Window_getMagFilter( VALUE obj )
{
    struct DXRubyRenderTarget *rt;

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

    return INT2FIX( rt->minfilter );
}


/*--------------------------------------------------------------------
   拡大フィルタ設定
 ---------------------------------------------------------------------*/
static VALUE Window_setMagFilter( VALUE obj, VALUE magfilter )
{
    struct DXRubyRenderTarget *rt;

    if( obj == mWindow )
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    }
    else
    {
        rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    }

    rt->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_hWnd );
}


/*--------------------------------------------------------------------
  フレームスキップ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_get_active( VALUE obj )
{
    return ( g_WindowInfo.active != 0 ? Qtrue : Qfalse );
}


/*--------------------------------------------------------------------
  ウィンドウアイコンを設定する
 ---------------------------------------------------------------------*/
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_hWnd, 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_hWnd;

	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_hWnd;

	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 )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );

    /* アイコンリソース解放 */
    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 );
    }

    /* リスト解放 */
    free( rt->MeshList );
    free( rt->MeshStruct );
    free( rt->SpriteList );
    free( rt->SpriteStruct );

//    /* 動画再生用インターフェイスの解放 */
//    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_hWnd  , &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_hWnd, &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_hWnd );
//    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;
//}


/*********************************************************************
 * RenderTargetクラス
 *
 * レンダーターゲットになれるImageクラス。
 * 編集機能は無いが、Window.updateで画像を作ることができる。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void RenderTarget_free( struct DXRubyRenderTarget *rt )
{
    RELEASE( rt->surface );
    if( rt->texture != NULL )
    {
        RELEASE( rt->texture->pD3DTexture );
        free( rt->texture );
        rt->texture = NULL;
    };
    RELEASE( rt->zbuffer );
    free( rt->MeshList );
    free( rt->MeshStruct );
    free( rt->SpriteList );
    free( rt->SpriteStruct );
    DeleteRenderTargetList( rt );
}

static void RenderTarget_release( struct DXRubyRenderTarget *rt )
{
    /* テクスチャオブジェクトの開放 */
    if( rt->texture )
    {
        RenderTarget_free( rt );
    }
    free( rt );
    rt = NULL;

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

/*--------------------------------------------------------------------
   RenderTargetクラスのdispose。
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_dispose( VALUE self )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    DXRUBY_CHECK_DISPOSE( rt, texture );
    RenderTarget_free( rt );
    return self;
}

/*--------------------------------------------------------------------
   RenderTargetクラスのmark
 ---------------------------------------------------------------------*/
static void RenderTarget_mark( struct DXRubyRenderTarget *rt )
{
    int i;

    for( i = 0; i < rt->MeshCount; i++ )
    {
        rb_gc_mark( rt->MeshList[i].sprite->value );
    }
    for( i = 0; i < rt->SpriteCount; i++ )
    {
        rb_gc_mark( rt->SpriteList[i].sprite->value );
    }
}

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

    /* DXRubyRenderTargetのメモリ取得＆RenderTargetオブジェクト生成 */
    rt = malloc(sizeof(struct DXRubyRenderTarget));
    if( rt == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - RenderTarget_allocate" );
    obj = Data_Wrap_Struct(klass, RenderTarget_mark, RenderTarget_release, rt);

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

    /* スプライト構造体の初期値設定 */
    rt->MeshCount = 0;
    rt->MeshAllocateCount = 128;
    rt->MeshSize = 0;
    rt->MeshAllocateSize = 128*32;
    rt->MeshList = malloc( rt->MeshAllocateCount * sizeof(struct DXRubySpriteList) );
    rt->MeshStruct = malloc( rt->MeshAllocateSize );
    rt->SpriteCount = 0;
    rt->SpriteAllocateCount = 128;
    rt->SpriteSize = 0;
    rt->SpriteAllocateSize = 128*32;
    rt->SpriteList = malloc( rt->SpriteAllocateCount * sizeof(struct DXRubySpriteList) );
    rt->SpriteStruct = malloc( rt->SpriteAllocateSize );

    rt->minfilter = D3DTEXF_LINEAR;
    rt->magfilter = D3DTEXF_LINEAR;
    rt->zn = -1000;
    rt->zf = -60000;
    rt->useview = 0;
    rt->useproj = 0;
    rt->fog_enable = 0;

    rt->a = 0;
    rt->r = 0;
    rt->g = 0;
    rt->b = 0;

    return obj;
}


/*--------------------------------------------------------------------
   RenderTargetクラスのInitialize
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_initialize( int argc, VALUE *argv, VALUE self )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    struct DXRubyTexture *texture;
    HRESULT hr;
    D3DSURFACE_DESC desc;
    VALUE vwidth, vheight, vary;
    int col = 0, width, height;

    g_iRefAll++;

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

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

    if( width <= 0 || height <= 0 )
    {
        rb_raise( eDXRubyError, "RenderTargetオブジェクトの作成に失敗しました - RenderTarget_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, "画像用メモリの取得に失敗しました - RenderTarget_initialize" );
    }

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

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

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

    /* Zバッファサーフェイスを作成する */
    g_pD3DDevice->lpVtbl->CreateDepthStencilSurface( g_pD3DDevice, 
                                                     texture->width, texture->height, 
                                                     D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, FALSE, &rt->zbuffer, NULL);

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

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

    AddRenderTargetList( rt );

    g_pD3DDevice->lpVtbl->SetRenderTarget( g_pD3DDevice, 0, rt->surface );
    g_pD3DDevice->lpVtbl->SetDepthStencilSurface( g_pD3DDevice, rt->zbuffer );
    g_pD3DDevice->lpVtbl->Clear( g_pD3DDevice, 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
                                 D3DCOLOR_ARGB( 0,0,0,0 ), 1.0f, 0 );

    return self;
}


/*--------------------------------------------------------------------
   RenderTargetクラスのImageオブジェクト化
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_to_image( VALUE self )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    VALUE vimage;
    struct DXRubyImage *image;
    LPDIRECT3DTEXTURE9 pD3DTexture;
    VALUE ary[2];
    IDirect3DSurface9 *surface;
    D3DLOCKED_RECT srctrect;
    D3DLOCKED_RECT dsttrect;
    int i, j;
    RECT srect;
    RECT drect;
    HRESULT hr;
    int *psrc;
    int *pdst;
    D3DSURFACE_DESC desc;

    DXRUBY_CHECK_DISPOSE( rt, texture );

    vimage = Image_allocate( cImage );
    ary[0] = INT2FIX( rt->width );
    ary[1] = INT2FIX( rt->height );
    Image_initialize( 2, ary, vimage );
    image = DXRUBY_GET_STRUCT( Image, vimage );

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

    /* テクスチャのサーフェイスを取得 */
    hr = pD3DTexture->lpVtbl->GetSurfaceLevel( pD3DTexture, 0, &surface );
    if( FAILED( hr ) ) rb_raise( eDXRubyError, "サーフェイスの作成に失敗しました - RenderTarget_to_image" );

    /* レンダーターゲットのイメージ取得 */
    hr = g_pD3DDevice->lpVtbl->GetRenderTargetData( g_pD3DDevice, rt->surface, surface );
    if( FAILED( hr ) ) rb_raise( eDXRubyError, "イメージの取得に失敗しました - RenderTarget_to_image" );

    /* イメージのコピー */
    srect.left = rt->x;
    srect.top = rt->y;
    srect.right = rt->width;
    srect.bottom = rt->height;
    drect.left = 0;
    drect.top = 0;
    drect.right = rt->width;
    drect.bottom = rt->height;

    hr = pD3DTexture->lpVtbl->LockRect( pD3DTexture, 0, &srctrect, &srect, 0 );
    hr = image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &dsttrect, &drect, D3DLOCK_READONLY );

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

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

    RELEASE( surface );
    RELEASE( pD3DTexture );

    return vimage;
}


/*--------------------------------------------------------------------
   レンダーターゲットの開始位置xを返す。
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_getX( VALUE self )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    DXRUBY_CHECK_DISPOSE( rt, texture );
    return INT2FIX( rt->x );
}


/*--------------------------------------------------------------------
   レンダーターゲットの開始位置yを返す。
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_getY( VALUE self )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    DXRUBY_CHECK_DISPOSE( rt, texture );
    return INT2FIX( rt->y );
}


/*--------------------------------------------------------------------
   レンダーターゲットのサイズ（幅）を返す。
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_getWidth( VALUE self )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    DXRUBY_CHECK_DISPOSE( rt, texture );
    return INT2FIX( rt->width );
}


/*--------------------------------------------------------------------
   レンダーターゲットのサイズ（高さ）を返す。
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_getHeight( VALUE self )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    DXRUBY_CHECK_DISPOSE( rt, texture );
    return INT2FIX( rt->height );
}


/*--------------------------------------------------------------------
   レンダーターゲットの開始位置xを設定する。
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_setX( VALUE self, VALUE vx )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    DXRUBY_CHECK_DISPOSE( rt, texture );
    rt->x = NUM2INT( vx );
    return vx;
}


/*--------------------------------------------------------------------
   レンダーターゲットの開始位置yを設定する。
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_setY( VALUE self, VALUE vy )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    DXRUBY_CHECK_DISPOSE( rt, texture );
    rt->y = NUM2INT( vy );
    return vy;
}


/*--------------------------------------------------------------------
   レンダーターゲットのサイズ（幅）を設定する。
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_setWidth( VALUE self, VALUE vwidth )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    DXRUBY_CHECK_DISPOSE( rt, texture );
    rt->width = NUM2INT( vwidth );
    return vwidth;
}


/*--------------------------------------------------------------------
   レンダーターゲットのサイズ（高さ）を設定する。
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_setHeight( VALUE self, VALUE vheight )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    DXRUBY_CHECK_DISPOSE( rt, texture );
    rt->height = NUM2INT( vheight );
    return vheight;
}


/*--------------------------------------------------------------------
   メッシュリストのメモリ確保
 ---------------------------------------------------------------------*/
static void *RenderTarget_AllocMeshList( struct DXRubyRenderTarget *rt, int size )
{
    void* result = rt->MeshStruct + rt->MeshSize;
    int i;

    rt->MeshSize += size;

    if( rt->MeshSize > rt->MeshAllocateSize )
    {
        char *temp = rt->MeshStruct;
        rt->MeshAllocateSize = rt->MeshAllocateSize * 3 / 2; /* 1.5倍にする */
        rt->MeshStruct = realloc( rt->MeshStruct, rt->MeshAllocateSize );
        for( i = 0; i < rt->MeshCount; i++)
        {
            rt->MeshList[i].sprite = (struct DXRubySprite *)((char *)rt->MeshList[i].sprite + (rt->MeshStruct - temp));
        }
        result = rt->MeshStruct + rt->MeshSize - size;
    }

    if( rt->MeshCount >= rt->MeshAllocateCount )
    {
        rt->MeshAllocateCount = rt->MeshAllocateCount * 3 / 2; /* 1.5倍にする */
        rt->MeshList = realloc( rt->MeshList, rt->MeshAllocateCount * sizeof(struct DXRubySpriteList) );
    }

    return result;
}


/*--------------------------------------------------------------------
   スプライトリストのメモリ確保
 ---------------------------------------------------------------------*/
static void *RenderTarget_AllocSpriteList( struct DXRubyRenderTarget *rt, int size )
{
    void* result = rt->SpriteStruct + rt->SpriteSize;
    int i;

    rt->SpriteSize += size;

    if( rt->SpriteSize > rt->SpriteAllocateSize )
    {
        char *temp = rt->SpriteStruct;
        rt->SpriteAllocateSize = rt->SpriteAllocateSize * 3 / 2; /* 1.5倍にする */
        rt->SpriteStruct = realloc( rt->SpriteStruct, rt->SpriteAllocateSize );
        for( i = 0; i < rt->SpriteCount; i++)
        {
            rt->SpriteList[i].sprite = (struct DXRubySprite *)((char *)rt->SpriteList[i].sprite + (rt->SpriteStruct - temp));
        }
        result = rt->SpriteStruct + rt->SpriteSize - size;
    }

    if( rt->SpriteCount >= rt->SpriteAllocateCount )
    {
        rt->SpriteAllocateCount = rt->SpriteAllocateCount * 3 / 2; /* 1.5倍にする */
        rt->SpriteList = realloc( rt->SpriteList, rt->SpriteAllocateCount * sizeof(struct DXRubySpriteList) );
    }

    return result;
}


/* マージソート */
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 RenderTarget_SortMeshList( struct DXRubyRenderTarget *rt )
{
    struct DXRubySpriteList *temp;
    int i;

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


void RenderTarget_SortSpriteList( struct DXRubyRenderTarget *rt )
{
    struct DXRubySpriteList *temp;
    int i;

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



/*********************************************************************
 * Shaderクラス
 *
 * シェーダーを管理する。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void Shader_free( struct DXRubyShader *shader)
{
    if( shader->handle )
    {
        free( shader->handle );
        shader->handle = NULL;
    }
    RELEASE( shader->pD3DXEffect );
    DeleteShaderList( shader );
}
static void Shader_release( struct DXRubyShader *shader )
{
    if( shader->pD3DXEffect )
    {
        Shader_free( shader );
    }
    free( shader );
}

/*--------------------------------------------------------------------
   GCから呼ばれるマーク関数
 ---------------------------------------------------------------------*/
static void Shader_mark( struct DXRubyShader* shader )
{
    if( shader->handle )
    {
        int i;
        for( i = 0; i < shader->handle_count; i++ )
        {
            rb_gc_mark( shader->handle[i].obj );
        }
    }
}

/*--------------------------------------------------------------------
   Shaderクラスのdispose。
 ---------------------------------------------------------------------*/
static VALUE Shader_dispose( VALUE self )
{
    struct DXRubyShader *shader = DXRUBY_GET_STRUCT( Shader, self );
    DXRUBY_CHECK_DISPOSE( shader, pD3DXEffect );
    Shader_free( shader );
    return self;
}

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

    /* DXRubyShaderのメモリ取得＆Shaderオブジェクト生成 */
    shader = malloc( sizeof( struct DXRubyShader ) );
    if( shader == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Shader_allocate" );
    obj = Data_Wrap_Struct( klass, Shader_mark, Shader_release, shader );

    /* とりあえず各オブジェクトはNULLにしておく */
    shader->pD3DXEffect = NULL;
    shader->technique = NULL;
    shader->handle_count = 0;
    shader->handle = NULL;
    shader->pass_number = 0;
    shader->pass_count = 1;

    return obj;
}


/*--------------------------------------------------------------------
   ShaderクラスのInitialize
 ---------------------------------------------------------------------*/
static VALUE Shader_initialize( VALUE self, VALUE vhlsl, VALUE vstring )
{
    struct DXRubyShader *shader = DXRUBY_GET_STRUCT( Shader, self );
    LPD3DXBUFFER pErr=NULL;

    Check_Type( vhlsl, T_STRING );
    Check_Type( vstring, T_STRING );

    if( FAILED( D3DXCreateEffect(
        g_pD3DDevice, RSTRING_PTR( vhlsl ), RSTRING_LEN( vhlsl ), NULL, NULL,
        0 , NULL, &shader->pD3DXEffect, &pErr )))
    {
        // シェーダの読み込みの失敗
        rb_raise( eDXRubyError, pErr->lpVtbl->GetBufferPointer( pErr ) );
    }
    RELEASE( pErr );

    shader->technique = shader->pD3DXEffect->lpVtbl->GetTechniqueByName( shader->pD3DXEffect, RSTRING_PTR( vstring ) );

    AddShaderList( shader );
    return self;
}

static VALUE Shader_set_value( VALUE self, VALUE sym, VALUE obj )
{
    struct DXRubyShader *shader = DXRUBY_GET_STRUCT( Shader, self );
    int i;
    ID id;
    VALUE param = obj;

    DXRUBY_CHECK_DISPOSE( shader, pD3DXEffect );

    id = SYMBOL_P(sym) ? SYM2ID(sym) : rb_to_id(sym);

    if( FIXNUM_P( param ) )
    {
        param = rb_float_new( NUM2INT( param ) );
    }

    // 型チェック
    if( TYPE( param ) != T_FLOAT )  // FloatはOK
    {
        if( TYPE( param ) == T_ARRAY ) // Arrayの場合
        {
            if( RARRAY_LEN( param ) == 0 )
            {
                rb_raise(rb_eTypeError, "配列の場合は要素は必須です");
            }
            for( i = 0; i < RARRAY_LEN( param ); i++ )
            {
                if( TYPE( RARRAY_PTR( param )[i] ) != T_FLOAT )  // FloatはOK
                {
                    if( TYPE( RARRAY_PTR( param )[i] ) == T_DATA && RDATA( RARRAY_PTR( param )[i] )->dfree != (RUBY_DATA_FUNC)Vector_release    // VectorはOK
                                                                 && RDATA( RARRAY_PTR( param )[i] )->dfree != (RUBY_DATA_FUNC)Matrix_release )  // MatrixはOK
                    {
                        rb_raise(rb_eTypeError, "wrong argument type %s (expected Float or DXRuby::Vector or DXRuby::Matrix)", rb_obj_classname( RARRAY_PTR( param )[i] ));
                    }
                }
            }
            for( i = 1; i < RARRAY_LEN( param ); i++ )
            {
                if( TYPE( RARRAY_PTR( param )[0] ) != TYPE( RARRAY_PTR( param )[i] ) )
                {
                    rb_raise(rb_eTypeError, "配列の中身の要素は統一されている必要があります");
                }
            }
            param = rb_ary_new();
            for( i = 0; i < RARRAY_LEN( obj ); i++ )
            {
                rb_ary_push( param, RARRAY_PTR( obj )[i] );
            }
        }
        else if( TYPE( param ) == T_DATA && RDATA( param )->dfree != (RUBY_DATA_FUNC)Vector_release     // VectorはOK
                                    && RDATA( param )->dfree != (RUBY_DATA_FUNC)Matrix_release )  // MatrixはOK
        {
            rb_raise(rb_eTypeError, "wrong argument type %s (expected Float or DXRuby::Vector or DXRuby::Matrix)", rb_obj_classname( param ));
        }
    }

    if( shader->handle_count == 0 )
    {
        shader->handle = (struct DXRubyShaderHandle *)malloc( sizeof( struct DXRubyShaderHandle ) );
        shader->handle[0].id = id;
        shader->handle[0].D3DXHandle = shader->pD3DXEffect->lpVtbl->GetParameterByName( shader->pD3DXEffect, NULL, rb_id2name( id ) );
        shader->handle[0].obj = param;
        shader->handle_count = 1;

        return self;
    }

    for( i = 0; i < shader->handle_count; i++ )
    {
        if( shader->handle[i].id == id )
        {
            break;
        }
    }
    if( i == shader->handle_count ) /* なかった */
    {
        shader->handle_count++;
        shader->handle = realloc( shader->handle, sizeof( struct DXRubyShaderHandle ) * shader->handle_count );
        shader->handle[i].id = id;
        shader->handle[i].D3DXHandle = shader->pD3DXEffect->lpVtbl->GetParameterByName( shader->pD3DXEffect, NULL, rb_id2name( shader->handle[i].id ) );
    }

    shader->handle[i].obj = param;

    return self;
}


/*********************************************************************
 * Polygonクラス
 *
 * 1枚のポリゴンを表現する。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void Polygon_release( struct DXRubyPolygon *poly )
{
    free( poly );
}

/*--------------------------------------------------------------------
   Polygonクラスのallocate。メモリを確保する為にinitialize前に呼ばれる。
 ---------------------------------------------------------------------*/
static VALUE Polygon_allocate( VALUE klass )
{
    VALUE obj;
    struct DXRubyPolygon *poly;
    int i;

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

    /* とりあえず各オブジェクトはNULLにしておく */
    for( i = 0; i < 3; i++ )
    {
        poly->p[i].x = poly->p[i].y = poly->p[i].z = 0.0f;
        poly->p[i].color = 0;
        poly->p[i].tu = poly->p[i].tv = 0;
    }

    return obj;
}

static VALUE Polygon_set_vertex( VALUE self, VALUE ary )
{
    struct DXRubyPolygon *poly = DXRUBY_GET_STRUCT( Polygon, self );
    int i;

    Check_Type( ary, T_ARRAY );

    if( RARRAY_LEN( ary ) < 6 ) rb_raise( eDXRubyError, "要素が足りません - Polygon#vertex=" );

    for( i = 0; i < 3; i++ )
    {
        poly->p[i].x = NUM2DBL( RARRAY_PTR( ary )[i * 3] );
        poly->p[i].y = NUM2DBL( RARRAY_PTR( ary )[i * 3 + 1] );
        poly->p[i].z = NUM2DBL( RARRAY_PTR( ary )[i * 3 + 2] );
    }

    return ary;
}

static VALUE Polygon_set_tutv( VALUE self, VALUE ary )
{
    struct DXRubyPolygon *poly = DXRUBY_GET_STRUCT( Polygon, self );
    int i;

    Check_Type( ary, T_ARRAY );

    if( RARRAY_LEN( ary ) < 4 ) rb_raise( eDXRubyError, "要素が足りません - Polygon#tutv=" );

    for( i = 0; i < 3; i++ )
    {
        poly->p[i].tu = NUM2DBL( RARRAY_PTR( ary )[i * 2] );
        poly->p[i].tv = NUM2DBL( RARRAY_PTR( ary )[i * 2 + 1] );
    }

    return ary;
}

static VALUE Polygon_set_diffuse( VALUE self, VALUE ary )
{
    struct DXRubyPolygon *poly = DXRUBY_GET_STRUCT( Polygon, self );
    int i;

    Check_Type( ary, T_ARRAY );

    if( RARRAY_LEN( ary ) < 4 ) rb_raise( eDXRubyError, "要素が足りません - Polygon#diffuse=" );

    for( i = 0; i < 3; i++ )
    {
        poly->p[i].color = D3DCOLOR_ARGB( NUM2INT( RARRAY_PTR( ary )[i] ),
                                          NUM2INT( RARRAY_PTR( ary )[i * 4 + 1] ),
                                          NUM2INT( RARRAY_PTR( ary )[i * 4 + 2] ),
                                          NUM2INT( RARRAY_PTR( ary )[i * 4 + 3] ) );
    }

    return ary;
}


/*--------------------------------------------------------------------
   PolygonクラスのInitialize
 ---------------------------------------------------------------------*/
static VALUE Polygon_initialize( VALUE self )
{
    return self;
}


/*********************************************************************
 * Meshクラス
 *
 * 3Dのxファイルを読み込んで作る。
 * Window.drawMeshでまとめて描画できる。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void Mesh_free( struct DXRubyMesh *mesh )
{
    /* メッシュオブジェクトの開放 */
    if( mesh->pD3DMaterials )
    {
        free( mesh->pD3DMaterials );
        mesh->pD3DMaterials = NULL;
    }

    if( mesh->texture )
    {
        mesh->texture->refcount--;
        if( mesh->texture->refcount == 0 )
        {
            RELEASE( mesh->texture->pD3DTexture );
            free( mesh->texture );
            mesh->texture = NULL;
        }
    }
    else
    {
        int i;
        for( i = 0; i < mesh->materials; i++ )
        {
            RELEASE( mesh->ppD3DTextures[i] );
        }
        free( mesh->ppD3DTextures );
        mesh->ppD3DTextures = NULL;
    }
}
static void Mesh_release( struct DXRubyMesh *mesh )
{
    if( mesh->ppD3DTextures )
    {
        Mesh_free( mesh );
    }
    free( mesh );
    mesh = NULL;

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

/*--------------------------------------------------------------------
   Meshクラスのdispose。
 ---------------------------------------------------------------------*/
static VALUE Mesh_dispose( VALUE self )
{
    struct DXRubyMesh *mesh = DXRUBY_GET_STRUCT( Mesh, self );
    DXRUBY_CHECK_DISPOSE( mesh, ppD3DTextures );
    Mesh_free( mesh );
    return self;
}

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

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

    /* とりあえず各オブジェクトはNULLにしておく */
    mesh->pD3DXMesh = NULL;
    mesh->pD3DMaterials = NULL;
    mesh->ppD3DTextures = NULL;
    mesh->materials = 0;
    mesh->texture = NULL;

    return obj;
}


/*--------------------------------------------------------------------
   MeshクラスのInitialize
 ---------------------------------------------------------------------*/
static VALUE Mesh_initialize( int argc, VALUE *argv, VALUE self )
{
    struct DXRubyMesh *mesh;
    LPD3DXMESH pMesh;
    LPD3DXBUFFER pD3DXMaterialBuffer;
    D3DXMATERIAL* pD3DXMaterials;
    int i;

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

    mesh = DXRUBY_GET_STRUCT( Mesh, self );

    if( argc == 1 && TYPE( argv[0] ) == T_STRING )
    {
        DWORD *pAdjacency = NULL;
        HRESULT hr;

        /* ファイルのロード */
        if( FAILED( D3DXLoadMeshFromX( RSTRING_PTR( argv[0] ), D3DXMESH_MANAGED, g_pD3DDevice,
                                       0, &pD3DXMaterialBuffer, 0, &mesh->materials, &mesh->pD3DXMesh ) ) )
        {
            rb_raise( eDXRubyError, "ファイルの読み込みに失敗しました - %s", RSTRING_PTR( argv[0] ) );
        }
        pAdjacency = alloca( sizeof(DWORD) * mesh->pD3DXMesh->lpVtbl->GetNumFaces( mesh->pD3DXMesh ) * 3 );

        // 隣接面データ用
        hr = mesh->pD3DXMesh->lpVtbl->GenerateAdjacency( mesh->pD3DXMesh, 1e-6f, pAdjacency );
        if( FAILED( hr ) ) rb_raise( eDXRubyError, "メッシュの生成に失敗しました - %s", RSTRING_PTR( argv[0] ) );
        hr = mesh->pD3DXMesh->lpVtbl->OptimizeInplace( mesh->pD3DXMesh, D3DXMESHOPT_COMPACT, pAdjacency, NULL, NULL, NULL );
        if( FAILED( hr ) ) rb_raise( eDXRubyError, "メッシュの生成に失敗しました" );
        // 属性テーブル最適化
        hr = mesh->pD3DXMesh->lpVtbl->OptimizeInplace( mesh->pD3DXMesh, D3DXMESHOPT_ATTRSORT, pAdjacency, NULL, NULL, NULL );
        if( FAILED( hr ) ) rb_raise( eDXRubyError, "メッシュの生成に失敗しました - %s", RSTRING_PTR( argv[0] ) );
        // 頂点キャッシュ最適化
        hr = mesh->pD3DXMesh->lpVtbl->OptimizeInplace( mesh->pD3DXMesh, D3DXMESHOPT_VERTEXCACHE, pAdjacency, NULL, NULL, NULL );
        if( FAILED( hr ) ) rb_raise( eDXRubyError, "メッシュの生成に失敗しました - %s", RSTRING_PTR( argv[0] ) );

        pD3DXMaterials = (D3DXMATERIAL*)pD3DXMaterialBuffer->lpVtbl->GetBufferPointer( pD3DXMaterialBuffer );
        mesh->pD3DMaterials = malloc( sizeof( D3DMATERIAL9 ) * mesh->materials );
        mesh->ppD3DTextures = malloc( sizeof( LPDIRECT3DTEXTURE9 ) * mesh->materials );

        for( i = 0; i < mesh->materials; i++ )
        {
            mesh->pD3DMaterials[i]= pD3DXMaterials[i].MatD3D;
            mesh->ppD3DTextures[i] = NULL;
            if( pD3DXMaterials[i].pTextureFilename != NULL )
            {
                if( FAILED( D3DXCreateTextureFromFileEx( g_pD3DDevice, pD3DXMaterials[i].pTextureFilename, 0,
                                             0, 0, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_FILTER_LINEAR,
                                             D3DX_FILTER_LINEAR, D3DCOLOR_XRGB(0,0,0), NULL, NULL, &mesh->ppD3DTextures[i] ) ) )
                {
                    rb_raise( eDXRubyError, "ファイルの読み込みに失敗しました - %s", pD3DXMaterials[i].pTextureFilename );
                }
            }
        }
        RELEASE( pD3DXMaterialBuffer );
    }
    else if( argc == 2 && TYPE( argv[0] ) == T_ARRAY )
    {
        struct tagVertex {
            float x, y, z;
            float tu, tv;
        } *pVertex;
        WORD *pIndex;
        struct DXRubyImage *image;
        D3DCOLORVALUE c;

        DXRUBY_CHECK_IMAGE( argv[1] );
        image = DXRUBY_GET_STRUCT( Image, argv[1] );
        DXRUBY_CHECK_DISPOSE( image, texture );

        if( FAILED( D3DXCreateMeshFVF( RARRAY_LEN( argv[0] ), RARRAY_LEN( argv[0] ) * 3, D3DXMESH_MANAGED,
                                       D3DFVF_XYZ | D3DFVF_TEX1, g_pD3DDevice, &mesh->pD3DXMesh ) ) )
        {
            rb_raise( eDXRubyError, "メッシュの作成に失敗しました - Mesh_initialize");
        }

        mesh->pD3DXMesh->lpVtbl->LockVertexBuffer( mesh->pD3DXMesh, 0, (void**)&pVertex );   /* 頂点バッファをロック */

        /* 頂点情報の書き込み */
        for( i = 0; i < RARRAY_LEN( argv[0] ); i++)
        {
            struct DXRubyPolygon *poly;
            DXRUBY_CHECK_TYPE( Polygon, RARRAY_PTR( argv[0] )[i] );
            poly = DXRUBY_GET_STRUCT( Polygon, RARRAY_PTR( argv[0] )[i] );
            pVertex[i*3].x = poly->p[0].x;
            pVertex[i*3].y = poly->p[0].y;
            pVertex[i*3].z = poly->p[0].z;
            pVertex[i*3].tu = (float)(image->x + poly->p[0].tu + 0.5f) / image->texture->width;
            pVertex[i*3].tv = (float)(image->y + poly->p[0].tv + 0.5f) / image->texture->height;
            pVertex[i*3+1].x = poly->p[1].x;
            pVertex[i*3+1].y = poly->p[1].y;
            pVertex[i*3+1].z = poly->p[1].z;
            pVertex[i*3+1].tu = (float)(image->x + poly->p[1].tu + 0.5f) / image->texture->width;
            pVertex[i*3+1].tv = (float)(image->y + poly->p[1].tv + 0.5f) / image->texture->height;
            pVertex[i*3+2].x = poly->p[2].x;
            pVertex[i*3+2].y = poly->p[2].y;
            pVertex[i*3+2].z = poly->p[2].z;
            pVertex[i*3+2].tu = (float)(image->x + poly->p[2].tu + 0.5f) / image->texture->width;
            pVertex[i*3+2].tv = (float)(image->y + poly->p[2].tv + 0.5f) / image->texture->height;
//            pVertex[i].color = poly->p[i].color;
        }

        mesh->pD3DXMesh->lpVtbl->UnlockVertexBuffer( mesh->pD3DXMesh );

        mesh->pD3DXMesh->lpVtbl->LockIndexBuffer( mesh->pD3DXMesh, 0, (void**)&pIndex );   /* インデックスバッファをロック */

        /* 頂点情報の書き込み */
        for( i = 0; i < RARRAY_LEN( argv[0] ) * 3; i++)
        {
            pIndex[i] = i;
        }

        mesh->pD3DXMesh->lpVtbl->UnlockIndexBuffer( mesh->pD3DXMesh );

        {
            DWORD *pAdjacency = NULL;
            HRESULT hr;
            pAdjacency = alloca( sizeof(DWORD) * mesh->pD3DXMesh->lpVtbl->GetNumFaces( mesh->pD3DXMesh ) * 3 );

            // 隣接面データ用
            hr = mesh->pD3DXMesh->lpVtbl->GenerateAdjacency( mesh->pD3DXMesh, 1e-6f, pAdjacency );
            if( FAILED( hr ) ) rb_raise( eDXRubyError, "メッシュの生成に失敗しました" );
            hr = mesh->pD3DXMesh->lpVtbl->OptimizeInplace( mesh->pD3DXMesh, D3DXMESHOPT_COMPACT, pAdjacency, NULL, NULL, NULL );
            if( FAILED( hr ) ) rb_raise( eDXRubyError, "メッシュの生成に失敗しました" );
            // 属性テーブル最適化
            hr = mesh->pD3DXMesh->lpVtbl->OptimizeInplace( mesh->pD3DXMesh, D3DXMESHOPT_ATTRSORT, pAdjacency, NULL, NULL, NULL );
            if( FAILED( hr ) ) rb_raise( eDXRubyError, "メッシュの生成に失敗しました" );
            // 頂点キャッシュ最適化
            hr = mesh->pD3DXMesh->lpVtbl->OptimizeInplace( mesh->pD3DXMesh, D3DXMESHOPT_VERTEXCACHE, pAdjacency, NULL, NULL, NULL );
            if( FAILED( hr ) ) rb_raise( eDXRubyError, "メッシュの生成に失敗しました" );
        }

        mesh->materials = 1;

        mesh->pD3DMaterials = malloc( sizeof( D3DMATERIAL9 ) );
        mesh->ppD3DTextures = malloc( sizeof( LPDIRECT3DTEXTURE9 ) );
        c.r = c.g = c.b = c.a = 1.0f;
        mesh->pD3DMaterials[0].Diffuse = c;
        mesh->pD3DMaterials[0].Ambient = c;
        c.r = c.g = c.b = c.a = 1.0f;
        mesh->pD3DMaterials[0].Specular = c;
        mesh->pD3DMaterials[0].Emissive = c;
        mesh->pD3DMaterials[0].Power = 1.0;
//        mesh->ppD3DTextures[0] = NULL;
        mesh->ppD3DTextures[0] = image->texture->pD3DTexture;
        mesh->texture = image->texture;
        image->texture->refcount++;
    }
    else
    {
        rb_raise( eDXRubyError, "引数がおかしい" );
    }

    g_iRefAll++;

    return self;
}

/*--------------------------------------------------------------------
   MeshクラスのcreateTeapot
 ---------------------------------------------------------------------*/
static VALUE Mesh_create_teapot( VALUE klass )
{
    VALUE obj = Mesh_allocate( klass );
    struct DXRubyMesh *mesh = DXRUBY_GET_STRUCT( Mesh, obj );
    LPD3DXBUFFER pD3DXMaterialBuffer;
    D3DXMATERIAL* pD3DXMaterials;

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

    if( FAILED( D3DXCreateTeapot( g_pD3DDevice, &mesh->pD3DXMesh, &pD3DXMaterialBuffer ) ) )
    {
        rb_raise( eDXRubyError, "ティーポットの生成に失敗しました" );
    }

    mesh->materials = 1;
    pD3DXMaterials = (D3DXMATERIAL*)pD3DXMaterialBuffer->lpVtbl->GetBufferPointer( pD3DXMaterialBuffer );
    mesh->pD3DMaterials = malloc( sizeof( D3DMATERIAL9 ) );
    mesh->ppD3DTextures = malloc( sizeof( LPDIRECT3DTEXTURE9 ) );

    mesh->pD3DMaterials[0]= pD3DXMaterials[0].MatD3D;
    mesh->ppD3DTextures[0] = NULL;
    RELEASE( pD3DXMaterialBuffer );

    g_iRefAll++;

    return obj;
}


/*********************************************************************
 * Matrixクラス
 *
 * 行列を表現する。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void Matrix_release( struct DXRubyMatrix* mat )
{
    mat->x = 1; /* 最適化抑止 */
    free( mat );
}

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

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

    mat->x = 0;
    mat->y = 0;
    ZeroMemory( mat->m, sizeof( float ) * 16 );

    return obj;
}

/*--------------------------------------------------------------------
   MatrixクラスのInitialize
 ---------------------------------------------------------------------*/
static VALUE Matrix_initialize( int argc, VALUE *argv, VALUE self )
{
    struct DXRubyMatrix *mat = DXRUBY_GET_STRUCT( Matrix, self );
    VALUE ary, *ary_p;
    int i, j, count;

    if( argc == 0 )
    {
        mat->m11 = 1;mat->m22 = 1;mat->m33 = 1;mat->m44 = 1;
        mat->x = mat->y = 4;
        return self;
    }

    if( argc == 1 )
    {
        ary = argv[0];
        Check_Type( ary, T_ARRAY );
        if( RARRAY_LEN( ary ) > 4 || RARRAY_LEN( ary ) < 1 ) rb_raise( eDXRubyError, "配列の数が正しくありません。 - Matrix_initialize");
        ary_p = RARRAY_PTR( ary );
        count = RARRAY_LEN( ary );
    }
    else
    {
        if( argc > 4 ) rb_raise( eDXRubyError, "引数の数が正しくありません。 - Matrix_initialize");
        ary_p = argv;
        count = argc;
    }

    mat->y = count;
    for( i = 0; i < count; i++ )
    {
        VALUE ary2 = ary_p[i];
        Check_Type( ary2, T_ARRAY );
        if( RARRAY_LEN( ary2 ) > 4 || RARRAY_LEN( ary2 ) < 1 || RARRAY_LEN( ary2 ) != mat->y ) rb_raise( eDXRubyError, "配列の数が正しくありません。 - Matrix_initialize");

        for( j = 0; j < RARRAY_LEN( ary2 ); j++ )
        {
            mat->m[i][j] = NUM2DBL( RARRAY_PTR( ary2 )[j] );
        }
    }
    mat->x = RARRAY_LEN( ary_p[0] );

    return self;
}

static VALUE Matrix_mul( VALUE self, VALUE varg )
{
    struct DXRubyMatrix *mat_d = DXRUBY_GET_STRUCT( Matrix, self );
    struct DXRubyMatrix *result;
    VALUE vresult;

    if( FIXNUM_P( varg ) || TYPE( varg ) == T_FLOAT || TYPE( varg ) == T_BIGNUM )
    {
        int i, j;

        vresult = Matrix_allocate( cMatrix );
        result = DXRUBY_GET_STRUCT( Matrix, vresult );
        result->x = mat_d->x;
        result->y = mat_d->y;
        for( i = 0; i < mat_d->y; i++ )
        {
            for( j = 0; j < mat_d->x; j++ )
            {
                result->m[i][j] = mat_d->m[i][j] * NUM2DBL( varg );
            }
        }
    }
    else
    {
        int i, j, k;
        struct DXRubyMatrix *mat_s;

        DXRUBY_CHECK_TYPE( Matrix, varg );
        mat_s = DXRUBY_GET_STRUCT( Matrix, varg );

        if( mat_d->x != mat_s->y || mat_d->y != mat_s->x ) rb_raise( eDXRubyError, "要素数が一致していません。 - Matrix_*");
        vresult = Matrix_allocate( cMatrix );
        result = DXRUBY_GET_STRUCT( Matrix, vresult );
        result->x = mat_s->x;
        result->y = mat_d->y;

        for( i = 0; i < mat_d->y; i++ )
        {
            for( j = 0; j < mat_s->x; j++ )
            {
                for( k = 0; k < mat_s->x; k++ )
                {
                    result->m[i][j] += mat_d->m[i][k] * mat_s->m[k][j];
                }
            }
        }
    }
    return vresult;
}


static VALUE Matrix_to_s( VALUE self )
{
    struct DXRubyMatrix *mat = DXRUBY_GET_STRUCT( Matrix, self );
    char buf[256];
    sprintf( buf, "x = %d, y = %d, (%f, %f, %f, %f)(%f, %f, %f, %f)(%f, %f, %f, %f)(%f, %f, %f, %f)", mat->x, mat->y
        , mat->m11, mat->m12, mat->m13, mat->m14
        , mat->m21, mat->m22, mat->m23, mat->m24
        , mat->m31, mat->m32, mat->m33, mat->m34
        , mat->m41, mat->m42, mat->m43, mat->m44);
    return rb_str_new2( buf );
}

/* ビュー行列作成 */
static VALUE Matrix_create_view( int argc, VALUE *argv, VALUE klass )
{
    struct DXRubyMatrix *mat;
    struct DXRubyMatrix *result;
    D3DMATRIX matrix;
    VALUE vresult;

    if( argc == 0 ) rb_raise( eDXRubyError, "引数が足りません - Matrix_create_view" );

    DXRUBY_CHECK_TYPE( Matrix, argv[0] );
    mat = DXRUBY_GET_STRUCT( Matrix, argv[0] );
    vresult = Matrix_allocate( cMatrix );
    result = DXRUBY_GET_STRUCT( Matrix, vresult );

    memcpy( &matrix, mat->m, sizeof( float ) * 16 );
    D3DXMatrixInverse( (D3DMATRIX *)&result->m, 0, (D3DMATRIX *)&matrix);

    result->x = 4;
    result->y = 4;
    if( argc == 1 || argv[1] == Qnil )
    {
        D3DXMatrixTranslation( &matrix, (float)-(g_WindowInfo.width)/2.0f, (float)-(g_WindowInfo.height)/2.0f, 0);
    }
    else
    {
        struct DXRubyRenderTarget *rt;
        DXRUBY_CHECK_TYPE( RenderTarget, argv[1] );
        rt = DXRUBY_GET_STRUCT( RenderTarget, argv[1] );
        DXRUBY_CHECK_DISPOSE( rt, texture );
        D3DXMatrixTranslation( &matrix, (float)-(rt->texture->width)/2.0f, (float)-(rt->texture->height)/2.0f, 0);
    }

    D3DXMatrixMultiply( (D3DMATRIX *)&result->m, &matrix, (D3DMATRIX *)&result->m );

    return vresult;
}


/* 射影変換行列作成 */
static VALUE Matrix_create_projection( VALUE klass, VALUE vwidth, VALUE vheight, VALUE vzn, VALUE vzf )
{
    struct DXRubyMatrix *result;
    D3DMATRIX matrix;
    VALUE vresult;
    vresult = Matrix_allocate( cMatrix );
    result = DXRUBY_GET_STRUCT( Matrix, vresult );

    D3DXMatrixScaling( &matrix, 1, -1, -1);
    D3DXMatrixPerspectiveLH( (D3DMATRIX*)result->m, NUM2DBL( vwidth ), NUM2DBL( vheight ), -NUM2DBL( vzn ), -NUM2DBL( vzf ) );
    D3DXMatrixMultiply( (D3DMATRIX *)&result->m, &matrix, (D3DMATRIX *)&result->m );

    return vresult;
}


/* 射影変換行列作成(画角指定) */
static VALUE Matrix_create_projection_fov( VALUE klass, VALUE vfov, VALUE vaspect, VALUE vzn, VALUE vzf )
{
    struct DXRubyMatrix *result;
    D3DMATRIX matrix;
    VALUE vresult;
    float angle;
    vresult = Matrix_allocate( cMatrix );
    result = DXRUBY_GET_STRUCT( Matrix, vresult );

    angle = 3.141592653589793115997963468544185161590576171875f / 180.0f * NUM2DBL( vfov );
    D3DXMatrixPerspectiveFovRH( (D3DMATRIX*)result->m, angle, NUM2DBL( vaspect ), -NUM2DBL( vzn ), -NUM2DBL( vzf ) );

    D3DXMatrixScaling( &matrix, 1, -1, -1);
    D3DXMatrixPerspectiveFovLH( (D3DMATRIX*)result->m, angle, NUM2DBL( vaspect ), -NUM2DBL( vzn ), -NUM2DBL( vzf ) );
    D3DXMatrixMultiply( (D3DMATRIX *)&result->m, &matrix, (D3DMATRIX *)&result->m );

    return vresult;
}


/* 正射影変換行列作成 */
static VALUE Matrix_create_projection_ortho( VALUE klass, VALUE vwidth, VALUE vheight, VALUE vzn, VALUE vzf )
{
    struct DXRubyMatrix *result;
    D3DMATRIX matrix;
    VALUE vresult;
    vresult = Matrix_allocate( cMatrix );
    result = DXRUBY_GET_STRUCT( Matrix, vresult );

    D3DXMatrixScaling( &matrix, 1, -1, -1);
    D3DXMatrixOrthoLH( (D3DMATRIX*)result->m, NUM2DBL( vwidth ), NUM2DBL( vheight ), -NUM2DBL( vzn ), -NUM2DBL( vzf ) );
    D3DXMatrixMultiply( (D3DMATRIX *)&result->m, &matrix, (D3DMATRIX *)&result->m );

    return vresult;
}


/* x軸回転行列作成 */
static VALUE Matrix_create_rot_x( VALUE klass, VALUE vangle )
{
    struct DXRubyMatrix *result;
    VALUE vresult;
    float angle;

    vresult = Matrix_allocate( cMatrix );
    result = DXRUBY_GET_STRUCT( Matrix, vresult );

    result->x = 4;
    result->y = 4;
    angle = 3.141592653589793115997963468544185161590576171875f / 180.0f * NUM2DBL( vangle );
    result->m11 = 1;
    result->m22 = cos( angle );
    result->m23 = sin( angle );
    result->m32 = -result->m23;
    result->m33 = result->m22;
    result->m44 = 1;

    return vresult;
}
/* y軸回転行列作成 */
static VALUE Matrix_create_rot_y( VALUE klass, VALUE vangle )
{
    struct DXRubyMatrix *result;
    VALUE vresult;
    float angle;

    vresult = Matrix_allocate( cMatrix );
    result = DXRUBY_GET_STRUCT( Matrix, vresult );

    result->x = 4;
    result->y = 4;
    angle = 3.141592653589793115997963468544185161590576171875f / 180.0f * NUM2DBL( vangle );
    result->m11 = cos( angle );
    result->m13 = -sin( angle );
    result->m22 = 1;
    result->m31 = -result->m13;
    result->m33 = result->m11;
    result->m44 = 1;

    return vresult;
}
/* z軸回転行列作成 */
static VALUE Matrix_create_rot_z( VALUE klass, VALUE vangle )
{
    struct DXRubyMatrix *result;
    VALUE vresult;
    float angle;

    vresult = Matrix_allocate( cMatrix );
    result = DXRUBY_GET_STRUCT( Matrix, vresult );

    result->x = 4;
    result->y = 4;
    angle = 3.141592653589793115997963468544185161590576171875f / 180.0f * NUM2DBL( vangle );
    result->m11 = cos( angle );
    result->m12 = sin( angle );
    result->m21 = -result->m12;
    result->m22 = result->m11;
    result->m33 = 1;
    result->m44 = 1;

    return vresult;
}
/* 平行移動行列作成 */
static VALUE Matrix_create_trans( VALUE klass, VALUE vx, VALUE vy, VALUE vz )
{
    struct DXRubyMatrix *result;
    VALUE vresult;

    vresult = Matrix_allocate( cMatrix );
    result = DXRUBY_GET_STRUCT( Matrix, vresult );

    result->x = 4;
    result->y = 4;
    result->m11 = 1;
    result->m22 = 1;
    result->m33 = 1;
    result->m41 = NUM2DBL( vx );
    result->m42 = NUM2DBL( vy );
    result->m43 = NUM2DBL( vz );
    result->m44 = 1;

    return vresult;
}
/* スケーリング行列作成 */
static VALUE Matrix_create_scale( VALUE klass, VALUE vx, VALUE vy, VALUE vz )
{
    struct DXRubyMatrix *result;
    VALUE vresult;

    vresult = Matrix_allocate( cMatrix );
    result = DXRUBY_GET_STRUCT( Matrix, vresult );

    result->x = 4;
    result->y = 4;
    result->m11 = NUM2DBL( vx );
    result->m22 = NUM2DBL( vy );
    result->m33 = NUM2DBL( vz );
    result->m44 = 1;

    return vresult;
}


/*********************************************************************
 * Vectorクラス
 *
 * ベクトルを表現する。
 *********************************************************************/

/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void Vector_release( struct DXRubyVector* vec )
{
    vec->x = 2; /* 最適化抑止 */
    free( vec );
}

/*--------------------------------------------------------------------
   Vectorクラスのallocate。メモリを確保する為にinitialize前に呼ばれる。
 ---------------------------------------------------------------------*/
static VALUE Vector_allocate( VALUE klass )
{
    VALUE obj;
    struct DXRubyVector *vec;
    int i;

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

    vec->x = 0;
    ZeroMemory( vec->v, sizeof( float ) * 4 );

    return obj;
}

/*--------------------------------------------------------------------
   VectorクラスのInitialize
 ---------------------------------------------------------------------*/
static VALUE Vector_initialize( int argc, VALUE *argv, VALUE self )
{
    struct DXRubyVector *vec = DXRUBY_GET_STRUCT( Vector, self );
    VALUE ary;
    VALUE *ary_p;
    int i, count;

    if( argc == 0 )
    {
        return self;
    }

    if( argc == 1 )
    {
        ary = argv[0];
        Check_Type( ary, T_ARRAY );
        if( RARRAY_LEN( ary ) > 4 || RARRAY_LEN( ary ) < 1 ) rb_raise( eDXRubyError, "配列の数が正しくありません。 - Vector_initialize");
        ary_p = RARRAY_PTR( ary );
        count = RARRAY_LEN( ary );
    }
    else
    {
        if( argc > 4 ) rb_raise( eDXRubyError, "引数の数が正しくありません。 - Vector_initialize");
        ary_p = argv;
        count = argc;
    }

    vec->x = count;
    for( i = 0; i < count; i++ )
    {
        vec->v[i] = NUM2DBL( ary_p[i] );
    }

    return self;
}

static VALUE Vector_mul( VALUE self, VALUE varg )
{
    struct DXRubyVector *vec = DXRUBY_GET_STRUCT( Vector, self );
    struct DXRubyVector *result;
    VALUE vresult;

    if( FIXNUM_P( varg ) || TYPE( varg ) == T_FLOAT || TYPE( varg ) == T_BIGNUM )
    {
        int i;
        vresult = Vector_allocate( cVector );
        result = DXRUBY_GET_STRUCT( Vector, vresult );
        result->x = vec->x;

        for( i = 0; i < vec->x; i++ )
        {
            result->v[i] = vec->v[i] * NUM2DBL( varg );
        }
    }
    else if( TYPE( varg ) == T_DATA && RDATA( varg )->dfree == (RUBY_DATA_FUNC)Vector_release )
    {
        struct DXRubyVector *vec_s = DXRUBY_GET_STRUCT( Vector, varg );
        return rb_float_new( vec->v1 * vec_s->v1 + vec->v2 * vec_s->v2 + vec->v3 * vec_s->v3 + vec->v4 * vec_s->v4 );
    }
    else if( TYPE( varg ) == T_DATA && RDATA( varg )->dfree == (RUBY_DATA_FUNC)Matrix_release )
    {
        struct DXRubyMatrix *mat;
        int i, j;
        DXRUBY_CHECK_TYPE( Matrix, varg );
        mat = DXRUBY_GET_STRUCT( Matrix, varg );

        if( vec->x != mat->y ) rb_raise( eDXRubyError, "要素数が一致していません。 - Vector_*");
        vresult = Vector_allocate( cVector );
        result = DXRUBY_GET_STRUCT( Vector, vresult );
        result->x = mat->x;

        for( i = 0; i < mat->x; i++ )
        {
            for( j = 0; j < mat->y; j++)
            {
                result->v[i] += vec->v[j] * mat->m[j][i];
            }
        }
    }
    else
    {
        rb_raise( eDXRubyError, "引数が異常です");
    }

    return vresult;
}

static VALUE Vector_add( VALUE self, VALUE vvector )
{
    struct DXRubyVector *vec_d = DXRUBY_GET_STRUCT( Vector, self );
    struct DXRubyVector *vec_s;
    struct DXRubyVector *result;
    VALUE vresult;
    int i;

    DXRUBY_CHECK_TYPE( Vector, vvector );
    vec_s =  DXRUBY_GET_STRUCT( Vector, vvector );
    vresult = Vector_allocate( cVector );
    result = DXRUBY_GET_STRUCT( Vector, vresult );
    result->x = vec_d->x;

    for( i = 0; i < vec_d->x; i++ )
    {
        result->v[i] = vec_d->v[i] + vec_s->v[i];
    }

    return vresult;
}

static VALUE Vector_sub( VALUE self, VALUE vvector )
{
    struct DXRubyVector *vec_d = DXRUBY_GET_STRUCT( Vector, self );
    struct DXRubyVector *vec_s;
    struct DXRubyVector *result;
    VALUE vresult;
    int i;

    DXRUBY_CHECK_TYPE( Vector, vvector );
    vec_s =  DXRUBY_GET_STRUCT( Vector, vvector );
    vresult = Vector_allocate( cVector );
    result = DXRUBY_GET_STRUCT( Vector, vresult );
    result->x = vec_d->x;

    for( i = 0; i < vec_d->x; i++ )
    {
        result->v[i] = vec_d->v[i] - vec_s->v[i];
    }

    return vresult;
}

static VALUE Vector_div( VALUE self, VALUE varg )
{
    struct DXRubyVector *vec = DXRUBY_GET_STRUCT( Vector, self );
    struct DXRubyVector *result;
    VALUE vresult;
    int i;

    vresult = Vector_allocate( cVector );
    result = DXRUBY_GET_STRUCT( Vector, vresult );
    result->x = vec->x;

    for( i = 0; i < vec->x; i++ )
    {
        result->v[i] = vec->v[i] / NUM2DBL( varg );
    }

    return vresult;
}

static VALUE Vector_minus( VALUE self )
{
    struct DXRubyVector *vec = DXRUBY_GET_STRUCT( Vector, self );
    struct DXRubyVector *result;
    VALUE vresult;
    int i;

    vresult = Vector_allocate( cVector );
    result = DXRUBY_GET_STRUCT( Vector, vresult );
    result->x = vec->x;

    for( i = 0; i < vec->x; i++ )
    {
        result->v[i] = -vec->v[i];
    }

    return vresult;
}

static VALUE Vector_normalize( VALUE self )
{
    struct DXRubyVector *vec = DXRUBY_GET_STRUCT( Vector, self );
    struct DXRubyVector *result;
    VALUE vresult;
    float magsq = vec->v1*vec->v1 + vec->v2*vec->v2 + vec->v3*vec->v3 + vec->v4*vec->v4;
    int i;

    vresult = Vector_allocate( cVector );
    result = DXRUBY_GET_STRUCT( Vector, vresult );
    result->x = vec->x;

    if( magsq > 0.0f )
    {
        float temp = 1.0f / sqrt( magsq );
        int i;
        for( i = 0; i < vec->x; i++ )
        {
            result->v[i] = temp * vec->v[i];
        }
    }

    return vresult;
}

static VALUE Vector_distance( VALUE klass, VALUE vvec1, VALUE vvec2 )
{
    struct DXRubyVector *vec1;
    struct DXRubyVector *vec2;
    float dx, dy, dz, dw;

    DXRUBY_CHECK_TYPE( Vector, vvec1 );
    DXRUBY_CHECK_TYPE( Vector, vvec2 );
    vec1 = DXRUBY_GET_STRUCT( Vector, vvec1 );
    vec2 = DXRUBY_GET_STRUCT( Vector, vvec2 );

    dx = vec1->v1 - vec2->v1;
    dy = vec1->v2 - vec2->v2;
    dz = vec1->v3 - vec2->v3;
    dw = vec1->v4 - vec2->v4;
    return rb_float_new( sqrt( dx*dx + dy*dy + dz*dz + dw*dw ) );
}

static VALUE Vector_cross_product( VALUE klass, VALUE vvec1, VALUE vvec2 )
{
    struct DXRubyVector *vec1;
    struct DXRubyVector *vec2;
    struct DXRubyVector *result;
    VALUE vresult;

    DXRUBY_CHECK_TYPE( Vector, vvec1 );
    DXRUBY_CHECK_TYPE( Vector, vvec2 );
    vec1 = DXRUBY_GET_STRUCT( Vector, vvec1 );
    vec2 = DXRUBY_GET_STRUCT( Vector, vvec2 );

    vresult = Vector_allocate( cVector );
    result = DXRUBY_GET_STRUCT( Vector, vresult );
    result->x = 3;

    result->v1 = vec1->v2*vec2->v3 - vec1->v3*vec2->v2;
    result->v2 = vec1->v3*vec2->v1 - vec1->v1*vec2->v3;
    result->v3 = vec1->v1*vec2->v2 - vec1->v2*vec2->v1;

    return vresult;
}

static VALUE Vector_inner_product( VALUE klass, VALUE vvec1, VALUE vvec2 )
{
    struct DXRubyVector *vec1;
    struct DXRubyVector *vec2;
    DXRUBY_CHECK_TYPE( Vector, vvec1 );
    DXRUBY_CHECK_TYPE( Vector, vvec2 );
    vec1 = DXRUBY_GET_STRUCT( Vector, vvec1 );
    vec2 = DXRUBY_GET_STRUCT( Vector, vvec2 );
    return rb_float_new( vec1->v1 * vec2->v1 + vec1->v2 * vec2->v2 + vec1->v3 * vec2->v3 + vec1->v4 * vec2->v4 );
}

static VALUE Vector_to_s( VALUE self )
{
    struct DXRubyVector *vec = DXRUBY_GET_STRUCT( Vector, self );
    char buf[128];
    sprintf( buf, "(%f, %f, %f, %f)", vec->v1, vec->v2, vec->v3, vec->v4 );
    return rb_str_new2( buf );
}

static VALUE Vector_get_x( VALUE self )
{
    struct DXRubyVector *vec = DXRUBY_GET_STRUCT( Vector, self );
    return rb_float_new(vec->v1);
}
static VALUE Vector_get_y( VALUE self )
{
    struct DXRubyVector *vec = DXRUBY_GET_STRUCT( Vector, self );
    return rb_float_new(vec->v2);
}
static VALUE Vector_get_z( VALUE self )
{
    struct DXRubyVector *vec = DXRUBY_GET_STRUCT( Vector, self );
    return rb_float_new(vec->v3);
}
static VALUE Vector_get_w( VALUE self )
{
    struct DXRubyVector *vec = DXRUBY_GET_STRUCT( Vector, self );
    return rb_float_new(vec->v4);
}
static VALUE Vector_get_size( VALUE self )
{
    struct DXRubyVector *vec = DXRUBY_GET_STRUCT( Vector, self );
    return rb_float_new(vec->x);
}


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

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

static void Image_release( struct DXRubyImage *image )
{
    if( image->texture )
    {
        Image_free( image );
    }
    free( image );
    image = NULL;

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

/*--------------------------------------------------------------------
   Imageクラスのdispose。
 ---------------------------------------------------------------------*/
static VALUE Image_dispose( VALUE self )
{
    struct DXRubyImage *image = DXRUBY_GET_STRUCT( Image, self );
    DXRUBY_CHECK_DISPOSE( image, texture );
    Image_free( image );
    return self;
}

/*--------------------------------------------------------------------
   Imageクラスの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, Image_release, 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;
    int *p;

    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++ )
    {
        p = (int*)((char *)texrect.pBits + y * texrect.Pitch);
        for( x = 0; x <= x2 - x1; x++ )
        {
            *(p++) = 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;
}


/*--------------------------------------------------------------------
   Imageクラスの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;

    g_iRefAll++;

    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;

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

    return obj;
}


/*--------------------------------------------------------------------
   Imageクラスの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, Image_release, image);

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

    g_iRefAll++;

    return obj;
}


/*--------------------------------------------------------------------
   Imageクラスの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, Image_release, image);

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

    g_iRefAll++;

    return obj;
}


/*--------------------------------------------------------------------
   Imageオブジェクトの分割作成
 ---------------------------------------------------------------------*/
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, Image_release, 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, Image_release, image);

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

    g_iRefAll++;

    return obj;
}


/*--------------------------------------------------------------------
   Imageオブジェクトの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 );
    DXRUBY_CHECK_DISPOSE( srcimage, texture );

    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, Image_release, 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 );
    DXRUBY_CHECK_DISPOSE( image, texture );

    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 );
    DXRUBY_CHECK_DISPOSE( image, texture );

    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 );
    DXRUBY_CHECK_DISPOSE( image, texture );

    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 );
    DXRUBY_CHECK_DISPOSE( image, texture );

    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 );
    DXRUBY_CHECK_DISPOSE( image, texture );

    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 );
    DXRUBY_CHECK_DISPOSE( image, texture );

    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;
    int *p;

    Data_Get_Struct( obj, struct DXRubyImage, image );
    DXRUBY_CHECK_DISPOSE( image, texture );

    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++ )
    {
        p = (int*)((char *)texrect.pBits + y * texrect.Pitch);
        for( x = 0; x < image->width; x++ )
        {
            if( (*p & 0x00ffffff) == col )
            {
//                *((int*)((char *)texrect.pBits + x * 4 + y * texrect.Pitch)) = col;
                *p = col;
            }
            p++;
        }
    }
    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;
    int *p;

    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;
    }

    p = (int*)((char *)texrect->pBits + y * texrect->Pitch + x1 * 4);
    for( x = 0; x <= x2 - x1; x++ )
    {
        *(p++) = 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;
    int tempx, tempy;

    Data_Get_Struct( obj, struct DXRubyImage, image );
    DXRUBY_CHECK_DISPOSE( image, texture );

    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;

    tempx = x0 - rect.left + (int)image->x;
    tempy = y0 - rect.top + (int)image->y;
    while ( x >= 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 );
    DXRUBY_CHECK_DISPOSE( image, texture );

    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 );
    DXRUBY_CHECK_DISPOSE( image, texture );

    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;
    int *psrc;
    int *pdst;

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

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

    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++)
    {
        psrc = (int*)((char *)srctrect.pBits + i * srctrect.Pitch);
        pdst = (int*)((char *)dsttrect.pBits + i * dsttrect.Pitch);
        for( j = 0; j < width; j++)
        {
            *(pdst++) = *(psrc++);
        }
    }

    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;
    int *psrc;
    int *pdst;

    Data_Get_Struct( self, struct DXRubyImage, srcimage );
    DXRUBY_CHECK_DISPOSE( srcimage, texture );

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

    g_iRefAll++;

    /* テクスチャメモリ取得 */
    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++)
    {
        psrc = (int*)((char *)srctrect.pBits + i * srctrect.Pitch);
        pdst = (int*)((char *)dsttrect.pBits + i * dsttrect.Pitch);
        for( j = 0; j < srcimage->width; j++)
        {
            *(pdst++) = *(psrc++);
        }
    }

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

    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;
    int *psrc;
    int *pdst;

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

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

    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++)
    {
        psrc = (int*)((char *)srctrect.pBits + i * srctrect.Pitch);
        pdst = (int*)((char *)dsttrect.pBits + i * dsttrect.Pitch);
        for( j = 0; j < width; j++)
        {
            struct DXRubyColor s = *((struct DXRubyColor*)(psrc));

            if( s.alpha == 255 )
            {
                *((struct DXRubyColor*)(pdst)) = s;
            }
            else if( s.alpha != 0 )
            {
                struct DXRubyColor d = *((struct DXRubyColor*)(pdst));
                int alpha = (255 - s.alpha) * d.alpha / 255 + s.alpha;

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

                *((struct DXRubyColor*)(pdst)) = s;
            }
            psrc++;
            pdst++;
        }
    }

    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, vstr, 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;
    TEXTMETRIC tm;
    GLYPHMETRICS gm;

    LPWSTR widestr;
    VALUE vwidestr;
    MAT2 mat2 = {{0,1},{0,0},{0,0},{0,1}};

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

    Check_Type(vstr, T_STRING);

    /* 引数のフォントオブジェクトから中身を取り出す */
    Data_Get_Struct( obj, struct DXRubyImage, image );
    DXRUBY_CHECK_DISPOSE( image, texture );
    Data_Get_Struct( data, struct DXRubyFont, font );
    DXRUBY_CHECK_DISPOSE( font, pD3DXFont );
    if (RDATA(data)->dfree != (RUBY_DATA_FUNC)Font_release)
    {
      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;
    }

    /* 描画文字のUTF16LE化 */
    {
        char *buf;
        int bufsize;
        bufsize = MultiByteToWideChar(CP_ACP, 0, RSTRING_PTR( vstr ), RSTRING_LEN( vstr ), 0, 0);
        buf = alloca(bufsize * 2);
        MultiByteToWideChar(CP_ACP, 0, RSTRING_PTR( vstr ), RSTRING_LEN( vstr ), (LPWSTR)buf, bufsize);
        vwidestr = rb_str_new( buf, bufsize*2 );
    }
    widestr = alloca( RSTRING_LEN( vwidestr ) + 2 );
    ZeroMemory( widestr, RSTRING_LEN( vwidestr ) + 2 );
    memcpy( widestr, RSTRING_PTR( vwidestr ), RSTRING_LEN( vwidestr ) );

    hDC = GetDC( g_hWnd );
    SelectObject( hDC, font->hFont );
    GetTextMetrics( hDC, &tm );

    dstrect.left = image->x;
    dstrect.top = image->y;
    dstrect.right = image->x + image->width;
    dstrect.bottom = image->y + image->height;

    image->texture->pD3DTexture->lpVtbl->LockRect( image->texture->pD3DTexture, 0, &dsttrect, &dstrect, 0 );

    for( i = 0; i < RSTRING_LEN( vwidestr ) / 2; i++ )
    {
        int bufsize = GetGlyphOutlineW( hDC, *(widestr + i), GGO_GRAY8_BITMAP,
                                        &gm, 0, NULL, &mat2 );
        unsigned char *buf = alloca( bufsize );
        int v, u;

        GetGlyphOutlineW( hDC, *(widestr + i), GGO_GRAY8_BITMAP, 
                          &gm, bufsize, (LPVOID)buf, &mat2 );

        for( v = 0; v < gm.gmBlackBoxY; v++ )
        {
            int yy = y + tm.tmAscent - gm.gmptGlyphOrigin.y + v;
            if( yy < 0 )
            {
                continue;
            }
            if( yy >= image->height )
            {
                break;
            }
            for( u = 0; u < gm.gmBlackBoxX; u++ )
            {
                int xx, src;

                xx = x + gm.gmptGlyphOrigin.x + u;
                if( xx < 0 )
                {
                    continue;
                }
                if( xx >= image->width )
                {
                    break;
                }

                src = (int)buf[ u + v * ((gm.gmBlackBoxX + 3) & 0xfffc) ] * 255 / 65;

                if( src == 255 )
                {
                    *((LPDWORD)((char*)dsttrect.pBits + xx * 4 + yy * dsttrect.Pitch)) = D3DCOLOR_ARGB(0xff, cr, cg, cb);
                }
                else if( src != 0 )
                {
                    struct DXRubyColor d = *((struct DXRubyColor*)((char*)dsttrect.pBits + xx * 4 + yy * dsttrect.Pitch));
                    struct DXRubyColor data;

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

                    *((struct DXRubyColor*)((char*)dsttrect.pBits + xx * 4 + yy * dsttrect.Pitch)) = data;
                }
            }
        }
        x += gm.gmCellIncX;
        if( x >= image->width )
        {
            break;
        }
    }

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

    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 );
    DXRUBY_CHECK_DISPOSE( image, texture );

    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;
}


/*--------------------------------------------------------------------
   イメージの開始位置xを返す。
 ---------------------------------------------------------------------*/
static VALUE Image_getX( VALUE obj )
{
    struct DXRubyImage *image;

    Data_Get_Struct( obj, struct DXRubyImage, image );
    DXRUBY_CHECK_DISPOSE( image, texture );

    return INT2FIX( image->x );
}


/*--------------------------------------------------------------------
   イメージの開始位置yを返す。
 ---------------------------------------------------------------------*/
static VALUE Image_getY( VALUE obj )
{
    struct DXRubyImage *image;

    Data_Get_Struct( obj, struct DXRubyImage, image );
    DXRUBY_CHECK_DISPOSE( image, texture );

    return INT2FIX( image->y );
}


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

    Data_Get_Struct( obj, struct DXRubyImage, image );
    DXRUBY_CHECK_DISPOSE( image, texture );

    return INT2FIX( image->width );
}


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

    Data_Get_Struct( obj, struct DXRubyImage, image );
    DXRUBY_CHECK_DISPOSE( image, texture );

    return INT2FIX( image->height );
}


/*--------------------------------------------------------------------
   イメージの開始位置xを設定する。
 ---------------------------------------------------------------------*/
static VALUE Image_setX( VALUE obj, VALUE vx )
{
    struct DXRubyImage *image;
    int x = NUM2INT( vx );

    Data_Get_Struct( obj, struct DXRubyImage, image );
    DXRUBY_CHECK_DISPOSE( image, texture );

    if( x < 0 || x + image->width > image->texture->width )
    {
        rb_raise( eDXRubyError, "bad value of x - Image#x=" );
    }

    image->x = x;
    return vx;
}


/*--------------------------------------------------------------------
   イメージの開始位置yを設定する。
 ---------------------------------------------------------------------*/
static VALUE Image_setY( VALUE obj, VALUE vy )
{
    struct DXRubyImage *image;
    int y = NUM2INT( vy );

    Data_Get_Struct( obj, struct DXRubyImage, image );
    DXRUBY_CHECK_DISPOSE( image, texture );

    if( y < 0 || y + image->height > image->texture->height )
    {
        rb_raise( eDXRubyError, "bad value of y - Image#y=" );
    }

    image->y = y;
    return vy;
}


/*--------------------------------------------------------------------
   イメージのサイズ（幅）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Image_setWidth( VALUE obj, VALUE vwidth )
{
    struct DXRubyImage *image;
    int width = NUM2INT( vwidth );

    Data_Get_Struct( obj, struct DXRubyImage, image );
    DXRUBY_CHECK_DISPOSE( image, texture );

    if( width < 1 || width + image->x > image->texture->width )
    {
        rb_raise( eDXRubyError, "bad value of width - Image#width=" );
    }

    image->width = width;
    return vwidth;
}


/*--------------------------------------------------------------------
   イメージのサイズ（高さ）を設定する。
 ---------------------------------------------------------------------*/
static VALUE Image_setHeight( VALUE obj, VALUE vheight )
{
    struct DXRubyImage *image;
    int height = NUM2INT( vheight );

    Data_Get_Struct( obj, struct DXRubyImage, image );
    DXRUBY_CHECK_DISPOSE( image, texture );

    if( height < 1 || height + image->y > image->texture->height )
    {
        rb_raise( eDXRubyError, "bad value of height - Image#height=" );
    }

    image->height = height;
    return vheight;
}



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

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

    /* サウンドオブジェクトの開放 */
    /* バンド解放 */
    if ( sound->pDMSegment )
    {
        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);
    }
}

static void Sound_release( struct DXRubySound *sound )
{
    if ( sound->pDMSegment )
    {
        Sound_free( sound );
    }
    free( sound );
    sound = NULL;

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

/*--------------------------------------------------------------------
   Soundクラスのdispose。
 ---------------------------------------------------------------------*/
static VALUE Sound_dispose( VALUE self )
{
    struct DXRubySound *sound = DXRUBY_GET_STRUCT( Sound, self );
    DXRUBY_CHECK_DISPOSE( sound, pDMSegment );
    Sound_free( sound );
    return self;
}

/*--------------------------------------------------------------------
   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, Sound_release, 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];

    g_iRefAll++;

	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_hWnd,               /* ウィンドウのハンドル */
	                                              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 ) )
    {
        sound->pDMSegment = NULL;
        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" );
    }


    /* 音量設定 */
    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 );
    DXRUBY_CHECK_DISPOSE( sound, pDMSegment );
    sound->start = NUM2INT( vstart );

    if( sound->midwavflag == 1 && sound->start > 0 )    /* wavの場合 */
    {
        hr = sound->pDMSegment->lpVtbl->SetLength( sound->pDMSegment, sound->start * DMUS_PPQ / 768 + 1 );
        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "セグメント開始位置の設定に失敗しました - SetLength" );
        }
    }

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

    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 );
    DXRUBY_CHECK_DISPOSE( sound, pDMSegment );
    sound->loopstart = NUM2INT( vloopstart );

    if( sound->midwavflag == 1 )
    {
        rb_raise( eDXRubyError, "WAVデータにループ開始位置は設定できません - Sound_loopStart=" );
    }

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

        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 );
    DXRUBY_CHECK_DISPOSE( sound, pDMSegment );
    sound->loopend = NUM2INT( vloopend );

    if( sound->midwavflag == 1 )
    {
        rb_raise( eDXRubyError, "WAVデータにループ終了位置は設定できません - Sound_loopEnd=" );
    }

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

        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 );
    DXRUBY_CHECK_DISPOSE( sound, pDMSegment );
    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 );
    DXRUBY_CHECK_DISPOSE( sound, pDMSegment );

    /* 音量設定 */
    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 );
    DXRUBY_CHECK_DISPOSE( sound, pDMSegment );

    /* 再生 */
    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 );
    DXRUBY_CHECK_DISPOSE( sound, pDMSegment );

    /* 再生 */
    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 SoundEffect_free( struct DXRubySoundEffect *soundeffect )
{
    /* サウンドバッファを開放 */
    RELEASE( soundeffect->pDSBuffer );

    g_iRefDS--;

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

}

static void SoundEffect_release( struct DXRubySoundEffect *soundeffect )
{
    if( soundeffect->pDSBuffer )
    {
        SoundEffect_free( soundeffect );
    }
    free( soundeffect );
    soundeffect = NULL;

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

/*--------------------------------------------------------------------
   SoundEffectクラスのdispose。
 ---------------------------------------------------------------------*/
static VALUE SoundEffect_dispose( VALUE self )
{
    struct DXRubySoundEffect *soundeffect = DXRUBY_GET_STRUCT( SoundEffect, self );
    DXRUBY_CHECK_DISPOSE( soundeffect, pDSBuffer );
    SoundEffect_free( soundeffect );
    return self;
}

/*--------------------------------------------------------------------
   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, SoundEffect_release, 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;

	g_iRefAll++;

    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_hWnd, 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 = DSBCAPS_GLOBALFOCUS;
	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" );
    }

	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 );
    DXRUBY_CHECK_DISPOSE( soundeffect, pDSBuffer );

	/* ロック */
	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 );
    DXRUBY_CHECK_DISPOSE( soundeffect, pDSBuffer );

	/* とめる */
    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 );
    DXRUBY_CHECK_DISPOSE( soundeffect, pDSBuffer );

    /* とめる */
    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;
    g_WindowInfo.userloop = 1;

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

    if( g_WindowInfo.requestclose == 1 )
    {
        g_WindowInfo.requestclose = 0;
        return Qtrue;
    }

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

    return Qfalse;
}


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

    /* フォーカスがはずれた場合、キーとパッド、マウスボタンの入力を受け付けなくする */
    if( g_WindowInfo.active == 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_hWnd, &cursor );
    return INT2FIX( cursor.x );
}


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

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

    return INT2FIX( cursor.y );
}


/*--------------------------------------------------------------------
   マウスカーソルの位置を設定する
 ---------------------------------------------------------------------*/
static VALUE Input_setmousepos( VALUE klass, VALUE vx, VALUE vy )
{
    POINT cursor;

    cursor.x = NUM2INT( vx );
    cursor.y = NUM2INT( vy );
    ClientToScreen( g_hWnd, &cursor );
    SetCursorPos( cursor.x, cursor.y );

    return Qnil;
}


/*--------------------------------------------------------------------
   マウス描画するかどうかの設定
 ---------------------------------------------------------------------*/
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 Font_free( struct DXRubyFont *font )
{
    RELEASE( font->pD3DXFont );
    DeleteObject( font->hFont );
}
static void Font_release( struct DXRubyFont *font )
{
    /* フォントオブジェクトの開放 */
    if( font->pD3DXFont )
    {
        Font_free( font );
    }
    free( font );
    font = NULL;

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

/*--------------------------------------------------------------------
   Fontクラスのdispose。
 ---------------------------------------------------------------------*/
static VALUE Font_dispose( VALUE self )
{
    struct DXRubyFont *font = DXRUBY_GET_STRUCT( Font, self );
    DXRUBY_CHECK_DISPOSE( font, pD3DXFont );
    Font_free( font );
    return self;
}

/*--------------------------------------------------------------------
   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, Font_release, font);
}


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

    g_iRefAll++;

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

    rb_scan_args( argc, argv, "12", &size, &fontname, &hash );

    if( hash == Qnil )
    {
        option = rb_hash_new();
    }
    else
    {
        Check_Type( hash, T_HASH );
        option = hash;
    }

    vweight = rb_hash_lookup( option, symbol_weight );
    vitalic = rb_hash_lookup( option, symbol_italic );

    if( vweight == Qnil || vweight == Qfalse )
    {
        weight = 400;
    }
    else if( vweight == Qtrue )
    {
        weight = 1000;
    }
    else
    {
        weight = NUM2INT( vweight );
    }

    if( vitalic == Qnil || vitalic == Qfalse )
    {
        italic = FALSE;
    }
    else
    {
        italic = TRUE;
    }

    desc.Height          = NUM2INT( size );
    desc.Width           = 0;
    desc.Weight          = weight;
    desc.MipLevels       = 0;
    desc.Italic          = italic;
    desc.CharSet         = DEFAULT_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.lfWeight          = weight;
    logfont.lfItalic          = italic;
    logfont.lfCharSet         = DEFAULT_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 );

    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 );
    DXRUBY_CHECK_DISPOSE( font, pD3DXFont );

    hDC = GetDC( g_hWnd );
    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_hWnd, 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 );
    DXRUBY_CHECK_DISPOSE( font, pD3DXFont );

    return INT2FIX( font->size );
}


/*
***************************************************************
*
*         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, "draw_scale", Window_drawScale  , -1 );
    rb_define_singleton_method( mWindow, "drawScale", Window_drawScale  , -1 );
    rb_define_singleton_method( mWindow, "draw_rot" , Window_drawRot    , -1 );
    rb_define_singleton_method( mWindow, "drawRot"  , Window_drawRot    , -1 );
    rb_define_singleton_method( mWindow, "draw_alpha", Window_drawAlpha  , -1 );
    rb_define_singleton_method( mWindow, "drawAlpha", Window_drawAlpha  , -1 );
    rb_define_singleton_method( mWindow, "draw_add" , Window_drawAdd    , -1 );
    rb_define_singleton_method( mWindow, "drawAdd"  , Window_drawAdd    , -1 );
    rb_define_singleton_method( mWindow, "draw_sub" , Window_drawSub    , -1 );
    rb_define_singleton_method( mWindow, "drawSub"  , Window_drawSub    , -1 );
    rb_define_singleton_method( mWindow, "draw_shader", Window_drawShader, -1 );
    rb_define_singleton_method( mWindow, "drawShader" , Window_drawShader, -1 );
    rb_define_singleton_method( mWindow, "draw_ex"  , Window_drawEx     , -1 );
    rb_define_singleton_method( mWindow, "drawEx"   , Window_drawEx     , -1 );
    rb_define_singleton_method( mWindow, "draw_font", Window_drawFont   , -1 );
    rb_define_singleton_method( mWindow, "drawFont" , Window_drawFont   , -1 );
    rb_define_singleton_method( mWindow, "draw_tile", Window_drawTile   , -1  );
    rb_define_singleton_method( mWindow, "drawTile" , Window_drawTile   , -1  );
    rb_define_singleton_method( mWindow, "draw_mesh", Window_drawMesh   , -1 );
    rb_define_singleton_method( mWindow, "drawMesh" , Window_drawMesh   , -1 );
    rb_define_singleton_method( mWindow, "draw3d", Window_draw3D   , -1  );
    rb_define_singleton_method( mWindow, "draw_3d", Window_draw3D   , -1  );
    rb_define_singleton_method( mWindow, "draw3D" , Window_draw3D   , -1  );
    rb_define_singleton_method( mWindow, "draw_morph", Window_drawMorph , -1 );
    rb_define_singleton_method( mWindow, "drawMorph" , Window_drawMorph , -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, "min_filter"   , Window_getMinFilter , 0 );
    rb_define_singleton_method( mWindow, "minFilter"    , Window_getMinFilter , 0 );
    rb_define_singleton_method( mWindow, "min_filter="  , Window_setMinFilter , 1 );
    rb_define_singleton_method( mWindow, "minFilter="   , Window_setMinFilter , 1 );
    rb_define_singleton_method( mWindow, "mag_filter"   , Window_getMagFilter , 0 );
    rb_define_singleton_method( mWindow, "magFilter"    , Window_getMagFilter , 0 );
    rb_define_singleton_method( mWindow, "mag_filter="  , Window_setMagFilter , 1 );
    rb_define_singleton_method( mWindow, "magFilter="   , Window_setMagFilter , 1 );
    rb_define_singleton_method( mWindow, "get_load"     , Window_getload, 0  );
    rb_define_singleton_method( mWindow, "getLoad"      , Window_getload, 0  );
    rb_define_singleton_method( mWindow, "open_filename", Window_openDialog, 2 );
    rb_define_singleton_method( mWindow, "openFilename" , Window_openDialog, 2 );
    rb_define_singleton_method( mWindow, "save_filename", Window_saveDialog, 2 );
    rb_define_singleton_method( mWindow, "saveFilename" , Window_saveDialog, 2 );
    rb_define_singleton_method( mWindow, "get_screen_shot", Window_getScreenShot, -1 );
    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, "load_icon"    , Window_loadIcon, 1  );
    rb_define_singleton_method( mWindow, "loadIcon"     , Window_loadIcon, 1  );
//    rb_define_singleton_method( mWindow, "playMovie"    , Window_playMovie, 1  );
    rb_define_singleton_method( mWindow, "zn="   , Window_set_zn   , 1  );
    rb_define_singleton_method( mWindow, "zn"    , Window_get_zn   , 0  );
    rb_define_singleton_method( mWindow, "zf="   , Window_set_zf   , 1  );
    rb_define_singleton_method( mWindow, "zf"    , Window_get_zf   , 0  );
    rb_define_singleton_method( mWindow, "view_matrix="   , Window_set_view   , 1  );
    rb_define_singleton_method( mWindow, "view_matrix"   , Window_get_view   , 0  );
    rb_define_singleton_method( mWindow, "projection_matrix="   , Window_set_proj   , 1  );
    rb_define_singleton_method( mWindow, "projection_matrix"   , Window_get_proj   , 0  );
    rb_define_singleton_method( mWindow, "set_fog"  , Window_set_fog   , -1  );
    rb_define_singleton_method( mWindow, "setFog"   , Window_set_fog   , -1  );
    rb_define_singleton_method( mWindow, "resize"   , Window_resize    , 2  );
    rb_define_singleton_method( mWindow, "active?"   , Window_get_active, 0  );
    rb_define_singleton_method( mWindow, "set_event"   , Window_set_event, 1  );
    rb_define_singleton_method( mWindow, "setEvent"   , Window_set_event, 1  );
    rb_define_singleton_method( mWindow, "get_event"   , Window_get_event, 1  );
    rb_define_singleton_method( mWindow, "getEvent"   , Window_get_event, 1  );


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

    /* RenderTargetクラスにメソッド登録*/
    rb_define_private_method( cRenderTarget, "initialize", RenderTarget_initialize, -1 );
    rb_define_method( cRenderTarget, "dispose"   , RenderTarget_dispose   , 0 );
    rb_define_method( cRenderTarget, "width"     , RenderTarget_getWidth  , 0 );
    rb_define_method( cRenderTarget, "height"    , RenderTarget_getHeight , 0 );
    rb_define_method( cRenderTarget, "width="    , RenderTarget_setWidth  , 1 );
    rb_define_method( cRenderTarget, "height="   , RenderTarget_setHeight , 1 );
    rb_define_method( cRenderTarget, "x"         , RenderTarget_getX      , 0 );
    rb_define_method( cRenderTarget, "y"         , RenderTarget_getY      , 0 );
    rb_define_method( cRenderTarget, "x="        , RenderTarget_setX      , 1 );
    rb_define_method( cRenderTarget, "y="        , RenderTarget_setY      , 1 );
    rb_define_method( cRenderTarget, "to_image"  , RenderTarget_to_image  , 0 );
    rb_define_method( cRenderTarget, "toImage"   , RenderTarget_to_image  , 0 );

    rb_define_method( cRenderTarget, "draw"     , Window_draw       , -1 );
    rb_define_method( cRenderTarget, "draw_scale", Window_drawScale  , -1 );
    rb_define_method( cRenderTarget, "drawScale", Window_drawScale  , -1 );
    rb_define_method( cRenderTarget, "draw_rot" , Window_drawRot    , -1 );
    rb_define_method( cRenderTarget, "drawRot"  , Window_drawRot    , -1 );
    rb_define_method( cRenderTarget, "draw_alpha", Window_drawAlpha  , -1 );
    rb_define_method( cRenderTarget, "drawAlpha", Window_drawAlpha  , -1 );
    rb_define_method( cRenderTarget, "draw_add" , Window_drawAdd    , -1 );
    rb_define_method( cRenderTarget, "drawAdd"  , Window_drawAdd    , -1 );
    rb_define_method( cRenderTarget, "draw_sub" , Window_drawSub    , -1 );
    rb_define_method( cRenderTarget, "drawSub"  , Window_drawSub    , -1 );
    rb_define_method( cRenderTarget, "draw_shader", Window_drawShader, -1 );
    rb_define_method( cRenderTarget, "drawShader" , Window_drawShader, -1 );
    rb_define_method( cRenderTarget, "draw_ex"  , Window_drawEx     , -1 );
    rb_define_method( cRenderTarget, "drawEx"   , Window_drawEx     , -1 );
    rb_define_method( cRenderTarget, "draw_font", Window_drawFont   , -1 );
    rb_define_method( cRenderTarget, "drawFont" , Window_drawFont   , -1 );
    rb_define_method( cRenderTarget, "draw_tile", Window_drawTile   , -1  );
    rb_define_method( cRenderTarget, "drawTile" , Window_drawTile   , -1  );
    rb_define_method( cRenderTarget, "draw_mesh", Window_drawMesh   , -1 );
    rb_define_method( cRenderTarget, "drawMesh" , Window_drawMesh   , -1 );
    rb_define_method( cRenderTarget, "draw3d", Window_draw3D   , -1  );
    rb_define_method( cRenderTarget, "draw_3d", Window_draw3D   , -1  );
    rb_define_method( cRenderTarget, "draw3D" , Window_draw3D   , -1  );
    rb_define_method( cRenderTarget, "zn="   , Window_set_zn   , 1  );
    rb_define_method( cRenderTarget, "zn"    , Window_get_zn   , 0  );
    rb_define_method( cRenderTarget, "zf="   , Window_set_zf   , 1  );
    rb_define_method( cRenderTarget, "zf"    , Window_get_zf   , 0  );
    rb_define_method( cRenderTarget, "view_matrix="   , Window_set_view   , 1  );
    rb_define_method( cRenderTarget, "view_matrix"   , Window_get_view   , 0  );
    rb_define_method( cRenderTarget, "projection_matrix="   , Window_set_proj   , 1  );
    rb_define_method( cRenderTarget, "projection_matrix"   , Window_get_proj   , 0  );
    rb_define_method( cRenderTarget, "set_fog"  , Window_set_fog   , -1  );
    rb_define_method( cRenderTarget, "setFog"   , Window_set_fog   , -1  );
    rb_define_method( cRenderTarget, "update"       , Window_update , 0 );
    rb_define_method( cRenderTarget, "bgcolor"      , Window_getbgcolor , 0  );
    rb_define_method( cRenderTarget, "bgcolor="     , Window_setbgcolor , 1  );

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


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

    /* Shaderクラスにメソッド登録*/
    rb_define_private_method( cShader, "initialize", Shader_initialize, 2 );
    rb_define_method( cShader, "dispose"   , Shader_dispose   , 0 );
    rb_define_method( cShader, "set_value", Shader_set_value, 2 );
    rb_define_method( cShader, "setValue", Shader_set_value, 2 );
//    rb_define_method( cShader, "method_missing", Shader_method_missing, 2 );

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


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

    /* Meshクラスにメソッド登録*/
    rb_define_private_method( cMesh, "initialize", Mesh_initialize, -1 );
//    rb_define_singleton_method( cMesh, "createTeapot", Mesh_create_teapot, 0 );
    rb_define_method( cMesh, "dispose"           , Mesh_dispose   , 0 );

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


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

    /* Polygonクラスにメソッド登録*/
    rb_define_private_method( cPolygon, "initialize", Polygon_initialize, 0 );
    rb_define_method( cPolygon, "vertex=", Polygon_set_vertex, 1 );
    rb_define_method( cPolygon, "tutv=", Polygon_set_tutv, 1 );

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


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

    /* Matrixクラスにクラスメソッド登録*/
    rb_define_singleton_method( cMatrix, "create_view", Matrix_create_view, -1 );
    rb_define_singleton_method( cMatrix, "createView", Matrix_create_view, -1 );
    rb_define_singleton_method( cMatrix, "create_projection", Matrix_create_projection, 4 );
    rb_define_singleton_method( cMatrix, "createProjection", Matrix_create_projection, 4 );
    rb_define_singleton_method( cMatrix, "create_projection_fov", Matrix_create_projection_fov, 4 );
    rb_define_singleton_method( cMatrix, "createProjectionFov", Matrix_create_projection_fov, 4 );
    rb_define_singleton_method( cMatrix, "create_projection_ortho", Matrix_create_projection_ortho, 4 );
    rb_define_singleton_method( cMatrix, "createProjectionOrtho", Matrix_create_projection_ortho, 4 );
    rb_define_singleton_method( cMatrix, "create_rotation_x", Matrix_create_rot_x, 1 );
    rb_define_singleton_method( cMatrix, "createRotationX", Matrix_create_rot_x, 1 );
    rb_define_singleton_method( cMatrix, "create_rotation_y", Matrix_create_rot_y, 1 );
    rb_define_singleton_method( cMatrix, "createRotationY", Matrix_create_rot_y, 1 );
    rb_define_singleton_method( cMatrix, "create_rotation_z", Matrix_create_rot_z, 1 );
    rb_define_singleton_method( cMatrix, "createRotationZ", Matrix_create_rot_z, 1 );
    rb_define_singleton_method( cMatrix, "create_scaling", Matrix_create_scale, 3 );
    rb_define_singleton_method( cMatrix, "createScaling", Matrix_create_scale, 3 );
    rb_define_singleton_method( cMatrix, "create_translation", Matrix_create_trans, 3 );
    rb_define_singleton_method( cMatrix, "createTranslation", Matrix_create_trans, 3 );

    /* Matrixクラスにメソッド登録*/
    rb_define_private_method( cMatrix, "initialize", Matrix_initialize, -1 );
    rb_define_method( cMatrix, "*", Matrix_mul, 1 );
    rb_define_method( cMatrix, "to_s", Matrix_to_s, 0 );

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


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

    /* Vectorクラスにクラスメソッド登録*/
    rb_define_singleton_method( cVector, "distance", Vector_distance, 2 );
    rb_define_singleton_method( cVector, "cross_product", Vector_cross_product, 2 );
    rb_define_singleton_method( cVector, "crossProduct", Vector_cross_product, 2 );
    rb_define_singleton_method( cVector, "inner_product", Vector_inner_product, 2 );
    rb_define_singleton_method( cVector, "innerProduct", Vector_inner_product, 2 );

    /* Vectorクラスにメソッド登録*/
    rb_define_private_method( cVector, "initialize", Vector_initialize, -1 );
    rb_define_method( cVector, "*", Vector_mul, 1 );
    rb_define_method( cVector, "+", Vector_add, 1 );
    rb_define_method( cVector, "-", Vector_sub, 1 );
    rb_define_method( cVector, "-@", Vector_minus, 0 );
    rb_define_method( cVector, "/", Vector_div, 1 );
    rb_define_method( cVector, "to_s", Vector_to_s, 0 );
    rb_define_method( cVector, "x", Vector_get_x, 0 );
    rb_define_method( cVector, "y", Vector_get_y, 0 );
    rb_define_method( cVector, "z", Vector_get_z, 0 );
    rb_define_method( cVector, "w", Vector_get_w, 0 );
    rb_define_method( cVector, "size", Vector_get_size, 0 );
    rb_define_method( cVector, "normalize", Vector_normalize, 0 );

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


    /* 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, "load_to_array", Image_loadToArray, 3);
    rb_define_singleton_method(cImage, "loadToArray", Image_loadToArray, 3);
    rb_define_singleton_method(cImage, "create_from_array", Image_createFromArray, 3);
    rb_define_singleton_method(cImage, "createFromArray", Image_createFromArray, 3);
    rb_define_singleton_method(cImage, "load_from_file_in_memory", Image_loadFromFileInMemory, 1);
    rb_define_singleton_method(cImage, "loadFromFileInMemory", Image_loadFromFileInMemory, 1);


    /* Imageクラスにメソッド登録*/
    rb_define_private_method( cImage, "initialize", Image_initialize, -1 );
    rb_define_method( cImage, "dispose"   , Image_dispose   , 0 );
    rb_define_method( cImage, "width"     , Image_getWidth  , 0 );
    rb_define_method( cImage, "height"    , Image_getHeight , 0 );
    rb_define_method( cImage, "width="    , Image_setWidth  , 1 );
    rb_define_method( cImage, "height="   , Image_setHeight , 1 );
    rb_define_method( cImage, "x"         , Image_getX      , 0 );
    rb_define_method( cImage, "y"         , Image_getY      , 0 );
    rb_define_method( cImage, "x="        , Image_setX      , 1 );
    rb_define_method( cImage, "y="        , Image_setY      , 1 );
    rb_define_method( cImage, "texture_x" , Image_getX      , 0 );
    rb_define_method( cImage, "texture_y" , Image_getY      , 0 );
    rb_define_method( cImage, "texture_x=", Image_setX      , 1 );
    rb_define_method( cImage, "texture_y=", Image_setY      , 1 );
    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, "box_fill"  , Image_boxFill   , 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, "circle_fill", Image_circleFill, 4 );
    rb_define_method( cImage, "circleFill", Image_circleFill, 4 );
    rb_define_method( cImage, "compare"   , Image_compare   , 3 );
    rb_define_method( cImage, "copy_rect" , Image_copyRect  , -1 );
    rb_define_method( cImage, "copyRect"  , Image_copyRect  , -1 );
    rb_define_method( cImage, "draw"      , Image_draw      , -1 );
    rb_define_method( cImage, "draw_text" , Image_drawFont  , -1 );
    rb_define_method( cImage, "draw_font" , Image_drawFont  , -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, "set_color_key", Image_setColorKey , 1 );
    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, "set_mouse_pos"  , Input_setmousepos     , 2  );
    rb_define_singleton_method( mInput, "setMousePos"    , Input_setmousepos     , 2  );
    rb_define_singleton_method( mInput, "mouse_pos_x"      , Input_getmouseposx    , 0  );
    rb_define_singleton_method( mInput, "mousePosX"      , Input_getmouseposx    , 0  );
    rb_define_singleton_method( mInput, "mouse_pos_y"      , Input_getmouseposy    , 0  );
    rb_define_singleton_method( mInput, "mousePosY"      , Input_getmouseposy    , 0  );
    rb_define_singleton_method( mInput, "key_down?"       , Input_keyDown         , 1  );
    rb_define_singleton_method( mInput, "keyDown?"       , Input_keyDown         , 1  );
    rb_define_singleton_method( mInput, "key_push?"       , Input_keyPush         , 1  );
    rb_define_singleton_method( mInput, "keyPush?"       , Input_keyPush         , 1  );
    rb_define_singleton_method( mInput, "pad_down?"       , Input_padDown         , -1 );
    rb_define_singleton_method( mInput, "padDown?"       , Input_padDown         , -1 );
    rb_define_singleton_method( mInput, "pad_push?"       , Input_padPush         , -1 );
    rb_define_singleton_method( mInput, "padPush?"       , Input_padPush         , -1 );
    rb_define_singleton_method( mInput, "mouse_down?"     , Input_mouseDown       , 1  );
    rb_define_singleton_method( mInput, "mouseDown?"     , Input_mouseDown       , 1  );
    rb_define_singleton_method( mInput, "mouse_push?"     , Input_mousePush       , 1  );
    rb_define_singleton_method( mInput, "mousePush?"     , Input_mousePush       , 1  );
    rb_define_singleton_method( mInput, "mouse_enable="   , Input_enablemouse     , 1  );
    rb_define_singleton_method( mInput, "mouseEnable="   , Input_enablemouse     , 1  );
    rb_define_singleton_method( mInput, "mouse_wheel_pos"  , Input_getmousewheelpos, 0  );
    rb_define_singleton_method( mInput, "mouseWheelPos"  , Input_getmousewheelpos, 0  );
    rb_define_singleton_method( mInput, "mouse_wheel_pos=" , Input_setmousewheelpos, 1  );
    rb_define_singleton_method( mInput, "mouseWheelPos=" , Input_setmousewheelpos, 1  );
    rb_define_singleton_method( mInput, "set_config"      , Input_setconfig       , -1 );
    rb_define_singleton_method( mInput, "setConfig"      , Input_setconfig       , -1 );
    rb_define_singleton_method( mInput, "set_repeat"      , Input_setrepeat       , 2  );
    rb_define_singleton_method( mInput, "setRepeat"      , Input_setrepeat       , 2  );
    rb_define_singleton_method( mInput, "update"         , Input_update          , 0  );
    rb_define_singleton_method( mInput, "set_key_repeat"   , Input_setkeyrepeat    , 3  );
    rb_define_singleton_method( mInput, "setKeyRepeat"   , Input_setkeyrepeat    , 3  );
    rb_define_singleton_method( mInput, "set_pad_repeat"   , Input_setpadrepeat    , -1 );
    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_private_method( cFont, "initialize", Font_initialize, -1 );
    rb_define_method( cFont, "dispose"   , Font_dispose   , 0 );
    rb_define_method( cFont, "get_width" , Font_getWidth  , 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_private_method( cSound, "initialize"   , Sound_initialize   , 1 );
    rb_define_method( cSound, "dispose"      , Sound_dispose   , 0 );
    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, "set_volume"   , Sound_setVolume    , -1 );
    rb_define_method( cSound, "setVolume"    , Sound_setVolume    , -1 );
    rb_define_method( cSound, "start="       , Sound_setStart     , 1 );
    rb_define_method( cSound, "loop_start="  , Sound_setLoopStart , 1 );
    rb_define_method( cSound, "loopStart="   , Sound_setLoopStart , 1 );
    rb_define_method( cSound, "loop_end="    , Sound_setLoopEnd   , 1 );
    rb_define_method( cSound, "loopEnd="     , Sound_setLoopEnd   , 1 );
    rb_define_method( cSound, "loop_count="  , Sound_setLoopCount , 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_private_method( cSoundEffect, "initialize", SoundEffect_initialize, -1 );
    rb_define_method( cSoundEffect, "dispose"   , SoundEffect_dispose   , 0 );
    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 );


    /* キーボードのスキャンコード定数設定 */
    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) );

    rb_define_const( mDXRuby, "FOG_LINEAR"   , INT2FIX(D3DFOG_LINEAR) );
    rb_define_const( mDXRuby, "FOG_EXP"   , INT2FIX(D3DFOG_EXP) );
    rb_define_const( mDXRuby, "FOG_EXP2"   , INT2FIX(D3DFOG_EXP2) );


    /* 例外定義 */
    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"));
    symbol_sub2           = ID2SYM(rb_intern("sub2"));
    symbol_shader         = ID2SYM(rb_intern("shader"));
    symbol_italic         = ID2SYM(rb_intern("italic"));
    symbol_weight         = ID2SYM(rb_intern("weight"));
    symbol_dividex        = ID2SYM(rb_intern("dividex"));
    symbol_dividey        = ID2SYM(rb_intern("dividey"));

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

    /* ロストリスト */
    g_RenderTargetList.pointer = malloc( sizeof(void*) * 16 );
    g_RenderTargetList.count = 0;
    g_RenderTargetList.allocate_size = 16;
    g_ShaderList.pointer = malloc( sizeof(void*) * 16 );
    g_ShaderList.count = 0;
    g_ShaderList.allocate_size = 16;

    g_WindowInfo.x = CW_USEDEFAULT;
    g_WindowInfo.y = CW_USEDEFAULT;
    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.hIcon = 0;
    g_WindowInfo.userloop = 0;
    g_WindowInfo.requestclose = 0;
    g_WindowInfo.render_target = RenderTarget_allocate( cRenderTarget );
    g_WindowInfo.active = 0;
    g_WindowInfo.event_hash = rb_hash_new();

    /* 大域変数に設定 */
    rb_global_variable( &g_WindowInfo.render_target );
    rb_global_variable( &g_WindowInfo.event_hash );

    /* 割り当て初期化 */
    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_hWnd = 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_hWnd == NULL )
    {
        rb_raise( eDXRubyError, "ウィンドウの初期化に失敗しました - CreateWindow" );
    }

    GetWindowRect( g_hWnd, &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_hWnd;
    g_D3DPP.Windowed                   = TRUE;
    g_D3DPP.EnableAutoDepthStencil     = TRUE;
    g_D3DPP.AutoDepthStencilFormat     = D3DFMT_D24S8;
    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_hWnd,
                                       D3DCREATE_MIXED_VERTEXPROCESSING, &g_D3DPP, &g_pD3DDevice );

    if( FAILED( hr ) )
    {
        hr = g_pD3D->lpVtbl->CreateDevice( g_pD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd,
                                           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_hWnd, 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_hWnd, 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 AddRenderTargetList( struct DXRubyRenderTarget *rt )
{
    if( g_RenderTargetList.allocate_size <= g_RenderTargetList.count )
    {
        g_RenderTargetList.allocate_size = g_RenderTargetList.allocate_size * 3 / 2; /* 1.5倍にする */
        g_RenderTargetList.pointer = realloc( g_RenderTargetList.pointer, sizeof( void* ) * g_RenderTargetList.allocate_size );
    }

    g_RenderTargetList.pointer[g_RenderTargetList.count] = rt;
    g_RenderTargetList.count++;
}

static void AddShaderList( struct DXRubyShader *shader )
{
    if( g_ShaderList.allocate_size <= g_ShaderList.count )
    {
        g_ShaderList.allocate_size = g_ShaderList.allocate_size * 3 / 2; /* 1.5倍にする */
        g_ShaderList.pointer = realloc( g_ShaderList.pointer, sizeof( void* ) * g_ShaderList.allocate_size );
    }

    g_ShaderList.pointer[g_ShaderList.count] = shader;
    g_ShaderList.count++;
}

static void DeleteRenderTargetList( struct DXRubyRenderTarget *rt )
{
    int i;

    for( i = 0; i < g_RenderTargetList.count; i++ )
    {
        if( g_RenderTargetList.pointer[i] == rt )
        {
            break;
        }
    }
//    if( i == g_RenderTargetList.count )
//    {
//        rb_raise( eDXRubyError, "内部エラー - DeleteRenderTargetList" );
//    }

    i++;
    for( ; i < g_RenderTargetList.count; i++ )
    {
        g_RenderTargetList.pointer[i - 1] = g_RenderTargetList.pointer[i];
    }

    g_RenderTargetList.count--;
}

static void DeleteShaderList( struct DXRubyShader *rt )
{
    int i;

    for( i = 0; i < g_ShaderList.count; i++ )
    {
        if( g_ShaderList.pointer[i] == rt )
        {
            break;
        }
    }
    if( i == g_ShaderList.count )
    {
        rb_raise( eDXRubyError, "内部エラー - DeleteShaderList" );
    }

    i++;
    for( ; i < g_ShaderList.count; i++ )
    {
        g_ShaderList.pointer[i - 1] = g_ShaderList.pointer[i];
    }

    g_ShaderList.count--;
}


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

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

    rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_IMAGE( vimage );
    image = DXRUBY_GET_STRUCT( Image, vimage );
    DXRUBY_CHECK_DISPOSE( image, texture );

    sprite = (struct DXRubySprite_draw *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_draw ) );

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_draw_func;
    sprite->x = x;
    sprite->y = y;
    sprite->value = vimage;
    sprite->alpha = 0xff;
    sprite->blendflag = 0;

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return;
}


/*--------------------------------------------------------------------
   描画設定（拡大縮小描画） フラグは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;
    struct DXRubySprite_drawEx *sprite;
    struct DXRubyRenderTarget *rt;

    rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_IMAGE( vimage );
    image = DXRUBY_GET_STRUCT( Image, vimage );
    DXRUBY_CHECK_DISPOSE( image, texture );

    sprite = (struct DXRubySprite_drawEx *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_drawEx ) );

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_drawEx_func;
    sprite->x = x;
    sprite->y = y;
    sprite->value = vimage;
    sprite->alpha = 0xff;
    sprite->blendflag = 0;
    sprite->angle  = 0.0f;
    sprite->scalex = scalex;
    sprite->scaley = scaley;
    sprite->centerx = flag ? centerx : image->width / 2;
    sprite->centery = flag ? centery : image->height / 2;

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return;
}


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

    rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_IMAGE( vimage );
    image = DXRUBY_GET_STRUCT( Image, vimage );
    DXRUBY_CHECK_DISPOSE( image, texture );

    sprite = (struct DXRubySprite_drawEx *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_drawEx ) );

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_drawEx_func;
    sprite->x = x;
    sprite->y = y;
    sprite->value = vimage;
    sprite->alpha = 0xff;
    sprite->blendflag = 0;
    sprite->angle  = angle;
    sprite->scalex = 1.0f;
    sprite->scaley = 1.0f;
    sprite->centerx = flag ? centerx : image->width / 2;
    sprite->centery = flag ? centery : image->height / 2;

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return;
}


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

    rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_IMAGE( vimage );
    image = DXRUBY_GET_STRUCT( Image, vimage );
    DXRUBY_CHECK_DISPOSE( image, texture );

    sprite = (struct DXRubySprite_draw *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_draw ) );

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_draw_func;
    sprite->x = x;
    sprite->y = y;
    sprite->value = vimage;
    sprite->alpha = alpha;
    sprite->blendflag = 0;

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return;
}


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

    rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_IMAGE( vimage );
    image = DXRUBY_GET_STRUCT( Image, vimage );
    DXRUBY_CHECK_DISPOSE( image, texture );

    sprite = (struct DXRubySprite_draw *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_draw ) );

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_draw_func;
    sprite->x = x;
    sprite->y = y;
    sprite->value = vimage;
    sprite->alpha = 0xff;
    sprite->blendflag = 4;

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return;
}


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

    rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_IMAGE( vimage );
    image = DXRUBY_GET_STRUCT( Image, vimage );
    DXRUBY_CHECK_DISPOSE( image, texture );

    sprite = (struct DXRubySprite_draw *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_draw ) );

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_draw_func;
    sprite->x = x;
    sprite->y = y;
    sprite->value = vimage;
    sprite->alpha = 0xff;
    sprite->blendflag = 6;

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return;
}


/*--------------------------------------------------------------------
   描画設定（フルオプション）
 ---------------------------------------------------------------------*/
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;
    struct DXRubyRenderTarget *rt;
    struct DXRubySprite_drawEx *sprite;

    rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );

    /* 引数のイメージオブジェクトから中身を取り出す */
    DXRUBY_CHECK_IMAGE( vimage );
    image = DXRUBY_GET_STRUCT( Image, vimage );
    DXRUBY_CHECK_DISPOSE( image, texture );

    sprite = (struct DXRubySprite_drawEx *)RenderTarget_AllocSpriteList( rt, sizeof( struct DXRubySprite_drawEx ) );

    /* DXRubySpriteオブジェクト設定 */
    sprite->func = Window_drawEx_func;
    sprite->x = x;
    sprite->y = y;
    sprite->value = vimage;
    sprite->alpha = alpha;
    sprite->blendflag = blend;
    sprite->angle  = angle;
    sprite->scalex = scalex;
    sprite->scaley = scaley;
    sprite->centerx = flag ? centerx : image->width / 2;
    sprite->centery = flag ? centery : image->height / 2;

    /* リストデータに追加 */
    rt->SpriteList[rt->SpriteCount].sprite = (struct DXRubySprite *)sprite;
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return;
}


/*--------------------------------------------------------------------
    イメージのロック
 ---------------------------------------------------------------------*/
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)Image_release )
    {
      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 );
}

