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

#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 <WINNLS32.H>

/* Ruby1.8の古いほう対応 */
#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_CHECK_DISPOSE( a, b ) {if( a->b == NULL ) { rb_raise( eDXRubyError, "disposed object" );}}
#define NUM2FLOAT( x ) ((float)( FIXNUM_P(x) ? FIX2INT(x) : NUM2DBL(x) ))

#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

static VALUE mDXRuby;       /* DXRubyモジュール     */
static VALUE mWindow;       /* ウィンドウモジュール */
static VALUE mInput;        /* インプットモジュール */
static VALUE cImage;        /* イメージクラス       */
static VALUE cRenderTarget; /* レンダーターゲットクラス */
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 __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 char sys_encode[256];

/* ウィンドウ情報構造体 */
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;       /* 自前ループされてたら1、Window.loopされたら2*/
    int requestclose;   /* ウィンドウが閉じられたら1 */
    VALUE render_target; /* スクリーンレンダーターゲット */
    int active;         /* ゲーム調整用 */
} g_WindowInfo;

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

/* フォントデータ */
struct DXRubyFont {
    LPD3DXFONT pD3DXFont;       /* フォントオブジェクト   */
    HFONT hFont;                /* Image描画に使うフォント  */
    int size;                   /* フォントサイズ */
    VALUE fontname;             /* フォント名称 */
    VALUE weight;               /* 太さ */
    VALUE italic;               /* イタリックフラグ */
};

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

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

/* 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;/* イメージの高さ ここまでImageと共通*/
    IDirect3DSurface9 *surface;

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

    struct DXRubySpriteList *SpriteList;

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

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

    VALUE array; /* drawFontExによる自動生成Imageの置き場所 */
};

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

/* 色 */
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_italic  = Qundef;
static VALUE symbol_weight  = Qundef;
static VALUE symbol_dividex = Qundef;
static VALUE symbol_dividey = Qundef;
static VALUE symbol_edge       = Qundef;
static VALUE symbol_edge_color = Qundef;
static VALUE symbol_edge_width = Qundef;
static VALUE symbol_edge_level = Qundef;
static VALUE symbol_shadow     = Qundef;
static VALUE symbol_shadow_color = Qundef;
static VALUE symbol_shadow_x = Qundef;
static VALUE symbol_shadow_y = Qundef;
static VALUE symbol_shadow_edge = Qundef;

/* プロトタイプ宣言 */
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 RenderTarget_update( VALUE );

static VALUE Image_allocate( VALUE klass );
static VALUE Image_initialize( int argc, VALUE *argv, VALUE obj );
static VALUE Image_dispose( VALUE self );

static void Image_release( struct DXRubyImage* image );
static void RenderTarget_release( struct DXRubyRenderTarget* image );
static void Font_release( struct DXRubyFont* font );

static VALUE RenderTarget_draw     ( int argc, VALUE *argv, VALUE obj );
static VALUE RenderTarget_drawAlpha( int argc, VALUE *argv, VALUE obj );
static VALUE RenderTarget_drawAdd  ( int argc, VALUE *argv, VALUE obj );
static VALUE RenderTarget_drawSub  ( int argc, VALUE *argv, VALUE obj );
static VALUE RenderTarget_drawScale( int argc, VALUE *argv, VALUE obj );
static VALUE RenderTarget_drawRot  ( int argc, VALUE *argv, VALUE obj );
static VALUE RenderTarget_drawEx   ( int argc, VALUE *argv, VALUE obj );
static VALUE RenderTarget_drawFont ( int argc, VALUE *argv, VALUE obj );
static VALUE RenderTarget_drawFontEx ( int argc, VALUE *argv, VALUE obj );
static VALUE RenderTarget_drawMorph( int argc, VALUE *argv, VALUE obj );
static VALUE RenderTarget_drawTile ( int argc, VALUE *argv, VALUE obj );
static VALUE RenderTarget_getMinFilter( VALUE self );
static VALUE RenderTarget_setMinFilter( VALUE self, VALUE filter );
static VALUE RenderTarget_getMagFilter( VALUE self );
static VALUE RenderTarget_setMagFilter( VALUE self, VALUE filter );

static VALUE Image_drawFontEx( int argc, VALUE *argv, VALUE obj );

static VALUE Font_getWidth( VALUE obj, VALUE vstr );
static VALUE Font_getSize( VALUE obj );

static VALUE hash_lookup(VALUE hash, VALUE key);

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

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

    /* 設定変更 */
    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 );
    }

    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の復元に失敗しました" );
    }

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


/*--------------------------------------------------------------------
   （内部関数）ウィンドウプロシージャ
 ---------------------------------------------------------------------*/
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_MOUSEWHEEL:
        g_WindowInfo.mousewheelpos += (short)HIWORD(wParam);
        break;
/*
    case WM_SETCURSOR:
        if( g_pD3DDevice )
        {
            if( LOWORD( lParam ) == HTCLIENT )  // クライアント領域のみ
            {
                if( g_WindowInfo.enablemouse == Qfalse )
                {
                    SetCursor( NULL );              // ウインドウの標準カーソルを消す
                    return TRUE;                    // 設定を終えたことを通知
                }
            }
        }
        break;
*/
    }

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


/*--------------------------------------------------------------------
   画面クリア色取得
 ---------------------------------------------------------------------*/
static VALUE Window_get_bgcolor( VALUE obj )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    return rb_ary_new3( 3, INT2FIX( rt->r ), INT2FIX( rt->g ), INT2FIX( rt->b ) );
}


/*--------------------------------------------------------------------
   画面クリア色指定
 ---------------------------------------------------------------------*/
static VALUE Window_set_bgcolor( VALUE obj, VALUE array )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );

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

/*--------------------------------------------------------------------
   描画設定（通常描画）
 ---------------------------------------------------------------------*/
static VALUE Window_draw( int argc, VALUE *argv, VALUE obj )
{
    RenderTarget_draw( argc, argv, g_WindowInfo.render_target );
    return Qnil;
}

/*--------------------------------------------------------------------
   描画設定（半透明描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawAlpha( int argc, VALUE *argv, VALUE obj )
{
    RenderTarget_drawAlpha( argc, argv, g_WindowInfo.render_target );
    return Qnil;
}


/*--------------------------------------------------------------------
   描画設定（加算合成描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawAdd( int argc, VALUE *argv, VALUE obj )
{
    RenderTarget_drawAdd( argc, argv, g_WindowInfo.render_target );
    return Qnil;
}


/*--------------------------------------------------------------------
   描画設定（減算合成描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawSub( int argc, VALUE *argv, VALUE obj )
{
    RenderTarget_drawSub( argc, argv, g_WindowInfo.render_target );
    return Qnil;
}


/*--------------------------------------------------------------------
   描画設定（拡大縮小描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawScale( int argc, VALUE *argv, VALUE obj )
{
    RenderTarget_drawScale( argc, argv, g_WindowInfo.render_target );
    return Qnil;
}

/*--------------------------------------------------------------------
   描画設定（回転描画）
 ---------------------------------------------------------------------*/
static VALUE Window_drawRot( int argc, VALUE *argv, VALUE obj )
{
    RenderTarget_drawRot( argc, argv, g_WindowInfo.render_target );
    return Qnil;
}

/*--------------------------------------------------------------------
   描画設定（フルオプション）
 ---------------------------------------------------------------------*/
static VALUE Window_drawEx( int argc, VALUE *argv, VALUE obj )
{
    RenderTarget_drawEx( argc, argv, g_WindowInfo.render_target );
    return Qnil;
}


/*--------------------------------------------------------------------
   フォント描画
 ---------------------------------------------------------------------*/
static VALUE Window_drawFont( int argc, VALUE *argv, VALUE obj )
{
    RenderTarget_drawFont( argc, argv, g_WindowInfo.render_target );
    return Qnil;
}

/*--------------------------------------------------------------------
   高品質フォント描画
 ---------------------------------------------------------------------*/
static VALUE Window_drawFontEx( int argc, VALUE *argv, VALUE obj )
{
    RenderTarget_drawFontEx( argc, argv, g_WindowInfo.render_target );
    return Qnil;
}


/*--------------------------------------------------------------------
   描画設定（4点指定）
 ---------------------------------------------------------------------*/
static VALUE Window_drawMorph( int argc, VALUE *argv, VALUE obj )
{
    RenderTarget_drawMorph( argc, argv, g_WindowInfo.render_target );
    return Qnil;
}


/*--------------------------------------------------------------------
   マップ描画
 ---------------------------------------------------------------------*/
static VALUE Window_drawTile( int argc, VALUE *argv, VALUE obj )
{
    RenderTarget_drawTile( argc, argv, g_WindowInfo.render_target );
    return Qnil;
}


/*--------------------------------------------------------------------
   （内部関数）画面更新
 ---------------------------------------------------------------------*/
static VALUE Window_update( VALUE obj )
{
    HRESULT hr;
    static int max;
    __int64 drawStartCounter;
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );

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

                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 );
                }
                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の復元に失敗しました" );
                }

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

    drawStartCounter = GetSystemCounter();

    RenderTarget_update( g_WindowInfo.render_target );

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

    return Qnil;
}


/*--------------------------------------------------------------------
  （内部関数）フレーム調整
 ---------------------------------------------------------------------*/
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->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->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_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_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_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 );

#ifdef HAVE_RB_ENC_STR_NEW
    return rb_funcall( rb_str_new2( buf ), rb_intern( "force_encoding" ), 1, rb_str_new2( sys_encode ) );
#else
    return rb_str_new2( buf );
#endif
}


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

#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vcaption ) != 0 )
    {
        vsjisstr = rb_funcall( vcaption, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
    }
    else
    {
        vsjisstr = vcaption;
    }
#else
    vsjisstr = vcaption;
#endif
    SetWindowText( g_hWnd, RSTRING_PTR( vsjisstr ) );

    return vcaption;
}


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


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

    return scale;
}


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


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


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

    return fps;
}


/*--------------------------------------------------------------------
   縮小フィルタ取得
 ---------------------------------------------------------------------*/
static VALUE Window_getMinFilter( VALUE obj )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    return INT2FIX( rt->minfilter );
}


/*--------------------------------------------------------------------
   縮小フィルタ設定
 ---------------------------------------------------------------------*/
static VALUE Window_setMinFilter( VALUE obj, VALUE vminfilter )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    rt->minfilter = FIX2INT( vminfilter );
    return vminfilter;
}


/*--------------------------------------------------------------------
   拡大フィルタ取得
 ---------------------------------------------------------------------*/
static VALUE Window_getMagFilter( VALUE obj )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    return INT2FIX( rt->magfilter );
}


/*--------------------------------------------------------------------
   拡大フィルタ設定
 ---------------------------------------------------------------------*/
static VALUE Window_setMagFilter( VALUE obj, VALUE vmagfilter )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    rt->magfilter = FIX2INT( vmagfilter );
    return vmagfilter;
}


/*--------------------------------------------------------------------
  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 vfilename )
{
    VALUE vsjisstr;
    Check_Type( vfilename, T_STRING );
#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vfilename ) != 0 )
    {
        vsjisstr = rb_funcall( vfilename, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
    }
    else
    {
        vsjisstr = vfilename;
    }
#else
    vsjisstr = vfilename;
#endif
    g_WindowInfo.hIcon = LoadImage( 0, RSTRING_PTR(vsjisstr), 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;
    VALUE vsjisstr;
    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;

#ifdef HAVE_RB_ENC_STR_NEW
    {
        int i;
        VALUE base_array = rb_ary_new();
        for( i = 0; i < RARRAY_LEN( vfilter ); i++ )
        {
            VALUE child_array = rb_ary_new();
            Check_Type(RARRAY_PTR( vfilter )[i], T_ARRAY);
            rb_ary_push( base_array, child_array );
            if( rb_enc_get_index( rb_ary_entry( RARRAY_PTR( vfilter )[i], 0) ) != 0 )
            {
                vsjisstr = rb_funcall( rb_ary_entry( RARRAY_PTR( vfilter )[i], 0), rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
            }
            else
            {
                vsjisstr = rb_ary_entry( RARRAY_PTR( vfilter )[i], 0);
            }
            rb_ary_push( child_array, vsjisstr );
            if( rb_enc_get_index( rb_ary_entry( RARRAY_PTR( vfilter )[i], 1) ) != 0 )
            {
                vsjisstr = rb_funcall( rb_ary_entry( RARRAY_PTR( vfilter )[i], 1), rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
            }
            else
            {
                vsjisstr = rb_ary_entry( RARRAY_PTR( vfilter )[i], 1);
            }
            rb_ary_push( child_array, vsjisstr );
        }
        filter = rb_ary_join( base_array, rb_str_new( "\0", 1 ) );
        filter = rb_str_cat( filter, "\0", 1 );
        OFN.lpstrFilter = RSTRING_PTR( filter );
    }
#else
    filter = rb_ary_join( vfilter, rb_str_new( "\0", 1 ) );
    filter = rb_str_cat( filter, "\0", 1 );
    OFN.lpstrFilter = RSTRING_PTR( filter );
#endif
    OFN.lpstrFile = buf;
    OFN.nMaxFile = MAX_PATH*2;
    OFN.Flags = OFN_FILEMUSTEXIST;
#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vtitle ) != 0 )
    {
        vsjisstr = rb_funcall( vtitle, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
    }
    else
    {
        vsjisstr = vtitle;
    }
#else
    vsjisstr = vtitle;
#endif
    OFN.lpstrTitle = RSTRING_PTR(vsjisstr);
    OFN.lpstrDefExt = 0;
    if( !GetOpenFileName(&OFN) )
    {
        return Qnil;
    }

#ifdef HAVE_RB_ENC_STR_NEW
    return rb_funcall( rb_str_new2( buf ), rb_intern( "force_encoding" ), 1, rb_str_new2( sys_encode ) );
#else
    return rb_str_new2( buf );
#endif
}


/*--------------------------------------------------------------------
  ファイルセーブダイアログを表示する
 ---------------------------------------------------------------------*/
static VALUE Window_saveDialog(VALUE obj, VALUE vfilter, VALUE vtitle)
{
    OPENFILENAME OFN; 
    char buf[MAX_PATH*2];
    VALUE filter;
    VALUE vsjisstr;
    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;

#ifdef HAVE_RB_ENC_STR_NEW
    {
        int i;
        VALUE base_array = rb_ary_new();
        for( i = 0; i < RARRAY_LEN( vfilter ); i++ )
        {
            VALUE child_array = rb_ary_new();
            Check_Type(RARRAY_PTR( vfilter )[i], T_ARRAY);
            rb_ary_push( base_array, child_array );
            if( rb_enc_get_index( rb_ary_entry( RARRAY_PTR( vfilter )[i], 0) ) != 0 )
            {
                vsjisstr = rb_funcall( rb_ary_entry( RARRAY_PTR( vfilter )[i], 0), rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
            }
            else
            {
                vsjisstr = rb_ary_entry( RARRAY_PTR( vfilter )[i], 0);
            }
            rb_ary_push( child_array, vsjisstr );
            if( rb_enc_get_index( rb_ary_entry( RARRAY_PTR( vfilter )[i], 1) ) != 0 )
            {
                vsjisstr = rb_funcall( rb_ary_entry( RARRAY_PTR( vfilter )[i], 1), rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
            }
            else
            {
                vsjisstr = rb_ary_entry( RARRAY_PTR( vfilter )[i], 1);
            }
            rb_ary_push( child_array, vsjisstr );
        }
        filter = rb_ary_join( base_array, rb_str_new( "\0", 1 ) );
        filter = rb_str_cat( filter, "\0", 1 );
        OFN.lpstrFilter = RSTRING_PTR( filter );
    }
#else
    filter = rb_ary_join( vfilter, rb_str_new( "\0", 1 ) );
    filter = rb_str_cat( filter, "\0", 1 );
    OFN.lpstrFilter = RSTRING_PTR( filter );
#endif
    OFN.lpstrFile = buf;
    OFN.nMaxFile = MAX_PATH*2;
    OFN.Flags = OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT;
#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vtitle ) != 0 )
    {
        vsjisstr = rb_funcall( vtitle, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
    }
    else
    {
    vsjisstr = vtitle;
    }
#else
    vsjisstr = vtitle;
#endif
    OFN.lpstrTitle = RSTRING_PTR(vsjisstr);
    OFN.lpstrDefExt = 0;
    if( !GetSaveFileName(&OFN) )
    {
        return Qnil;
    }

#ifdef HAVE_RB_ENC_STR_NEW
    return rb_funcall( rb_str_new2( buf ), rb_intern( "force_encoding" ), 1, rb_str_new2( sys_encode ) );
#else
    return rb_str_new2( buf );
#endif
}


/*--------------------------------------------------------------------
   終了時に実行する
 ---------------------------------------------------------------------*/
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->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 vfilename, vformat, vsjisstr;
    int dispx;
    int dispy;

    rb_scan_args( argc, argv, "11", &vfilename, &vformat );

    Check_Type( vfilename, 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);
        if( p.x < 0 )
        {
            p.x = 0;
        }
        if( p.y < 0 )
        {
            p.y = 0;
        }
        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);
        dispx = GetSystemMetrics(SM_CXSCREEN);
        dispy = GetSystemMetrics(SM_CYSCREEN);
        if( p.x >= dispx )
        {
            p.x = dispx - 1;
        }
        if( p.y >= dispy )
        {
            p.y = dispy - 1;
        }
        rect.right = p.x; rect.bottom = p.y;
    }
#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vfilename ) != 0 )
    {
        vsjisstr = rb_funcall( vfilename, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
    }
    else
    {
        vsjisstr = vfilename;
    }
#else
    vsjisstr = vfilename;
#endif
    hr = D3DXSaveSurfaceToFile(
            RSTRING_PTR( vsjisstr ),                            /* 保存ファイル名 */
            vformat == Qnil ? D3DXIFF_JPG : FIX2INT( vformat ), /* ファイルフォーマット */
            pSurface,                                           /* 保存するサーフェス */
            NULL,                                               /* パレット */
            g_D3DPP.Windowed ? &rect : NULL);                   /* 保存領域 */
    RELEASE(pSurface);
	if( FAILED( hr ) )
	{
        rb_raise( eDXRubyError, "キャプチャに失敗しました - D3DXSaveSurfaceToFile" );
    }

	return obj;
}


/*--------------------------------------------------------------------
   使用可能なスクリーンサイズを取得する
 ---------------------------------------------------------------------*/
static VALUE Window_getScreenModes( VALUE obj )
{
    D3DDISPLAYMODE d3ddm;
    int count , max, oldHeight, oldWidth;
    VALUE ary, temp;

    ary = rb_ary_new();
    max = g_pD3D->lpVtbl->GetAdapterModeCount( g_pD3D, D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8 );

    for( count = 0 ; count < max ; count++ )
    {
        temp = rb_ary_new();
        g_pD3D->lpVtbl->EnumAdapterModes( g_pD3D, D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8 , count , &d3ddm);
        rb_ary_push( temp, INT2NUM(d3ddm.Width) );
        rb_ary_push( temp, INT2NUM(d3ddm.Height) );
//        rb_ary_push( temp, INT2NUM(d3ddm.RefreshRate) );
        rb_ary_push( ary, temp );
    }

    rb_funcall( ary, rb_intern("uniq!"), 0 );

    return ary;
}


/*********************************************************************
 * RenderTargetクラス
 *
 * レンダーターゲットになれるImageクラス。
 * 直接編集機能は無いが、draw系メソッドでハードウェア描画ができる。
 *********************************************************************/

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

/*--------------------------------------------------------------------
   参照されなくなったときに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;
    };
    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, surface );
    RenderTarget_free( rt );
    return self;
}

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

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

    rb_gc_mark( rt->array );
}

/*--------------------------------------------------------------------
   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->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->a = 0;
    rt->r = 0;
    rt->g = 0;
    rt->b = 0;
    rt->array = Qnil;
    rt->array = rb_ary_new();

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

    /* テクスチャメモリ取得 */
    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;

    rt->texture = texture;
    rt->x = 0;
    rt->y = 0;
    rt->width = width;
    rt->height = height;
    if( vary != Qnil )
    {
        Check_Type( vary, T_ARRAY );
        if( RARRAY_LEN( vary ) == 4 )
        {
            rt->a = NUM2INT( rb_ary_entry(vary, 0) );
            rt->r = NUM2INT( rb_ary_entry(vary, 1) );
            rt->g = NUM2INT( rb_ary_entry(vary, 2) );
            rt->b = NUM2INT( rb_ary_entry(vary, 3) );
        }
        else
        {
            rt->a = 255;
            rt->r = NUM2INT( rb_ary_entry(vary, 0) );
            rt->g = NUM2INT( rb_ary_entry(vary, 1) );
            rt->b = NUM2INT( rb_ary_entry(vary, 2) );
        }
    }

    AddRenderTargetList( rt );

    g_pD3DDevice->lpVtbl->SetRenderTarget( g_pD3DDevice, 0, rt->surface );
    g_pD3DDevice->lpVtbl->Clear( g_pD3DDevice, 0, NULL, D3DCLEAR_TARGET,
                                 D3DCOLOR_ARGB( rt->a, rt->r, rt->g, rt->b ), 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, surface );

    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, surface );
    return INT2FIX( rt->x );
}


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


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


/*--------------------------------------------------------------------
   レンダーターゲットのサイズ（高さ）を返す。
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_getHeight( VALUE self )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
        DXRUBY_CHECK_DISPOSE( rt, surface );
    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, surface );
    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, surface );
    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, surface );
    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, surface );
    rt->height = NUM2INT( vheight );
    return vheight;
}


/*--------------------------------------------------------------------
   スプライトリストのメモリ確保
 ---------------------------------------------------------------------*/
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_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;
        }
    }
}


/*--------------------------------------------------------------------
   画面クリア色取得
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_get_bgcolor( VALUE self )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
        DXRUBY_CHECK_DISPOSE( rt, surface );
    return rb_ary_new3( 4, INT2FIX( rt->a ), INT2FIX( rt->r ), INT2FIX( rt->g ), INT2FIX( rt->b ) );
}


/*--------------------------------------------------------------------
   画面クリア色指定
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_set_bgcolor( VALUE self, VALUE array )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
        DXRUBY_CHECK_DISPOSE( rt, surface );

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

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 RenderTarget_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 RenderTarget_draw( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;
    struct DXRubySprite_draw *sprite;
    float z;
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
        DXRUBY_CHECK_DISPOSE( rt, surface );

    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 = RenderTarget_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 : NUM2FLOAT( argv[3] );
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return obj;
}

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

    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 = RenderTarget_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 : NUM2FLOAT( argv[4] );
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return obj;
}


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

    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 = RenderTarget_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 : NUM2FLOAT( argv[3] );
    rt->SpriteList[rt->SpriteCount].z = z;
    sprite->z = z;
    rt->SpriteCount++;

    return obj;
}


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

    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 = RenderTarget_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 : NUM2FLOAT( argv[3] );
    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 RenderTarget_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 RenderTarget_drawScale( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyImage *image;
    struct DXRubySprite_drawEx *sprite;
    float z;
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
        DXRUBY_CHECK_DISPOSE( rt, surface );

    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 = RenderTarget_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 = NUM2FLOAT( argv[3] );
    sprite->scaley = NUM2FLOAT( argv[4] );
    sprite->centerx = argc < 6 || argv[5] == Qnil  ? image->width / 2 : NUM2FLOAT( argv[5] );
    sprite->centery = argc < 7 || argv[6] == Qnil  ? image->height / 2 : NUM2FLOAT( argv[6] );

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

    return obj;
}

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

    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 = RenderTarget_drawEx_func;
    sprite->x = NUM2INT( argv[0] );
    sprite->y = NUM2INT( argv[1] );
    sprite->value = argv[2];
    sprite->alpha = 0xff;
    sprite->blendflag = 0;
    sprite->angle  = NUM2FLOAT( argv[3] );
    sprite->scalex = 1.0f;
    sprite->scaley = 1.0f;
    sprite->centerx = argc < 5 || argv[4] == Qnil  ? image->width / 2 : NUM2FLOAT( argv[4] );
    sprite->centery = argc < 5 || argv[5] == Qnil  ? image->height / 2 : NUM2FLOAT( argv[5] );

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

    return obj;
}

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

    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 = hash_lookup( option, symbol_blend );
    vangle = hash_lookup( option, symbol_angle );
    valpha = hash_lookup( option, symbol_alpha );
    vscalex = hash_lookup( option, symbol_scalex );
    vscaley = hash_lookup( option, symbol_scaley );
    vcenterx = hash_lookup( option, symbol_centerx );
    vcentery = hash_lookup( option, symbol_centery );
    vz = hash_lookup( option, symbol_z );

    {
        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 = RenderTarget_drawEx_func;
        sprite->x = NUM2INT( argv[0] );
        sprite->y = NUM2INT( argv[1] );
        sprite->angle   = (vangle   == Qnil ? 0.0f             : NUM2FLOAT( vangle   ));
        sprite->scalex  = (vscalex  == Qnil ? 1.0f             : NUM2FLOAT( vscalex  ));
        sprite->scaley  = (vscaley  == Qnil ? 1.0f             : NUM2FLOAT( vscaley  ));
        sprite->centerx = (vcenterx == Qnil ? image->width / 2 : NUM2FLOAT( vcenterx ));
        sprite->centery = (vcentery == Qnil ? image->height / 2 : NUM2FLOAT( 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 : NUM2FLOAT( vz );
        rt->SpriteList[rt->SpriteCount].z = z;
        sprite->z = z;
    }

    rt->SpriteCount++;

    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 RenderTarget_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 RenderTarget_drawFont( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubySprite_drawFont *sprite;
    struct DXRubyFont *font;
    VALUE vcolor;
    int cr, cg, cb;
    VALUE vz, vangle, vscalex, vscaley, valpha, vcenterx, vcentery, vblend;
    VALUE voption;
    struct DXRubyFontSprite *fontsprite;
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    VALUE vsjisstr;

    DXRUBY_CHECK_DISPOSE( rt, surface );

    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 )
    {
        voption = rb_hash_new();
    }
    else
    {
        Check_Type( argv[4], T_HASH );
        voption = argv[4];
    }

    vblend = hash_lookup( voption, symbol_blend );
    vangle = hash_lookup( voption, symbol_angle );
    valpha = hash_lookup( voption, symbol_alpha );
    vscalex = hash_lookup( voption, symbol_scalex );
    vscaley = hash_lookup( voption, symbol_scaley );
    vcenterx = hash_lookup( voption, symbol_centerx );
    vcentery = hash_lookup( voption, symbol_centery );
    vz = hash_lookup( voption, symbol_z );
    vcolor = hash_lookup( voption, 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 = RenderTarget_drawFont_func;
    sprite->x = NUM2INT( argv[0] );
    sprite->y = NUM2INT( argv[1] );
    sprite->angle   = (vangle   == Qnil ? 0.0f : NUM2FLOAT( vangle ));
    sprite->scalex  = (vscalex  == Qnil ? 1.0f : NUM2FLOAT( vscalex ));
    sprite->scaley  = (vscaley  == Qnil ? 1.0f : NUM2FLOAT( vscaley ));
    sprite->centerx = (vcenterx == Qnil ? 0.0f : NUM2FLOAT( vcenterx ));;
    sprite->centery = (vcentery == Qnil ? 0.0f : NUM2FLOAT( 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[3];

#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( argv[2] ) != 0 )
    {
        vsjisstr = rb_funcall( argv[2], rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
    }
    else
    {
        vsjisstr = argv[2];
    }
#else
    vsjisstr = argv[2];
#endif
    lstrcpy( sprite->str, RSTRING_PTR( vsjisstr ) );  /* 文字列の保存 */
    sprite->str[RSTRING_LEN(vsjisstr)] = ' ';    /* イタリック対策にスペース追加 */
    sprite->str[RSTRING_LEN(vsjisstr)+1] = 0;

    if( vcolor != Qnil )
    {
        Check_Type( vcolor, T_ARRAY );
        if( RARRAY_LEN( vcolor ) == 4 )
        {
            sprite->alpha = NUM2INT( rb_ary_entry( vcolor, 0 ) );
            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;
    }
    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 = vz == Qnil ? 0.0f : NUM2FLOAT( vz );
    rt->SpriteCount++;

    return obj;
}


/*--------------------------------------------------------------------
   高品質フォント描画
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_drawFontEx( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, obj );
    int fontsize, edge_width, shadow_x, shadow_y;
    VALUE vimage, voption, vedge_width, vedge, vshadow;

    DXRUBY_CHECK_DISPOSE( rt, surface );
    DXRUBY_CHECK_TYPE( Font, argv[3] );

    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 )
    {
        voption = rb_hash_new();
    }
    else
    {
        Check_Type( argv[4], T_HASH );
        voption = argv[4];
    }

    /* 内部Imageオブジェクト生成*/

    /* エッジオプション補正 */
    vedge = hash_lookup( voption, symbol_edge );
    if( vedge == Qnil || vedge == Qfalse )
    {
        edge_width = 0;
    }
    else
    {
        vedge_width = hash_lookup( voption, symbol_edge_width );
        edge_width = vedge_width == Qnil ? 2 : NUM2INT( vedge_width );
    }

    /* 影オプション補正 */
    vshadow = hash_lookup( voption, symbol_shadow );
    if( vshadow == Qnil || vshadow == Qfalse )
    {
        shadow_x = 0;
        shadow_y = 0;
    }
    else
    {
        VALUE vshadow_x, vshadow_y;

        vshadow_x = hash_lookup( voption, symbol_shadow_x );
        shadow_x = vshadow_x == Qnil ? NUM2INT( Font_getSize( argv[3] ) ) / 24 + 1 : NUM2INT(vshadow_x);

        vshadow_y = hash_lookup( voption, symbol_shadow_y );
        shadow_y = vshadow_y == Qnil ? NUM2INT( Font_getSize( argv[3] ) ) / 24 + 1 : NUM2INT(vshadow_y);
    }

    fontsize = NUM2INT( Font_getSize( argv[3] ) );
    vimage = Image_allocate( cImage );
    {
        VALUE arr[2] = {INT2FIX(NUM2INT( Font_getWidth( argv[3], argv[2] ) ) + fontsize / 2 + edge_width * 2 + shadow_x), INT2FIX(fontsize + edge_width * 2 + shadow_y)};
        Image_initialize( 2, arr, vimage );
    }

    /* Imageに描画 */
    {
        VALUE arr[5] = {INT2FIX(0), INT2FIX(0), argv[2], argv[3], voption};
        Image_drawFontEx( 5, arr, vimage );
    }

    /* RenderTarget_drawEx呼び出し */
    {
        VALUE arr[4] = {argv[0], argv[1], vimage, voption};
        RenderTarget_drawEx( 4, arr, obj );
    }

    rb_ary_push( rt->array, vimage );

    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;
    char colorflag, r, g, b;
};

static void RenderTarget_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,sprite->r,sprite->g,sprite->b);
            /* Ｚ座標 */
            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);
        }
    }

    if( sprite->colorflag == 1 )
    {
        g_pD3DDevice->lpVtbl->SetTextureStageState( g_pD3DDevice, 0, D3DTSS_COLOROP, D3DTOP_SELECTARG2 );
    }

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

    if( sprite->colorflag == 1 )
    {
        g_pD3DDevice->lpVtbl->SetTextureStageState( g_pD3DDevice, 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
    }
}

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

    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 = hash_lookup( voption, symbol_blend );
    valpha = hash_lookup( voption, symbol_alpha );
    vdividex = hash_lookup( voption, symbol_dividex );
    vdividey = hash_lookup( voption, symbol_dividey );
    vz = hash_lookup( voption, symbol_z );
    vcolor = hash_lookup( voption, symbol_color );

    /* 引数のイメージオブジェクトから中身を取り出す */
    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 = RenderTarget_drawMorph_func;
    sprite->x1 = NUM2FLOAT( argv[0] );
    sprite->y1 = NUM2FLOAT( argv[1] );
    sprite->x2 = NUM2FLOAT( argv[2] );
    sprite->y2 = NUM2FLOAT( argv[3] );
    sprite->x3 = NUM2FLOAT( argv[4] );
    sprite->y3 = NUM2FLOAT( argv[5] );
    sprite->x4 = NUM2FLOAT( argv[6] );
    sprite->y4 = NUM2FLOAT( 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以下は指定できません");
    }

    if( vcolor != Qnil )
    {
        Check_Type( vcolor, T_ARRAY );
        if( RARRAY_LEN( vcolor ) < 4 )
        {
            sprite->r = NUM2INT( rb_ary_entry(vcolor, 0) );
            sprite->g = NUM2INT( rb_ary_entry(vcolor, 1) );
            sprite->b = NUM2INT( rb_ary_entry(vcolor, 2) );
        }
        else
        {
            sprite->alpha = NUM2INT( rb_ary_entry(vcolor, 0) ) * sprite->alpha / 255;
            sprite->r = NUM2INT( rb_ary_entry(vcolor, 1) );
            sprite->g = NUM2INT( rb_ary_entry(vcolor, 2) );
            sprite->b = NUM2INT( rb_ary_entry(vcolor, 3) );
        }
        sprite->colorflag = 1;
    }
    else
    {
        sprite->r = sprite->g = sprite->b = 255;
        sprite->colorflag = 0;
    }


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

    return obj;
}


/*--------------------------------------------------------------------
   マップ描画
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_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 = DXRUBY_GET_STRUCT( RenderTarget, obj );
    DXRUBY_CHECK_DISPOSE( rt, surface );

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

    if( RARRAY_LEN( vmap ) == 0 )
    {
        return Qnil;
    }

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

    startx = NUM2INT( vstartx );
    starty = NUM2INT( vstarty );
    z = vz == Qnil ? 0.0f : NUM2FLOAT( 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);

        if( RARRAY_LEN( arr1[my] ) == 0 )
        {
            continue;
        }

        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 = RenderTarget_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 Qnil;
}


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

    DXRUBY_CHECK_DISPOSE( rt, surface );

    /* シーンのクリア */
    {
        D3DVIEWPORT9 vp;
        vp.X       = x_2d = 0;
        vp.Y       = y_2d = 0;
        if( rt->texture == NULL )
        {
            vp.Width   = width_2d = g_D3DPP.BackBufferWidth;
            vp.Height  = height_2d = g_D3DPP.BackBufferHeight;
        }
        else
        {
            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->SetViewport( g_pD3DDevice, &vp );
        g_pD3DDevice->lpVtbl->Clear( g_pD3DDevice, 0, NULL, D3DCLEAR_TARGET,
                                     D3DCOLOR_ARGB( rt->a, rt->r, rt->g, rt->b ), 1.0f, 0 );
    }

    /* シーンの描画開始 */
    if( SUCCEEDED( g_pD3DDevice->lpVtbl->BeginScene( g_pD3DDevice ) ) )
    {
        i = 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_FALSE );
        g_pD3DDevice->lpVtbl->SetRenderState( g_pD3DDevice, D3DRS_ZWRITEENABLE, FALSE );
        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_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_CULLMODE, D3DCULL_NONE);

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

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

            RenderTarget_SortSpriteList( rt );

            /* 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 );

            for( i = 0; 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;
                    }
                }

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

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

    rt->SpriteCount = 0;
    rt->SpriteSize = 0;

    for( i = 0; i < RARRAY_LEN(rt->array); i++ )
    {
        Image_dispose( RARRAY_PTR(rt->array)[i] );
    }
    rb_ary_clear( rt->array );

    return self;
}


/*--------------------------------------------------------------------
   縮小フィルタ取得
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_getMinFilter( VALUE self )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    DXRUBY_CHECK_DISPOSE( rt, surface );
    return INT2FIX( rt->minfilter );
}


/*--------------------------------------------------------------------
   縮小フィルタ設定
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_setMinFilter( VALUE self, VALUE minfilter )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    DXRUBY_CHECK_DISPOSE( rt, surface );
    rt->minfilter = FIX2INT( minfilter );
    return minfilter;
}


/*--------------------------------------------------------------------
   拡大フィルタ取得
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_getMagFilter( VALUE self )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    DXRUBY_CHECK_DISPOSE( rt, surface );
    return INT2FIX( rt->minfilter );
}


/*--------------------------------------------------------------------
   拡大フィルタ設定
 ---------------------------------------------------------------------*/
static VALUE RenderTarget_setMagFilter( VALUE self, VALUE magfilter )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, self );
    DXRUBY_CHECK_DISPOSE( rt, surface );
    rt->magfilter = FIX2INT( magfilter );
    return magfilter;
}



/*********************************************************************
 * 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クラスのdelayed_dispose。
 ---------------------------------------------------------------------*/
static VALUE Image_delayed_dispose( VALUE self )
{
    struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
    struct DXRubyImage *image = DXRUBY_GET_STRUCT( Image, self );
    DXRUBY_CHECK_DISPOSE( image, texture );

    rb_ary_push( rt->array, self );

    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 )
    {
        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 vfilename, vx, vy, vwidth, vheight, obj, vsjisstr;
    int x, y, width, height;

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

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

    Check_Type(vfilename, T_STRING);

    /* ファイル情報取得 */
#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vfilename ) != 0 )
    {
        vsjisstr = rb_funcall( vfilename, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
    }
    else
    {
        vsjisstr = vfilename;
    }
#else
    vsjisstr = vfilename;
#endif

    hr = D3DXGetImageInfoFromFile( RSTRING_PTR( vsjisstr ), &srcinfo );

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

    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( vsjisstr ), &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 vstr )
{
    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(vstr, T_STRING);

    /* ファイル情報取得 */
    size = RSTRING_LEN( vstr );

    hr = D3DXGetImageInfoFromFileInMemory( RSTRING_PTR( vstr ), 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( vstr ), 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 vfilename, VALUE vx, VALUE vy )
{
    struct DXRubyImage *image;
    struct DXRubyTexture *texture;
    int i, j, x, y;
    VALUE obj;
    VALUE varray, vsjisstr;
    D3DXIMAGE_INFO srcinfo;
	D3DSURFACE_DESC desc;
    HRESULT hr;

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

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

    /* ファイル情報取得 */
#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vfilename ) != 0 )
    {
        vsjisstr = rb_funcall( vfilename, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
    }
    else
    {
        vsjisstr = vfilename;
    }
#else
    vsjisstr = vfilename;
#endif
    hr = D3DXGetImageInfoFromFile( RSTRING_PTR( vsjisstr ), &srcinfo );

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

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

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

    /* Ruby配列作成 */
    varray = 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(varray, obj);
            g_iRefAll++;
        }
    }

    return varray;
}


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


///*--------------------------------------------------------------------
//   トランジション用画像生成
// ---------------------------------------------------------------------*/
//static VALUE Image_transition( int argc, VALUE *argv, VALUE klass )
//{
//    struct DXRubyImage *srcimage;
//    struct DXRubyImage *dstimage;
//    struct DXRubyImage *ruleimage;
//    D3DLOCKED_RECT srctrect;
//    D3DLOCKED_RECT dsttrect;
//    D3DLOCKED_RECT ruletrect;
//    struct DXRubyTexture *texture;
//    D3DSURFACE_DESC desc;
//    HRESULT hr;
//
//    VALUE vsrc, vdst, vrule, vcount, vparam, voption;
//
//    int x, y, param, count, width, height, cmp;
//    RECT srcrect;
//    RECT dstrect;
//    RECT rulerect;
//
//    int *psrc;
//    int *pdst;
//    int *prule;
//    struct DXRubyColor c;
//
//    /* voptionはtrueでだんだん表示する(After)、falseでだんだん消す(Before) */
//    /* 切り替え後の画像を処理するか、切り替え前の画像を処理するかの違い。デフォルトはfalse(Before)つまり古い画像を消す動作 */
//    rb_scan_args( argc, argv, "41", &vsrc, &vrule, &vcount, &vparam, &voption );
//
//    DXRUBY_CHECK_IMAGE( vsrc );
//
//    /* ソース画像 */
//    /* とりあえずImage専用ロジック */
//    Data_Get_Struct( vsrc, struct DXRubyImage, srcimage );
//    DXRUBY_CHECK_DISPOSE( srcimage, texture );
//
//    /* ルール画像 */
//    DXRUBY_CHECK_TYPE( Image, vrule );
//    Data_Get_Struct( vrule, struct DXRubyImage, ruleimage );
//    DXRUBY_CHECK_DISPOSE( ruleimage, texture );
//
//    /* Imageオブジェクト生成 */
//    vdst = Image_allocate( cImage );
//    dstimage = DXRUBY_GET_STRUCT( Image, vdst );
//
//    if( srcimage == ruleimage ) rb_raise( eDXRubyError, "画像とルールに同じImageオブジェクトが指定されています - Image_get_transition_image" );
//
//    g_iRefAll++;
//
//    /* テクスチャメモリ取得 */
//    texture = (struct  DXRubyTexture *)malloc( sizeof( struct DXRubyTexture ) );
//
//    if( texture == NULL )
//    {
//        rb_raise( eDXRubyError, "画像用メモリの取得に失敗しました - Image_initialize" );
//    }
//
//    /* テクスチャオブジェクトを作成する */
//    hr = D3DXCreateTexture( g_pD3DDevice, srcimage->width, srcimage->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;
//
//    dstimage->texture = texture;
//    dstimage->x = 0;
//    dstimage->y = 0;
//    dstimage->width = srcimage->width;
//    dstimage->height = srcimage->height;
//
//    /* count */
//    count = NUM2INT( vcount );
//
//    /* param */
//    param = NUM2INT( vparam );
//
//    /* ロック矩形生成 */
//    dstrect.left = 0;
//    dstrect.top = 0;
//    dstrect.right = dstimage->width;
//    dstrect.bottom = dstimage->height;
//    srcrect.left = srcimage->x;
//    srcrect.top = srcimage->y;
//    srcrect.right = srcimage->x + srcimage->width;
//    srcrect.bottom = srcimage->y + srcimage->height;
//    rulerect.left = ruleimage->x;
//    rulerect.top = ruleimage->y;
//    rulerect.right = ruleimage->x + ruleimage->width;
//    rulerect.bottom = ruleimage->y + ruleimage->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 );
//    ruleimage->texture->pD3DTexture->lpVtbl->LockRect( ruleimage->texture->pD3DTexture, 0, &ruletrect, &rulerect, D3DLOCK_READONLY );
//
//    cmp = count - param;
//    c.alpha = c.red = c.green = c.blue = 0;
//    if( RTEST( voption ) )
//    {
//        for( y = 0; y < dstimage->height; y++)
//        {
//            psrc = (int*)((char *)srctrect.pBits + y * srctrect.Pitch);
//            pdst = (int*)((char *)dsttrect.pBits + y * dsttrect.Pitch);
//            prule = (int*)((char *)ruletrect.pBits + (y % (int)ruleimage->height) * ruletrect.Pitch);
//            for( x = 0; x < dstimage->width; x++)
//            {
//                struct DXRubyColor r = *((struct DXRubyColor*)(prule + (x % (int)ruleimage->width)));
//
//                if( (int)r.red <= cmp )
//                {
//                    *((struct DXRubyColor*)(pdst)) = *((struct DXRubyColor*)(psrc));
//                }
//                else if( (int)r.red > count )
//                {
//                    *((struct DXRubyColor*)(pdst)) = c;
//                }
//                else
//                {
//                    struct DXRubyColor s = *((struct DXRubyColor*)(psrc));
//                    s.alpha = (int)s.alpha * (cmp - (int)r.red) / param;
//                    *((struct DXRubyColor*)(pdst)) = s;
//                }
//
//                psrc++;
//                pdst++;
//            }
//        }
//    }
//    else
//    {
//        for( y = 0; y < dstimage->height; y++)
//        {
//            psrc = (int*)((char *)srctrect.pBits + y * srctrect.Pitch);
//            pdst = (int*)((char *)dsttrect.pBits + y * dsttrect.Pitch);
//            prule = (int*)((char *)ruletrect.pBits + (y % (int)ruleimage->height) * ruletrect.Pitch);
//            for( x = 0; x < dstimage->width; x++)
//            {
//                struct DXRubyColor r = *((struct DXRubyColor*)(prule + (x % (int)ruleimage->width)));
//
//                if( (int)r.red < cmp )
//                {
//                    *((struct DXRubyColor*)(pdst)) = c;
//                }
//                else if( (int)r.red >= count )
//                {
//                    *((struct DXRubyColor*)(pdst)) = *((struct DXRubyColor*)(psrc));
//                }
//                else
//                {
//                    struct DXRubyColor s = *((struct DXRubyColor*)(psrc));
//                    s.alpha = (int)s.alpha * ((int)r.red - cmp) / param;
//                    *((struct DXRubyColor*)(pdst)) = s;
//                }
//
//                psrc++;
//                pdst++;
//            }
//        }
//    }
//    dstimage->texture->pD3DTexture->lpVtbl->UnlockRect( dstimage->texture->pD3DTexture, 0 );
//    srcimage->texture->pD3DTexture->lpVtbl->UnlockRect( srcimage->texture->pD3DTexture, 0 );
//    ruleimage->texture->pD3DTexture->lpVtbl->UnlockRect( ruleimage->texture->pD3DTexture, 0 );
//
//    return vdst;
//}


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


/*--------------------------------------------------------------------
   ImgaeオブジェクトのsliceToArray
 ---------------------------------------------------------------------*/
static VALUE Image_sliceToArray( VALUE self, VALUE vx, VALUE vy )
{
    struct DXRubyImage *srcimage;
    struct DXRubyTexture *texture;
    HRESULT hr;
    int x, y, i, j;
    VALUE array, obj;

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

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

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

    texture = srcimage->texture;
    texture->refcount += x * y;

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

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

            image->texture = texture;
            image->x = srcimage->x + j * srcimage->width / x;
            image->y = srcimage->y + i * srcimage->height / y;
            image->width = srcimage->width / x;
            image->height = srcimage->height / y;

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

    return array;
}


/*--------------------------------------------------------------------
   イメージのデータ取得
 ---------------------------------------------------------------------*/
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 )
            {
                *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 );
    DXRUBY_CHECK_TYPE( Image, data );
    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/clone
 ---------------------------------------------------------------------*/
static VALUE Image_initialize_copy( VALUE self, VALUE obj )
{
    struct DXRubyImage *srcimage;
    struct DXRubyImage *dstimage;
    struct DXRubyTexture *texture;
    D3DLOCKED_RECT srctrect;
    D3DLOCKED_RECT dsttrect;
    int i, j;
    RECT srcrect;
    RECT dstrect;
    HRESULT hr;
    D3DSURFACE_DESC desc;
    int *psrc;
    int *pdst;

    if( TYPE(obj) != T_DATA || RDATA(obj)->dfree != Image_release )
    {
        rb_raise( eDXRubyError, "wrong argument type" );
    }

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

    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 );
    DXRUBY_CHECK_TYPE( Image, data );
    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 + s.alpha * 255;

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

                *((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 void get_color( VALUE vcolor, int *cr, int *cg, int *cb )
{
    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));
    }
}
static void drawfont_sub( int blackboxX, int blackboxY, int baseX, int baseY, int pitch,  char *buf, D3DLOCKED_RECT *dsttrect, int cr, int cg, int cb, int width, int height )
{
    int v, u, xx, yy;

    for( v = 0; v < blackboxY; v++ )
    {
        int yy = baseY + v;
        if( yy < 0 )
        {
            continue;
        }
        if( yy >= height )
        {
            break;
        }
        for( u = 0; u < blackboxX; u++ )
        {
            int xx, src;

            xx = baseX + u;
            if( xx < 0 )
            {
                continue;
            }
            if( xx >= width )
            {
                break;
            }

            src = (int)buf[ u + v * pitch ] * 255 / 64;

            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;
                int temp = (255 - src) * d.alpha + src * 255;

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

                *((struct DXRubyColor*)((char*)dsttrect->pBits + xx * 4 + yy * dsttrect->Pitch)) = data;
            }
        }
    }
}
/*--------------------------------------------------------------------
   イメージにフォント描画
 ---------------------------------------------------------------------*/
static VALUE Image_drawFont( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyFont *font;
    struct DXRubyImage *image;
    VALUE vx, vy, vcolor, vstr, data;
    int cr=255, cg=255, cb=255, 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 );
    DXRUBY_CHECK_TYPE( Font, data );
    Data_Get_Struct( data, struct DXRubyFont, font );
    DXRUBY_CHECK_DISPOSE( font, pD3DXFont );

    x = NUM2INT( vx );
    y = NUM2INT( vy );
    if( vcolor != Qnil )
    {
        get_color( vcolor, &cr, &cg, &cb );
    }

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

    /* 描画文字のUTF16LE化 */
#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vstr ) != 0 )
    {
        vwidestr = rb_funcall( vstr, rb_intern( "encode" ), 1, rb_str_new2( "UTF-16LE" ) );
    }
    else
    {
        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 );
    }
#else
    {
        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 );
    }
#endif
    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 );

        if( bufsize > 0 )
        {
            unsigned char *buf = alloca( bufsize );
            int v, u;

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

            drawfont_sub( gm.gmBlackBoxX, gm.gmBlackBoxY, x + gm.gmptGlyphOrigin.x, y + tm.tmAscent - gm.gmptGlyphOrigin.y, (gm.gmBlackBoxX + 3) & 0xfffc, buf, &dsttrect, cr, cg, cb, image->width, image->height );
        }
        x += gm.gmCellIncX;
        if( x >= image->width )
        {
            break;
        }
    }

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

    ReleaseDC( g_hWnd, hDC );
    return obj;
}


/* fast_int_hypot from http://demo.and.or.jp/makedemo/effect/math/hypot/fast_hypot.c */
int fast_int_hypot(int lx, int ly)
{
	int len1, len2,t,length;

/*	lx = abs(lx); */
/*	ly = abs(ly); */
	if(lx<0) lx = -lx;
	if(ly<0) ly = -ly;
	/*
		CWD
		XOR EAX,EDX
		SUB EAX,EDX
	*/
	
	if (lx >= ly)
	{
		len1 = lx ; len2 = ly;
	}
	else
	{
		len1 = ly ; len2 = lx;
	}

	t = len2 + (len2 >> 1) ;
	length = len1 - (len1 >> 5) - (len1 >> 7) + (t >> 2) + (t >> 6) ;
	return length;
}
/*--------------------------------------------------------------------
   イメージにフォント描画高機能版
 ---------------------------------------------------------------------*/
static VALUE Image_drawFontEx( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyFont *font;
    struct DXRubyImage *image;
    VALUE vx, vy, vcolor, vstr, vfont, voption;
    VALUE vedge;
    VALUE vshadow;
    int edge_width, edge_level, shadow_x, shadow_y, shadow_edge;
    int cr=255, cg=255, cb=255, x, y;
    int br=0, bg=0, bb=0;
    int sr=0, sg=0, sb=0;
    LPDIRECT3DSURFACE9 pD3DSurface;
    HDC hDC;
    RECT rc;
    HRESULT hr;
    D3DLOCKED_RECT srctrect;
    D3DLOCKED_RECT dsttrect;
    int i, j;
    RECT srcrect;
    RECT dstrect;
    TEXTMETRIC tm;
    GLYPHMETRICS gm;

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

    /* 引数取得 */
    if( argc < 4 ) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 4);

    vx = argv[0];
    vy = argv[1];
    vstr = argv[2];
    vfont = argv[3];

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

    Check_Type(vstr, T_STRING);

    /* 引数のフォントオブジェクトから中身を取り出す */
    Data_Get_Struct( obj, struct DXRubyImage, image );
    DXRUBY_CHECK_DISPOSE( image, texture );
    DXRUBY_CHECK_TYPE( Font, vfont );
    Data_Get_Struct( vfont, struct DXRubyFont, font );
    DXRUBY_CHECK_DISPOSE( font, pD3DXFont );

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

    /* 文字の色 */
    vcolor = hash_lookup( voption, symbol_color );
    if( vcolor != Qnil )
    {
        get_color( vcolor, &cr, &cg, &cb );
    }

    /* エッジオプション */
    vedge = hash_lookup( voption, symbol_edge );
    if( vedge == Qnil || vedge == Qfalse )
    {
        edge_width = 0;
        edge_level = 0;
    }
    else
    {
        VALUE vedge_color, vedge_width, vedge_level;
        vedge_color = hash_lookup( voption, symbol_edge_color );
        if( vedge_color != Qnil )
        {
            get_color( vedge_color, &br, &bg, &bb );
        }

        vedge_width = hash_lookup( voption, symbol_edge_width );
        edge_width = vedge_width == Qnil ? 2 : NUM2INT( vedge_width ); /* エッジの幅 */

        vedge_level = hash_lookup( voption, symbol_edge_level );
        edge_level = vedge_level == Qnil ? 4 : NUM2INT( vedge_level ); /* エッジの強さ */
    }

    /* 影オプション */
    vshadow = hash_lookup( voption, symbol_shadow );
    if( vshadow == Qnil || vshadow == Qfalse )
    {
        shadow_x = 0;
        shadow_y = 0;
    }
    else
    {
        VALUE vshadow_color, vshadow_x, vshadow_y, vshadow_edge;
        vshadow_color = hash_lookup( voption, symbol_shadow_color );
        if( vshadow_color != Qnil )
        {
            get_color( vshadow_color, &sr, &sg, &sb );
        }

        vshadow_x = hash_lookup( voption, symbol_shadow_x );
        shadow_x = vshadow_x == Qnil ? NUM2INT( Font_getSize( vfont ) ) / 24 + 1 : NUM2INT(vshadow_x);

        vshadow_y = hash_lookup( voption, symbol_shadow_y );
        shadow_y = vshadow_y == Qnil ? NUM2INT( Font_getSize( vfont ) ) / 24 + 1 : NUM2INT(vshadow_y);

        vshadow_edge = hash_lookup( voption, symbol_shadow_edge );
        if( vshadow_edge == Qnil || vshadow_edge == Qfalse )
        {
            shadow_edge = 0;
        }
        else
        {
            shadow_edge = 1;
        }
    }

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

    /* 描画文字のUTF16LE化 */
#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vstr ) != 0 )
    {
        vwidestr = rb_funcall( vstr, rb_intern( "encode" ), 1, rb_str_new2( "UTF-16LE" ) );
    }
    else
    {
        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 );
    }
#else
    {
        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 );
    }
#endif
    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 );
        if( bufsize > 0 )
        {
            unsigned char *buf = alloca( bufsize );
            int v, u;

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

            if( edge_width > 0 && edge_level > 0 )
            { /* エッジ生成。吉里吉里2のソースを参考にしている。 */
                int edge_pitch  = ((gm.gmBlackBoxX + edge_width * 2 - 1) >> 2) + 1 << 2;
                unsigned char *blurbuf = alloca( edge_pitch * (gm.gmBlackBoxY + edge_width * 2) ); /* バッファ */
                int lvsum = 0;
                memset( blurbuf, 0, edge_pitch * (gm.gmBlackBoxY + edge_width * 2) ); /* バッファのクリア */

                for( v = -edge_width; v <= edge_width; v++ )
                {
                    for( u = -edge_width; u <= edge_width; u++ )
                    {
                        int len = fast_int_hypot(u, v);
                        if(len <= edge_width)
                            lvsum += (edge_width - len + 1);
                    }
                }

                if( lvsum )
                {
                    lvsum = (1<<18) / lvsum;
                }
                else
                {
                    lvsum = (1<<18);
                }

                for( v = -edge_width; v <= edge_width; v++ )
                {
                    for( u = -edge_width; u <= edge_width; u++ )
                    {
                        int len = fast_int_hypot(u, v);
                        if( len <= edge_width )
                        {
                            int sx, sy;
                            for( sy = 0; sy < gm.gmBlackBoxY; sy++ )
                            {
                                for( sx = 0; sx < gm.gmBlackBoxX; sx++ )
                                {
                                    int temp;
                                    temp = blurbuf[(v + sy + edge_width) * edge_pitch + u + sx + edge_width] + ((buf[ sx + sy * ((gm.gmBlackBoxX + 3) & 0xfffc) ] * lvsum * (edge_width - len + 1) * edge_level) >> 18);
                                    if( temp > 64 ) temp = 64;
                                    blurbuf[(v + sy + edge_width) * edge_pitch + u + sx + edge_width] = temp;
                                }
                            }
                        }
                    }
                }

                if( shadow_x != 0 || shadow_y != 0 )
                {
                    /* エッジがある場合の影の描画 */
                    if( shadow_edge == 1 )
                    {
                        drawfont_sub( gm.gmBlackBoxX + edge_width * 2, gm.gmBlackBoxY + edge_width * 2, x + gm.gmptGlyphOrigin.x - edge_width + shadow_x, y + tm.tmAscent - gm.gmptGlyphOrigin.y - edge_width + shadow_y, edge_pitch, blurbuf, &dsttrect, sr, sg, sb, image->width, image->height );
                    }
                    /* エッジがあるけどエッジ無し影の描画 */
                    else
                    {
                        drawfont_sub( gm.gmBlackBoxX, gm.gmBlackBoxY, x + gm.gmptGlyphOrigin.x + shadow_x, y + tm.tmAscent - gm.gmptGlyphOrigin.y + shadow_y, (gm.gmBlackBoxX + 3) & 0xfffc, buf, &dsttrect, sr, sg, sb, image->width, image->height );
                    }
                }

                /* エッジの描画 */
                drawfont_sub( gm.gmBlackBoxX + edge_width * 2, gm.gmBlackBoxY + edge_width * 2, x + gm.gmptGlyphOrigin.x - edge_width, y + tm.tmAscent - gm.gmptGlyphOrigin.y - edge_width, edge_pitch, blurbuf, &dsttrect, br, bg, bb, image->width, image->height );

            }
            else if( shadow_x != 0 || shadow_y != 0 ) /* エッジがない場合の影の描画 */
            {
                drawfont_sub( gm.gmBlackBoxX, gm.gmBlackBoxY, x + gm.gmptGlyphOrigin.x + shadow_x, y + tm.tmAscent - gm.gmptGlyphOrigin.y + shadow_y, (gm.gmBlackBoxX + 3) & 0xfffc, buf, &dsttrect, sr, sg, sb, image->width, image->height );
            }

            /* 文字の描画 */
            drawfont_sub( gm.gmBlackBoxX, gm.gmBlackBoxY, x + gm.gmptGlyphOrigin.x, y + tm.tmAscent - gm.gmptGlyphOrigin.y, (gm.gmBlackBoxX + 3) & 0xfffc, buf, &dsttrect, cr, cg, cb, image->width, image->height );

        }
        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 vfilename, vformat, vsjisstr;

    rb_scan_args( argc, argv, "11", &vfilename, &vformat );

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

#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vfilename ) != 0 )
    {
        vsjisstr = rb_funcall( vfilename, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
    }
    else
    {
        vsjisstr = vfilename;
    }
#else
    vsjisstr = vfilename;
#endif
    hr = D3DXSaveTextureToFile(
            RSTRING_PTR( vsjisstr ),                            /* 保存ファイル名 */
            vformat == Qnil ? D3DXIFF_JPG : FIX2INT( vformat ), /* ファイルフォーマット */
            (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 vfilename )
{
    HRESULT hr;
    WCHAR wstrFileName[MAX_PATH];
    struct DXRubySound *sound;
    CHAR strPath[MAX_PATH];
    DWORD i;
    WCHAR wstrSearchPath[MAX_PATH];
    VALUE vsjisstr;

    g_iRefAll++;

	Check_Type(vfilename, 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" );
	}

    /* ファイルロード */
#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vfilename ) != 0 )
    {
        vsjisstr = rb_funcall( vfilename, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
    }
    else
    {
        vsjisstr = vfilename;
    }
#else
    vsjisstr = vfilename;
#endif
    MultiByteToWideChar( CP_ACP, 0, RSTRING_PTR( vsjisstr ), -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( vsjisstr ), ".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;

    if( g_WindowInfo.userloop == 2 )
    {
        rb_raise( eDXRubyError, "Window.loop実行中にInput.updateは実行できません。" );
    }

    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クラスのmark
 ---------------------------------------------------------------------*/
static void Font_mark( struct DXRubyFont *font )
{
    rb_gc_mark( font->fontname );
    rb_gc_mark( font->italic );
    rb_gc_mark( font->weight );
}

/*--------------------------------------------------------------------
   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" );
    obj = Data_Wrap_Struct(klass, Font_mark, Font_release, font);
    font->fontname = Qnil;
    font->italic = Qnil;
    font->weight = Qnil;
    return obj;
}


/*--------------------------------------------------------------------
   FontクラスのInitialize
 ---------------------------------------------------------------------*/
static VALUE Font_initialize( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyFont *font;
    HRESULT hr;
    D3DXFONT_DESC desc;
    VALUE size, vfontname, voption, 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, &vfontname, &hash );

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

    vweight = hash_lookup( voption, symbol_weight );
    vitalic = hash_lookup( voption, 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( vfontname == Qnil )
    {
        lstrcpy(desc.FaceName, "");
        lstrcpy(logfont.lfFaceName, "");
    }
    else
    {
        VALUE vsjisstr;
        Check_Type(vfontname, T_STRING);
#ifdef HAVE_RB_ENC_STR_NEW
        if( rb_enc_get_index( vfontname ) != 0 )
        {
            vsjisstr = rb_funcall( vfontname, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
        }
        else
        {
            vsjisstr = vfontname;
        }
#else
        vsjisstr = vfontname;
#endif
        lstrcpy(desc.FaceName, RSTRING_PTR( vsjisstr ));
        lstrcpy(logfont.lfFaceName, RSTRING_PTR( vsjisstr ));
    }

    /* フォントオブジェクト取り出し */
    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 );
    font->italic = vitalic;
    font->weight = vweight;
#ifdef HAVE_RB_ENC_STR_NEW
    font->fontname = vfontname == Qnil ? Qnil : rb_str_new_shared( vfontname );
#else
    font->fontname = vfontname == Qnil ? Qnil : rb_str_new2( RSTRING_PTR(vfontname) );
#endif

    return obj;
}


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

#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vstr ) != 0 )
    {
        vsjisstr = rb_funcall( vstr, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
    }
    else
    {
        vsjisstr = vstr;
    }
#else
    vsjisstr = vstr;
#endif
    result = AddFontResourceEx( RSTRING_PTR( vsjisstr ), 0x10, 0 );
    if( result == 0 )
    {
        rb_raise( eDXRubyError, "フォントのインストールに失敗しました - Font_install" );
    }

    return INT2FIX( result );
}


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

    Check_Type( vstr, T_STRING );

    /* フォントオブジェクト取り出し */
    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 );
#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vstr ) != 0 )
    {
        vsjisstr = rb_funcall( vstr, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
    }
    else
    {
        vsjisstr = vstr;
    }
#else
    vsjisstr = vstr;
#endif
    DrawText( hDC, RSTRING_PTR( vsjisstr ), -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 )
{
    struct DXRubyFont *font;

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

    return INT2FIX( font->size );
}


/*--------------------------------------------------------------------
   フォント名称を取得する
 ---------------------------------------------------------------------*/
static VALUE Font_getFontname( VALUE obj )
{
    struct DXRubyFont *font;

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

    return font->fontname;
}


/*--------------------------------------------------------------------
   イタリックフラグを取得する
 ---------------------------------------------------------------------*/
static VALUE Font_getItalic( VALUE obj )
{
    struct DXRubyFont *font;

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

    return font->italic;
}


/*--------------------------------------------------------------------
   フォントの太さを取得する
 ---------------------------------------------------------------------*/
static VALUE Font_getWeight( VALUE obj )
{
    struct DXRubyFont *font;

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

    return font->weight;
}


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

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

    hr = CoInitialize(NULL);

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

    /* システムのエンコード取得 */
    strcpy( sys_encode, "CP" );
    itoa( GetACP(), sys_encode+2, 10 );

    /* インスタンスハンドル取得 */
    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_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_font_ex", Window_drawFontEx , -1 );
    rb_define_singleton_method( mWindow, "drawFontEx" , Window_drawFontEx , -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_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_get_bgcolor , 0  );
    rb_define_singleton_method( mWindow, "bgcolor="     , Window_set_bgcolor , 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, "resize"   , Window_resize    , 2  );
    rb_define_singleton_method( mWindow, "active?"   , Window_get_active, 0  );
    rb_define_singleton_method( mWindow, "getScreenModes", Window_getScreenModes, 0  );
    rb_define_singleton_method( mWindow, "get_screen_modes", Window_getScreenModes, 0  );


    /* 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, "to_image"  , RenderTarget_to_image  , 0 );
    rb_define_method( cRenderTarget, "toImage"   , RenderTarget_to_image  , 0 );

    rb_define_method( cRenderTarget, "draw"     , RenderTarget_draw       , -1 );
    rb_define_method( cRenderTarget, "draw_scale", RenderTarget_drawScale  , -1 );
    rb_define_method( cRenderTarget, "drawScale", RenderTarget_drawScale  , -1 );
    rb_define_method( cRenderTarget, "draw_rot" , RenderTarget_drawRot    , -1 );
    rb_define_method( cRenderTarget, "drawRot"  , RenderTarget_drawRot    , -1 );
    rb_define_method( cRenderTarget, "draw_alpha", RenderTarget_drawAlpha  , -1 );
    rb_define_method( cRenderTarget, "drawAlpha", RenderTarget_drawAlpha  , -1 );
    rb_define_method( cRenderTarget, "draw_add" , RenderTarget_drawAdd    , -1 );
    rb_define_method( cRenderTarget, "drawAdd"  , RenderTarget_drawAdd    , -1 );
    rb_define_method( cRenderTarget, "draw_sub" , RenderTarget_drawSub    , -1 );
    rb_define_method( cRenderTarget, "drawSub"  , RenderTarget_drawSub    , -1 );
    rb_define_method( cRenderTarget, "draw_ex"  , RenderTarget_drawEx     , -1 );
    rb_define_method( cRenderTarget, "drawEx"   , RenderTarget_drawEx     , -1 );
    rb_define_method( cRenderTarget, "draw_font", RenderTarget_drawFont   , -1 );
    rb_define_method( cRenderTarget, "drawFont" , RenderTarget_drawFont   , -1 );
    rb_define_method( cRenderTarget, "draw_font_ex", RenderTarget_drawFontEx , -1 );
    rb_define_method( cRenderTarget, "drawFontEx" , RenderTarget_drawFontEx , -1 );
    rb_define_method( cRenderTarget, "draw_tile", RenderTarget_drawTile   , -1  );
    rb_define_method( cRenderTarget, "drawTile" , RenderTarget_drawTile   , -1  );
    rb_define_method( cRenderTarget, "draw_morph", RenderTarget_drawMorph , -1 );
    rb_define_method( cRenderTarget, "drawMorph" , RenderTarget_drawMorph , -1 );
    rb_define_method( cRenderTarget, "update"       , RenderTarget_update , 0 );
    rb_define_method( cRenderTarget, "bgcolor"      , RenderTarget_get_bgcolor , 0  );
    rb_define_method( cRenderTarget, "bgcolor="     , RenderTarget_set_bgcolor , 1  );
    rb_define_method( cRenderTarget, "min_filter"   , RenderTarget_getMinFilter , 0 );
    rb_define_method( cRenderTarget, "minFilter"    , RenderTarget_getMinFilter , 0 );
    rb_define_method( cRenderTarget, "min_filter="  , RenderTarget_setMinFilter , 1 );
    rb_define_method( cRenderTarget, "minFilter="   , RenderTarget_setMinFilter , 1 );
    rb_define_method( cRenderTarget, "mag_filter"   , RenderTarget_getMagFilter , 0 );
    rb_define_method( cRenderTarget, "magFilter"    , RenderTarget_getMagFilter , 0 );
    rb_define_method( cRenderTarget, "mag_filter="  , RenderTarget_setMagFilter , 1 );
    rb_define_method( cRenderTarget, "magFilter="   , RenderTarget_setMagFilter , 1 );

    /* RenderTargetオブジェクトを生成した時にinitializeの前に呼ばれるメモリ割り当て関数登録 */
    rb_define_alloc_func( cRenderTarget, RenderTarget_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 );
//    rb_define_singleton_method( cImage, "transition", Image_transition, -1 );


    /* Imageクラスにメソッド登録*/
    rb_define_private_method( cImage, "initialize", Image_initialize, -1 );
    rb_define_method( cImage, "dispose"   , Image_dispose   , 0 );
    rb_define_method( cImage, "delayed_dispose"   , Image_delayed_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, "set_color_key", Image_setColorKey , 1 );
    rb_define_method( cImage, "setColorKey", Image_setColorKey , 1 );
    rb_define_method( cImage, "slice_to_array", Image_sliceToArray , 2 );
    rb_define_method( cImage, "sliceToArray", Image_sliceToArray , 2 );
    rb_define_method( cImage, "initialize_copy", Image_initialize_copy , 1 );
    rb_define_method( cImage, "draw_font_ex" , Image_drawFontEx  , -1 );
    rb_define_method( cImage, "drawFontEx"   , Image_drawFontEx  , -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, "fontname", Font_getFontname  , 0 );
    rb_define_method( cFont, "italic", Font_getItalic  , 0 );
    rb_define_method( cFont, "weight" , Font_getWeight  , 0 );
    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, "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) );

    /* 例外定義 */
    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_italic         = ID2SYM(rb_intern("italic"));
    symbol_weight         = ID2SYM(rb_intern("weight"));
    symbol_dividex        = ID2SYM(rb_intern("dividex"));
    symbol_dividey        = ID2SYM(rb_intern("dividey"));
    symbol_edge           = ID2SYM(rb_intern("edge"));
    symbol_edge_color     = ID2SYM(rb_intern("edge_color"));
    symbol_edge_width     = ID2SYM(rb_intern("edge_width"));
    symbol_edge_level     = ID2SYM(rb_intern("edge_level"));
    symbol_shadow         = ID2SYM(rb_intern("shadow"));
    symbol_shadow_color   = ID2SYM(rb_intern("shadow_color"));
    symbol_shadow_x       = ID2SYM(rb_intern("shadow_x"));
    symbol_shadow_y       = ID2SYM(rb_intern("shadow_y"));
    symbol_shadow_edge    = ID2SYM(rb_intern("shadow_edge"));

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

    /* ロストリスト */
    g_RenderTargetList.pointer = malloc( sizeof(void*) * 16 );
    g_RenderTargetList.count = 0;
    g_RenderTargetList.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_iRefAll++;

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

    /* BG色の初期化 */
    Window_set_bgcolor( mWindow, rb_ary_new3( 3, INT2FIX( 0 ), INT2FIX( 0 ), INT2FIX( 0 ) ) );
    
    /* 割り当て初期化 */
    for( i = 0; i < 256; i++)
    {
        g_diKeyConfig[i] = -1;
    }
    for( i = 0; i < PADMAX; i++)
    {
        for( j = 0; j < 20; j++)
        {
            g_PadState[i].PadConfig[j] = -1;
        }
    }

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

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

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

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


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

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

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

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

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

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

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

    WINNLSEnableIME( g_hWnd, FALSE );
}


/*--------------------------------------------------------------------
  （内部関数）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] );
    }

}


/* Ruby1.8の古いほう対応 */
static VALUE hash_lookup(VALUE hash, VALUE key)
{
    VALUE val;

    if (!RHASH_TBL(hash) || !st_lookup(RHASH_TBL(hash), key, &val)) {
        return Qnil;
    }
    return val;
}


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

/*--------------------------------------------------------------------
   描画設定（通常描画）
 ---------------------------------------------------------------------*/
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 = RenderTarget_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 = RenderTarget_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 = RenderTarget_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 = RenderTarget_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 = RenderTarget_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 = RenderTarget_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 = RenderTarget_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 );
}

