module y4d_draw.texture;

private import opengl;
private import SDL;

private import std.string;
private import std.path;
private import std.thread;

private import y4d_aux.cacheobject;

private import y4d_draw.surface;
private import y4d_draw.drawcontext;
private import y4d_draw.drawbase;
private import y4d_draw.texturebase;
private import y4d_draw.glextensions;

private import ytl.y4d_result;
private import ytl.vector;

private import yamalib.log.log;
private import yamalib.log.performancelog;
private import yamalib.draw.texturekga;


// #region Texture生成時のオプションに関するinterfaceおよびclass
public interface IGlTextureOption
{
	/// <summary>
	/// 拡大縮小のときに線形補間を用いるのか？
	/// </summary>
	/// <remarks>
	/// これを用いると古いビデオカードでは極端に遅くなる。
	///	</remarks>
	bool smooth();
	bool smooth(bool b);

	/// <summary>
	/// S3TCテクスチャ圧縮を用いるのか。
	/// ビデオメモリの少ないマシンでは用いたほうが良いが
	/// 万人向けというわけでもない。
	/// 
	/// ゲーム内のconfigで変更できるように作るのが好ましいだろう。
	/// ※　TxRcとの併用は出来ないのでTxRcが使える状況ならばTxRcを優先する。
	/// ただし、NPOTとは併用できる。
	/// 
	/// default = false
	/// </summary>
	bool S3TC();
	bool S3TC(bool b);

	/// <summary>
	/// FXT1テクスチャ圧縮を用いるのか。
	/// ビデオメモリの少ないマシンでは用いたほうが良いが
	/// 万人向けというわけでもない。
	/// 
	/// default = false
	/// </summary>
	bool FXT1();
	bool FXT1(bool b);

	/// <summary>
	/// テクスチャ圧縮(S3TC,FXT1など)を用いるのか？
	/// 
	/// テクスチャ圧縮を用いると画質が落ちるが
	/// ビデオメモリ節約になる。
	/// </summary>
	bool compress();
	bool compress(bool b);

	/// <summary>
	/// NPOT(2の累乗サイズ以外のテクスチャ)が使える状況なら
	/// ビデオメモリ節約のために使うかのフラグ。
	/// 
	/// ただし、Radeon系は遅くなるので、使える状況でも使わない。
	/// </summary>
	bool NPOT();
	bool NPOT(bool b);

	/// <summary>
	/// TxRc(自由なサイズのテクスチャ)が使えるなら使うか。
	/// ビデオメモリ節約のために使ったほうが良い。
	/// 
	/// TxRcのほうがNPOTより対応しているビデオカードが多く、
	/// また、NPOTと違い、速度的なペナルティも少ないのでお勧め。
	/// 
	/// GlTextureはNPOTとTxRcとどちらも使える状況ならば、TxRcを優先する。
	/// </summary>
	bool TxRc();
	bool TxRc(bool b);
}

/// <summary>
/// GlTexture用のGlobalOption。
/// </summary>
public class GlTextureGlobalOption : IGlTextureOption
{
	/// <summary>
	/// 拡大縮小時の補間をSmoothにするのか。
	/// (ハードで対応していないと遅くなる)
	/// 
	/// 拡大縮小をあまり使わないならfalseでいいと思う。
	/// 
	/// default == false
	/// </summary>
	public bool smooth() {
		return m_smooth;
	}
	public bool smooth(bool b) {
		return m_smooth = b;
	}
	private bool m_smooth = false;

	/// <summary>
	/// S3TCテクスチャ圧縮を用いるのか。
	/// ビデオメモリの少ないマシンでは用いたほうが良いが
	/// 万人向けというわけでもない。
	/// 
	/// ゲーム内のconfigで変更できるように作るのが好ましいだろう。
	/// ※　TxRcとの併用は出来ないのでTxRcが使える状況ならばTxRcを優先する。
	/// ただし、NPOTとは併用できる。
	/// 
	/// default = false
	/// </summary>
	public bool S3TC() {
		return m_s3TC;
	}
	public bool S3TC(bool b) {
		return m_s3TC = b;
	}
	private bool m_s3TC = false;

	/// <summary>
	/// FXT1テクスチャ圧縮を用いるのか。
	/// ビデオメモリの少ないマシンでは用いたほうが良いが
	/// 万人向けというわけでもない。
	/// 
	/// default = false
	/// </summary>
	public bool FXT1() {
		return m_fXT1;
	}
	public bool FXT1(bool b) {
		return m_fXT1 = b;
	}
	private bool m_fXT1 = false;

	/// <summary>
	/// テクスチャ圧縮(S3TC,FXT1など)を用いるのか？
	/// 
	/// テクスチャ圧縮を用いると画質が落ちるが
	/// ビデオメモリ節約になる。
	/// </summary>
	public bool compress()
	{
		return S3TC || FXT1;
	}
	public bool compress(bool b) {
		return S3TC = FXT1 = b;
	}

	/// <summary>
	/// NPOT(2の累乗サイズ以外のテクスチャ)が使える状況なら
	/// ビデオメモリ節約のために使うかのフラグ。
	/// 
	/// ただし、Radeon系は遅くなるので、使える状況でも使わない。
	/// </summary>
	public bool NPOT() {
		return m_nPOT;
	}
	public bool NPOT(bool b) {
		return m_nPOT = b;
	}
	
	private bool m_nPOT = true;
//	private bool m_nPOT = false;

	/// <summary>
	/// TxRc(自由なサイズのテクスチャ)が使えるなら使うか。
	/// ビデオメモリ節約のために使ったほうが良い。
	/// 
	/// TxRcのほうがNPOTより対応しているビデオカードが多く、
	/// また、NPOTと違い、速度的なペナルティも少ないのでお勧め。
	/// 
	/// GlTextureはNPOTとTxRcとどちらも使える状況ならば、TxRcを優先する。
	/// </summary>
	public bool TxRc() {
		return m_txRc;
	}
	public bool TxRc(bool b) {
		return m_txRc = b;
	}
	private bool m_txRc = true;
//	private bool m_txRc = false;
}

//#endregion



/// Screen に画像を描画するためのサーフェース
/**
	openGLのテクスチャー。
	bmp,png,etc..を読み込んで、画面に描画するときに使います。

	サイズは 幅、高さがそれぞれ、2のべき乗でなければ、2のべき乗に
	なるようにテクスチャーサイズを内部で拡大します。
*/
class StorableTexture : ICacheObject , ITextureBase {

	/// メインスレッドのＩＤを設定する
	synchronized static void setMainThreadId(uint id)
	/**
		別スレッドでテクスチャのロードを行うような場合
		テクスチャを生成するスレッドはメインスレッドでなければならないようで、
		このＩＤをみて、生成するか、遅延するか判断する
	*/
	{
		mainThreadId = id;
	}
	
	/// 遅延生成リストに追加する
	synchronized static void markUnReg(StorableTexture t) 
	/**
		リストを参照として保持しているので、
		実体化を待っているままにすると、そのテクスチャはいつまでも解放されない
		従って、しかるべきタイミングで createTextureAll をかならず呼ぶべきである
	*/
	{
		unRegList ~= t;
	}
	
	/// 生成遅延リストにテクスチャを生成する
	synchronized static bool createTextureAll() {

		if ( !isMainThread() ) return false;

		foreach (inout StorableTexture t; unRegList) {
			try {
				t.reg();
				t = null;
			} catch {
				// 参照はつねにもっているからアクセス違反が発生しない。
				Log.printError("createTextureAll");
			}
		}
		
		unRegList = null;
		
		return true;
	}
	
	/// 今、メインスレッドから呼びされているか
	synchronized static bool isMainThread() {
		return cast(bool) (mainThreadId == cast(uint) Thread.getThis().hdl);
	}
	
	/// テクスチャのデフォルト設定
	public static IGlTextureOption globalOption() {
		if (glGlbTexOption is null) {
			glGlbTexOption = new GlTextureGlobalOption();
		}
		return glGlbTexOption;
	}
	private static IGlTextureOption glGlbTexOption = null;
	
	/// テクスチャのデフォルト設定
	public static IGlTextureOption localOption() {
		if (glLocalTexOption is null) {
			glLocalTexOption = new GlTextureGlobalOption();
		}
		return glLocalTexOption;
	}
	private static IGlTextureOption glLocalTexOption = null;
	
	/// 遅延生成になっているテクスチャを有効化する
	bool reg() {

		if ( !isMainThread() ) return false;
		
		//	textureの生成
		glGenTextures(1,&textureName);

		if (textureName == 0)
		{
			Log.printError("Texture#reg : glGenTextures fault %d", textureName);
			// これ 割り当て失敗でね？
			this.release();
			return false;
		}
		
		//	bind
		bLoad = true;	//	これをtrueにしておかないとbindできない
		bind();
		
		uint internalformat = bAlpha ? opengl.GL_RGBA : opengl.GL_RGB;
		{
			// テクスチャ圧縮を用いるのか？
			bool s3tc = localOption.S3TC;
			bool fxt1 = localOption.FXT1;
			GlExtensions exts = GlExtensions.instance;
			
			// テクスチャ圧縮を行ないたい場合
			if ((s3tc || fxt1)
			//	&& !npot // s3tcはnpotとは併用できる
				&& !useTxRc // s3tcはTxRcとは併用できない
				&& exts.isAvailable("GL_ARB_texture_compression"))
			{
				// S3TCが使えるのか？
				if (s3tc
					&& exts.isAvailable("GL_EXT_texture_compression_s3tc"))
				{
					internalformat = bAlpha ?
						opengl.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT :
						opengl.GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
					//
					// RGBAの方は、DXT1、DXT3、DXT5の3種類があり、
					// DXT1はαが1ビットらしいので除外するとしても、
					// DXT3はα値が急激に変化するフォントなどに向いていて、
					// DXT5はグラデーションなどの、α値が緩やかに変化する場合に向いている…らしい。
					//
					// まぁ、あんまり深く考えずにどちらか決めうちでいい気も。。(´ω`)
					//
				}
				// FXT1が使えるのか？
				else if (fxt1
					&& exts.isAvailable("GL_3DFX_texture_compression_FXT1"))
				{
					internalformat = bAlpha ?
						opengl.GL_COMPRESSED_RGBA_FXT1_3DFX :
						opengl.GL_COMPRESSED_RGB_FXT1_3DFX;
				}
			}
		}

		void* v = bDup ? dupSurface.getPixels() : tmpSurface.getPixels();

		//	↓このエラー検出できなーだ(´Д`)
		glTexImage2D(textureType,
			0,	//	texture level
			internalformat,	//	colors : RGB or ARGB
			sizeRX, sizeRY,
			0,	//	texture境界は存在しない
			bAlpha ? GL_RGBA : GL_RGB,	// α付き画像ならば GL_RGBAを指定する
			GL_UNSIGNED_BYTE, v
		);
		
		uint error = opengl.glGetError();
		if (error != 0)
		{
			Log.printError("Texture#reg : glTexImage2D fault - %s  - error code %d", strFileName, error);
			unbind();
			this.release();
			return false;
		}

		uint option = localOption.smooth ? GL_LINEAR : GL_NEAREST;

		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,option);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,option);

		tmpSurface.release();	//	画像もったいないから解放しておく
		dupSurface.release();	//	no more needed

		bReg = true;

//	printf("sizeX,sizeY:%d,%d\n",sizeX,sizeY);

		unbind();
		
		return true;
	}
	
	///	指定されたファイル名の画像を読み込む
	/**
		読み込みに失敗すれば非0が返る。
	*/
	y4d_result load(char[] filename)
	{
		try {
			PerformanceLog.logTextureLoad( filename, null );
		
			release();
			releaseBackSurface();
			strFileName = filename;

			y4d_result result = tmpSurface.load(filename);
			if (result) { // 読み込みエラー
				Log.printError("Texture Load Error %s#load : filename=[%s]", this.toString(), filename);
				return result;
			}
	
			return innerSetSurface(tmpSurface, false); // surface,dup/init
		} catch {
			// なんらかのエラー
			return y4d_result.happen_some_error;
		} finally {
			PerformanceLog.endLog();
		}
		
		return  y4d_result.happen_some_error;
	}
	
	/// 復元処理
	bool restore() {
		try {
			bRestore = true;
			if ( !(strFileName is null) ) {
				return cast(bool) (y4d_result.no_error == load(strFileName));
		
			} else if ( !(backupSurface is null) ) {
				// バックアップサーフェイスは再びスクリーン変換のときに使うもしれないので
				// さらにバックアップをつくって渡す
				return cast(bool) (y4d_result.no_error == setSurface( backupSurface.clone() ));
			}
			
		} catch(Exception e) {
			Log.printError("%s#restore Exception : [%s]", this.toString(), e.toString());
		} finally {
			bRestore = false;
		}
		return false;
	}
	
	/// 読み込んでいるファイル名を返却する（デバッグ用）
	char[] getFilename() {
		return this.strFileName;
	}

	///	SDL_Surfaceを渡し、それをテクスチャーとする
	y4d_result	setSurface(SDL_Surface* surface){
		release();
		tmpSurface.setSurface(surface);
		return innerSetSurface(tmpSurface,true); // surface,dup/init
	}

	///	Surfaceを渡し、それをテクスチャーとする
	y4d_result setSurface(Surface surface) {
		release();

		strFileName = null;
		// リストアするために、オリジナルのサーフェイスを保管
		if (!bRestore) {
			releaseBackSurface();
			backupSurface = surface.clone();
		}
		
		tmpSurface.setSurface(surface.getSurface());
		return innerSetSurface(tmpSurface,true); // surface,dup/init
	}

	///	Surfaceを渡し、それをテクスチャーと入れ替える
	y4d_result subSurfaceFast(inout Surface surface)
	/**
		テクスチャ生成時にリサイズを行わない
		かつ、参照されたサーフェイスを再利用させるために解放しない
	*/	
	{
		if (!bLoad) {
			// ロードされていなければ入れ替えられない
			return y4d_result.precondition_error;
		}
		return innerSubSurfaceFast(surface);
	}

	///	テクスチャーの解放
	/**
		loadで読み込んだ画像を解放する。
	*/
	void	release() {
		if (bLoad) {
			if (0 != textureName) {
				glDeleteTextures(1,&textureName);
			}
		}

		bDup = false;
		bLoad = false;
		sizeX = sizeY = sizeRX = sizeRY = 0;
		widthRate = heightRate = 0;
	}

	///	テクスチャーの解放
	/**
		loadで読み込んだ画像を解放する。
	*/
	void	releaseBackSurface() {
		if ( !(backupSurface is null) ) {
			backupSurface.release();
			backupSurface = null;
		}
	}

	///	テクスチャーを貼り付けるときに使う
	/**
		この関数を呼び出したあと、
<PRE>
		glBegin(GL_POLYGON);
			glTexCoord2f(0 , 0); glVertex2f(-0.9 , -0.9);
			glTexCoord2f(0 , 1); glVertex2f(-0.9 , 0.9);
			glTexCoord2f(1 , 1); glVertex2f(0.9 , 0.9);
			glTexCoord2f(1 , 0); glVertex2f(0.9 , -0.9);
		glEnd();
</PRE>
		のようにすれば、テクスチャーをポリゴンにはりつけることが出来ます。
	*/
	void	bind() {
		if (bLoad) {
			if (textureType == opengl.GL_TEXTURE_RECTANGLE_ARB) {
				opengl.glEnable(opengl.GL_TEXTURE_RECTANGLE_ARB);
			}
			opengl.glBindTexture(textureType, textureName);
		}
	}

	///	テクスチャーのバインドを解除する
	/**
		bind();
		でバインドしていたテクスチャーをunbindする。
	*/
	void	unbind(){
		glBindTexture(textureType,0);
		// 念のためにこれも対になるように処理を入れておく。
		if (textureType == opengl.GL_TEXTURE_RECTANGLE_ARB) {
			opengl.glDisable(opengl.GL_TEXTURE_RECTANGLE_ARB);
		}
	}

	///	画像を読み込んでいるか
	/**
		テクスチャーを生成しているときはtrueを返す。
	*/
	bool	isLoad() {
		return bLoad;
	}

	///	テクスチャーの幅を取得
	/**
		テクスチャーを読み込んでいないときは0を返す。
	*/
	float getWidth() { return sizeX; }

	///	テクスチャーの高さを取得
	/**
		テクスチャーを読み込んでいないときは0を返す。
	*/
	float getHeight() { return sizeY; }

	///	テクスチャーの確保された幅を取得
	/**
		テクスチャーは、実際には2のべき乗に切り上げて確保される。
		テクスチャーを読み込んでいないときは0を返す。
	*/
	int	getRWidth() { return sizeRX; }

	///	getWidth()/getRWidth() を返す
	float getWidthRate() { return widthRate; }

	///	getHeight()/getRHeight() を返す
	float getHeightRate() { return heightRate; }

	///	テクスチャーの確保された高さを取得
	/**
		テクスチャーは、実際には2のべき乗に切り上げて確保される。
		テクスチャーを読み込んでいないときは0を返す。
	*/
	int	getRHeight() { return sizeRY; }

	///	確保しているテクスチャーで使用しているメモリサイズを取得する
	/**
		単位は[byte]。cacheシステムでテクスチャーとして使用している
		サイズを計測するのに使用する。
	*/
	ulong getBufferSize() {
		return sizeRX*sizeRY*(bAlpha?4:3);
	}

	///	α転送を無効に
	/**
		ディフォルトではαは有効。
		元情報にαが含まれていない場合で、α付きの転送を行なわないと
		わかっている場合(BGなど)は、無効化しておくことで、
		転送時の高速化をはかれる。<BR>

		抜き色の指定をする場合は、αサーフェースでなければならない。
		(抜き色をαを書き換えて実現しているため)
	*/
	void	disableAlpha() { bAlpha = false; }

	///	α転送を有効に
	/**
		ディフォルトではαは有効。
		元情報にαが含まれていない場合で、α付きの転送を行なわないと
		わかっている場合(BGなど)は、無効化しておくことで、
		転送時の高速化をはかれる。<BR>

		抜き色の指定をする場合は、αサーフェースでなければならない。
		(抜き色をαを書き換えて実現しているため)<BR>

		また、画像を読み込む(load)するより以前に、この関数を
		呼び出しておかなければならない。
	*/
	void	enableAlpha() { bAlpha = true; }

	///	抜き色の指定
	/**
		転送時の転送元カラーキー(抜き色)を指定します。
		これを指定して、loadを行なうと、そのサーフェースの該当色の部分の
		α値が0(透過)になる。(ただしαサーフェース作成時のみ)<BR>

		r,g,bはそれぞれ0～255。<BR>

		また、画像を読み込む(load)するより以前に、この関数を
		呼び出しておかなければならない。(loadするときに抜き色部分の
		α値を0に書き換えるので)<BR>

		その後の画像読み込み、解放によっては設定したカラーキーは
		無効にはならない。
	*/
	void	setColorKey(int r_,int g_,int b_){
		bColorKey = true;
		bColorKeyPos = false;
		r = r_; g = g_; b = b_;
	}

	///	テクスチャーのある座標をカラーキーにする
	/**
		また、画像を読み込む(load)するより以前に、この関数を
		呼び出しておかなければならない。(loadするときに抜き色部分の
		α値を0に書き換えるので)<BR>

		その後の画像読み込み、解放によっては設定したカラーキーは
		無効にはならない。
	*/
	void	setColorKeyPos(int cx_,int cy_){
		bColorKey = false;
		bColorKeyPos = true;
		cx = cx_; cy = cy_;
	}

	///	カラーキーの取り消し
	/**
		setColorKey / setColorKeyPos で設定した情報を無効化します。
	*/
	void	resetColorKey(){
		bColorKey = false;
		bColorKeyPos = false;
	}


	///	テクスチャを貼るときに線形補間する
	/**
		テクスチャを GL_LINEAR で貼る。
		線形補間されるので拡大・縮小時に綺麗に貼れる。
		むかしのビデオカードだとハードで対応していないので遅くなる。
		ディフォルトではtrue。<BR>

		この関数を設定するなら、loadを行なう前に設定しておくこと。<BR>

		BG用の画像等は、(拡大縮小しないならば) setSmooth(false);　しておけば
		良いと思われる。
	*/
	void	setSmooth(bool b) { bSmooth = b; }
	
	uint getTextureType() {
		return textureType;
	}
	
	/// 自分自身をコピーして帰す
	StorableTexture dup() {
		StorableTexture texture = new StorableTexture();
		texture.load(strFileName);
		return texture;
	}
	
	/// コンストラクタ
	this() {
		tmpSurface = new Surface;
		dupSurface = new Surface;
		bAlpha = true;
		bSmooth = true;
	}
	
	/// デストラクタ
	~this() { 
		try {
			release();
			releaseBackSurface();
		} catch (Object e) {
			printf("Texture destrucator Suraface release Error\n");
		}
	}

protected:


	//	visitor パターンなのだ
	/*
		DrawContextに基づいて拡大縮小してオフセット分、移動させて、
		クリップして描画しないといけない。

		実装面倒くさいですなぁ．．（´Д｀）
	*/
	void	blt(DrawContext context,float x,float y)
	{
		if (!isLoad()) return ;

		float w = getWidth();
		float h = getHeight();

		if (w==0 || h==0) return ;

		float wr = getWidthRate();
		float hr = getHeightRate();

		//	描画は、DrawContext.rateX,rateYの値を反映しなければならない

		bind();
		glBegin(GL_POLYGON);

		float rateX = context.rateX;
		float rateY = context.rateY;

		x = x * rateX + context.offsetRX;
		y = y * rateY + context.offsetRY;

		w *= rateX;
		h *= rateY;

		//	クリップ処理は、openglに任せたので、もはや不要なのだ
		if ( /* !context.isClip()*/ true) {
		//	クリップ無し
			glTexCoord2f(0,0); glVertex2f(x  ,y  );
			glTexCoord2f(wr,0); glVertex2f(x+w,y  );
			glTexCoord2f(wr,hr); glVertex2f(x+w,y+h);
			glTexCoord2f(0,hr); glVertex2f(x  ,y+h);
		} else {
		//	クリップ有り
			float ww = w , hh = h;
			//	クリップされる被覆領域を求める
			float leftRr = context.leftR - x;
			if (leftRr < 0.0 ) {
			//	被覆領域なし
				leftRr = 0.0;
			} else {
			//	被覆領域分を調整する
				x = context.leftR;
				ww -= leftRr; // その分表示エリアが減る
				leftRr = leftRr / w * wr;
			}
			//	以下同様
			float topRr = context.topR - y;
			if (topRr < 0.0 ) {
			//	被覆領域なし
				topRr = 0.0;
			} else {
			//	被覆領域分を調整する
				y = context.topR;
				hh -= topRr; // その分表示エリアが減る
				topRr = topRr / h * hr;
			}
			float rightRr = (x+ww) - context.rightR;
			if (rightRr < 0.0 ) {
				rightRr = wr;
			} else {
				ww -= rightRr;
				rightRr = wr - rightRr / w * wr;
			}
			float bottomRr = (y+hh) - context.bottomR;
			if (bottomRr < 0.0 ) {
				bottomRr = hr;
			} else {
				hh -= bottomRr; // その分表示エリアが減る
				bottomRr = hr - bottomRr / h * hr;
			}

			if (ww>0 && hh>0){
			glTexCoord2f(leftRr,topRr); 	glVertex2f(x   ,y  );
			glTexCoord2f(rightRr,topRr);	glVertex2f(x+ww,y  );
			glTexCoord2f(rightRr,bottomRr); glVertex2f(x+ww,y+hh);
			glTexCoord2f(leftRr,bottomRr);	glVertex2f(x   ,y+hh);
			}
		}
		glEnd();
		unbind();
	}

	//	visitor パターンなのだ
	/*
		DrawContextに基づいて拡大縮小してオフセット分、移動させて、
		クリップして描画しないといけない。

		実装面倒くさいですなぁ．．（´Д｀）
	*/
	void	blt(DrawContext context,float x,float y,Rect* srcRect){
		if (!isLoad()) return ;

		float w = getWidth();
		float h = getHeight();

		if (w==0 || h==0) return ;

		Rect r;
		if (!srcRect) {
			r.left = 0; r.top = 0; r.right = w; r.bottom = h;
			srcRect = &r;
		}

		float wr = getWidthRate();
		float hr = getHeightRate();

		float left,top,right,bottom;
		left	= (wr * srcRect.left) / w;
		top		= (hr * srcRect.top) / h;
		right	= (wr * srcRect.right) / w;
		bottom	= (hr * srcRect.bottom) / h;

		float rateX = context.rateX;
		float rateY = context.rateY;

		w = srcRect.right-srcRect.left;
		h = srcRect.bottom-srcRect.top;

		if (w==0 || h==0) return ;

		//	左右、上下反転描画のためにabsをとる。
		if (w<0) w=-w;
		if (h<0) h=-h;

		x = x * rateX + context.offsetRX;
		y = y * rateY + context.offsetRY;

		w *= rateX;
		h *= rateY;

		bind();
		glBegin(GL_POLYGON);

		//	クリップ処理は、openglに任せたので、もはや不要なのだ
		if (/* !context.isClip() */ true) {
		//	クリップ無し
			glTexCoord2f(left,top); glVertex2f(x  ,y  );
			glTexCoord2f(right,top); glVertex2f(x+w,y  );
			glTexCoord2f(right,bottom); glVertex2f(x+w,y+h);
			glTexCoord2f(left,bottom); glVertex2f(x  ,y+h);
		} else {
		//	クリップ有り
			float ww = w , hh = h;
			float wt = right-left , ht = bottom-top; // テクスチャ座標での距離
			//	クリップされる被覆領域を求める
			float leftRr = context.leftR - x; // ドット数
			if (leftRr < 0.0 ) {
			//	被覆領域なし
				leftRr = left;
			} else {
			//	被覆領域分を調整する
				x = context.leftR;
				ww -= leftRr; // その分表示エリアが減る
				leftRr = left + leftRr / w * wt;
			}
			//	以下同様
			float topRr = context.topR - y;
			if (topRr < 0.0 ) {
			//	被覆領域なし
				topRr = top;
			} else {
			//	被覆領域分を調整する
				y = context.topR;
				hh -= topRr; // その分表示エリアが減る
				topRr = top + topRr / h * ht;
			}
			float rightRr = (x+ww) - context.rightR;
			if (rightRr < 0.0 ) {
				rightRr = right;
			} else {
				ww -= rightRr;
				rightRr = right - rightRr / w * wt;
			}
			float bottomRr = (y+hh) - context.bottomR;
			if (bottomRr < 0.0 ) {
				bottomRr = bottom;
			} else {
				hh -= bottomRr; // その分表示エリアが減る
				bottomRr = bottom - bottomRr / h * ht;
			}

			if (ww>0 && hh>0){
			glTexCoord2f(leftRr,topRr); 	glVertex2f(x   ,y  );
			glTexCoord2f(rightRr,topRr);	glVertex2f(x+ww,y  );
			glTexCoord2f(rightRr,bottomRr); glVertex2f(x+ww,y+hh);
			glTexCoord2f(leftRr,bottomRr);	glVertex2f(x   ,y+hh);
			}
		}
		glEnd();
		unbind();
	}

	void	blt(DrawContext context,float x,float y,
		Rect* srcRect,Size* dstSize){
		if (!isLoad()) return ;

		float w = getWidth();
		float h = getHeight();

		if (w==0 || h==0) return ;

		Rect r;
		if (!srcRect) {
			r.left = 0; r.top = 0; r.right = w; r.bottom = h;
			srcRect = &r;
		}

		float wr = getWidthRate();
		float hr = getHeightRate();

		float left,top,right,bottom;
		left	= (wr * srcRect.left) / w;
		top		= (hr * srcRect.top) / h;
		right	= (wr * srcRect.right) / w;
		bottom	= (hr * srcRect.bottom) / h;

		float rateX = context.rateX;
		float rateY = context.rateY;

		w = srcRect.right-srcRect.left;
		h = srcRect.bottom-srcRect.top;

		if (w==0 || h==0) return ;

		//	転送先サイズは指定されているが..
		if (dstSize){
			w = dstSize.cx;
			h = dstSize.cy;
		} else {
			w = context.screenSizeX;
			h = context.screenSizeY;
		}

		x = x * rateX + context.offsetRX;
		y = y * rateY + context.offsetRY;

		w *= rateX;
		h *= rateY;

		bind();
		glBegin(GL_POLYGON);

		//	クリップ処理は、openglに任せたので、もはや不要なのだ
		if ( /* !context.isClip() */ true ) {
		//	クリップ無し
			glTexCoord2f(left,top); glVertex2f(x  ,y  );
			glTexCoord2f(right,top); glVertex2f(x+w,y  );
			glTexCoord2f(right,bottom); glVertex2f(x+w,y+h);
			glTexCoord2f(left,bottom); glVertex2f(x  ,y+h);
		} else {
		//	クリップ有り
			float ww = w , hh = h;
			float wt = right-left , ht = bottom-top; // テクスチャ座標での距離
			//	クリップされる被覆領域を求める
			float leftRr = context.leftR - x; // ドット数
			if (leftRr < 0.0 ) {
			//	被覆領域なし
				leftRr = left;
			} else {
			//	被覆領域分を調整する
				x = context.leftR;
				ww -= leftRr; // その分表示エリアが減る
				leftRr = left + leftRr / w * wt;
			}
			//	以下同様
			float topRr = context.topR - y;
			if (topRr < 0.0 ) {
			//	被覆領域なし
				topRr = top;
			} else {
			//	被覆領域分を調整する
				y = context.topR;
				hh -= topRr; // その分表示エリアが減る
				topRr = top + topRr / h * ht;
			}
			float rightRr = (x+ww) - context.rightR;
			if (rightRr < 0.0 ) {
				rightRr = right;
			} else {
				ww -= rightRr;
				rightRr = right - rightRr / w * wt;
			}
			float bottomRr = (y+hh) - context.bottomR;
			if (bottomRr < 0.0 ) {
				bottomRr = bottom;
			} else {
				hh -= bottomRr; // その分表示エリアが減る
				bottomRr = bottom - bottomRr / h * ht;
			}

			if (ww>0 && hh>0){
			glTexCoord2f(leftRr,topRr); 	glVertex2f(x   ,y  );
			glTexCoord2f(rightRr,topRr);	glVertex2f(x+ww,y  );
			glTexCoord2f(rightRr,bottomRr); glVertex2f(x+ww,y+hh);
			glTexCoord2f(leftRr,bottomRr);	glVertex2f(x   ,y+hh);
			}
		}
		glEnd();
		unbind();
	}

	//	visitor パターンなのだ
	/*
		DrawContextに基づいて拡大縮小してオフセット分、移動させて、
		クリップして描画しないといけない。
	*/
	void	blt(DrawContext context,Rect* srcRect,Point[4] dstPoint){
		if (!isLoad()) return ;

		//	クリップ処理は、openglに任せたので、もはや不要なのだ
		/*
		if (context.isClip()){
			//	clipするんかー。お手上げにょ
			Point[4] points;

			points[0].setPoint(0,0);
			points[1].setPoint(getWidth()-1,0);
			points[2].setPoint(getWidth()-1,getHeight()-1);
			points[3].setPoint(0,getHeight()-1);

			blt(context,points,dstPoint);
			return ;
		}
		*/

		float w = getWidth();
		float h = getHeight();

		if (w==0 || h==0) return ;

		Rect r;
		if (!srcRect) {
			r.setRect(0,0,w,h);
			srcRect = &r;
		}

		float wr = getWidthRate();
		float hr = getHeightRate();

		float left,top,right,bottom;
		left	= (wr*srcRect.left) / w;
		top		= (hr*srcRect.top) / h;
		right	= (wr*srcRect.right) / w;
		bottom	= (hr*srcRect.bottom) / h;

		Point[4] dp;
		for(int i=0;i<4;++i){
			dp[i].x = dstPoint[i].x*context.rateX + context.offsetRX;
			dp[i].y = dstPoint[i].y*context.rateY + context.offsetRY;
		}

		bind();
		glBegin(GL_POLYGON);
			glTexCoord2f(left,top);
			glVertex2f(dp[0].x,dp[0].y);

			glTexCoord2f(right,top);
			glVertex2f(dp[1].x,dp[1].y);

			glTexCoord2f(right,bottom);
			glVertex2f(dp[2].x,dp[2].y);

			glTexCoord2f(left,bottom);
			glVertex2f(dp[3].x,dp[3].y);
		glEnd();
		unbind();
	}

	//	visitor パターンなのだ
	/*
		DrawContextに基づいて拡大縮小してオフセット分、移動させて、
		クリップして描画しないといけない。

		転送元は凸四角形。転送先も凸四角形。超ウザー（ﾟДﾟ）<BR>

	*/
	void	blt(DrawContext context,Point[4] srcPoint,Point[4] dstPoint)
	{
		if (!isLoad()) return ;

		//	クリップ処理は、openglに任せたので、もはや不要なのだ

		float w = getWidth();
		float h = getHeight();

		if (w==0 || h==0) return ;

		float wr = getWidthRate();
		float hr = getHeightRate();

		bind();
		glBegin(GL_POLYGON);
			for(int i=0;i<4;++i){
				float xx	= (wr*srcPoint[i].x) / w;
				float yy	= (hr*srcPoint[i].y) / h;
				glTexCoord2f(xx,yy);
				float dx	= dstPoint[i].x * context.rateX + context.offsetRX;
				float dy	= dstPoint[i].y * context.rateY + context.offsetRY;
				glVertex2f(dx,dy);
			}
		glEnd();
		unbind();

	}
	
private:
	static uint mainThreadId;		//!< 描画を行うスレッドのＩＤ
	static StorableTexture[] unRegList;		//!< テクスチャを生成していないものキュー
	bool bReg;
	bool bDup;

	uint	myId = 0xFFFFFFFF;
	uint	textureName;	//	openGLではテクスチャー名は整数
	bool	bLoad;			//	テクスチャーを読み込んでいるか
	int		sizeX,sizeY;	//	テクスチャーサイズ
	int		sizeRX,sizeRY;	//	テクスチャーの実サイズ
	float	widthRate,heightRate; // 指定されたテクスチャーサイズと
							//	実際に確保されたサイズとの比
	Surface tmpSurface;		//	SDL_surfaceのwrapperとしてテンポラリ的に用いる
	Surface dupSurface;		//	必要ならばRGB888,ARGB8888等の
							//	サーフェースを作成するために利用される
	bool	bAlpha;			//	α付きサーフェースか
	bool	bSmooth;		//	テクスチャ貼り付け方法(trueならばGL_LINEAR)
	bool	bColorKey;		//	カラーキーが設定されているか。
	uint	r,g,b;			//	カラーキー
	bool	bColorKeyPos;	//	位置指定のカラーキーが設定されているか。
	uint	cx,cy;			//	位置指定型カラーキーのcx,cy
	
	char[] strFileName;		//!< リストア対策（ファイル名を保持しておきファイルからリストア）
	Surface backupSurface;	//!< サーフェイスに保持しておき、それからリストア
	bool bRestore;
	uint textureType;
	bool useTxRc;
	

	//	2^nに繰り上げる
	int	floor(int n){
		/*
			2^nに繰り上げる by yaneurao 2003 Dec.
		*/
		int r=1,bits=0;
		while (n) {
			bits += n & 1;
			n >>= 1; r <<= 1;
		}
		return (bits==1) ? (r>>1) : r;
	}

	/// 内部読込
	y4d_result innerSetSurface(Surface surface,bool bDup_){

		sizeX = tmpSurface.getWidth();
		sizeY = tmpSurface.getHeight();
		bDup = bDup_;
		
//	printf("sizeX,sizeY:%d,%d\n",sizeX,sizeY);

		if (sizeX == 0 || sizeY == 0){
			sizeX = sizeY = 0;
			return y4d_result.precondition_error; // おかしい..
		}

		useTxRc = false;

		//  GL_TEXTURE_RECTANGLE_ARBが使える状況なら積極的に使う。
		//　GL_TEXTURE_RECTANGLE_ARBが使えずにNPOT拡張が使えるというのは
		//  あまり考えにくいのだが(´ω`)
		if (localOption.TxRc && GlExtensions.instance.isAvailableRectTexture(sizeX, sizeY))
		{
			textureType = opengl.GL_TEXTURE_RECTANGLE_ARB;

			sizeRX = sizeX;
			sizeRY = sizeY;

			// 非正規化テクスチャなのでテクスチャ座標は
			// (0,0)×(1,1)の座標系ではなく、
			// (0,0)×(sizeRX,sizeRY)の座標系になる。

			widthRate = cast(float) sizeX;
			heightRate = cast(float) sizeY;

			// ↓これ本来どこで呼び出すのが正しいんだろ。(´ω`)
			// Gl.glEnable(Gl.GL_TEXTURE_RECTANGLE_ARB);

			// ↑draw contextを切り替えたときにうまく動く必要がある
			// bindするときに指定すべき。
			useTxRc = true;
		}
		else
		{
			if (localOption.NPOT && GlExtensions.instance.NPOT)
			{
				// NPOT拡張が使えるなら使う。

				// 2の累乗サイズのテクスチャでなくとも構わないならそうしたほうが
				// ビデオメモリの節約になって大変良い(´ー｀)ｖ

				// 2の累乗じゃなくてもいいならぴったりサイズで作成。
				sizeRX = sizeX;
				sizeRY = sizeY;

		//		npot = true;
			}
			else
			{
				sizeRX = floor(sizeX);
				sizeRY = floor(sizeY);
			}
			
			textureType = opengl.GL_TEXTURE_2D;

			widthRate = (cast(float)sizeX) / sizeRX;
			heightRate = (cast(float)sizeY) / sizeRY;
		}
		
		//	読み込んだsurfaceの特質を調べる。
		if (!bAlpha) {
			//	RGB888でなければならない
			if (!tmpSurface.checkRGB888() || sizeX!=sizeRX || sizeY!=sizeRY
				|| bDup ){
				dupSurface.createDIB(sizeRX,sizeRY,false);
				dupSurface.blt(tmpSurface,0,0);
				bDup = true;
			}
		} else {
			//	ARGB8888でなければならない
			if (!tmpSurface.checkRGBA8888() || sizeX!=sizeRX || sizeY!=sizeRY
				|| bDup ){
				dupSurface.createDIB(sizeRX,sizeRY,true);
				dupSurface.blt(tmpSurface,0,0);
				bDup = true;

			/*	//	書き換える例)
				SDL_Surface* s = dupSurface.getSurface();
				SDL_LockSurface(s);

				for(int i=0;i < s.w * s.h ; ++i){
					((ubyte*)s.pixels)[i*4 + 3] = 255;
				}

				SDL_UnlockSurface(s);
			*/

			}
			
			if (bColorKey || bColorKeyPos) {
			//	カラーキーが有効なので、カラーキーの該当部分のαを0に
			//	書き換えていく作業が必要となる。

version (LittleEndian) {
				uint colorKeyMask = 0x00ffffff;
} else version (BigEndian) {
				uint colorKeyMask = 0xffffff00;
}

				uint* p = bDup ?
					cast(uint*)(dupSurface.getPixels()):
					cast(uint*)(tmpSurface.getPixels());

				uint colorKey;
version (LittleEndian) {
				if (bColorKey) {
					colorKey  = (b << 16) + (g << 8) + r;
				} else {	//	bColorKeyPos == true
					if (cx<sizeX && cy<sizeY){
						colorKey = p[cx + cy*sizeY] & colorKeyMask;
					} else {
					//	範囲外だとダメじゃん..
						goto exitColorKey;
					}
				}
} else version (BigEndian) {
				if (bColorKey) {
				colorKey  = (r << 24) + (g << 16) + (b<<8);
				} else {	//	bColorKeyPos == true
					if (cx<sizeX && cy<sizeY){
						colorKey = p[cx + cy*sizeY] & colorKeyMask;
					} else {
					//	範囲外だとダメじゃん..
						goto exitColorKey;
					}
				}
}

				for(int y=0;y<sizeY;++y){
					for(int x=0;x<sizeX;++x){
						uint data = p[x];
						if ((data & colorKeyMask) == colorKey){
							data &= colorKeyMask;
							p[x] = data;
						}
					}
					p += sizeRX;	//	1ラスタ送る
				}
			}
		}
exitColorKey:;

		if ( isMainThread() ) {
			reg();
		} else {
			markUnReg(this);
		}
		
		return y4d_result.no_error;
	}
	
	/// 内部サーフェイスを指定サーフェスで入れ替える
	y4d_result innerSubSurfaceFast(Surface surface)
	/**
		単純にロードされているテクスチャを上書きする。
		リサイズを行わないのでボトルネックは小さい
	*/
	{

		sizeX = surface.getWidth();
		sizeY = surface.getHeight();

		bind();

		void * v = surface.getPixels();

		//  テクスチャ入れ替え！！
		// これは2のべき上でないサイズも使える！
		glTexSubImage2D(textureType,
			0,	// texture level
			0,	// xoffset
			0,	// yoffset
			sizeX, sizeY,
			bAlpha ? GL_RGBA : GL_RGB,	// α付き画像ならば GL_RGBAを指定する
			GL_UNSIGNED_BYTE, 
			v
		);

		widthRate	= 1.0f;
		heightRate	= 1.0f;

		return y4d_result.no_error;
	}
}	



/// 実Textureクラスをwrapして、リストアに対応したクラス
class Texture : ICacheObject ,ITextureBase  {
	/// コンストラクタ
	this() {
		try {
			if ( emptyQueue.length <= 0 ) {
				Log.printError("TEXTURE MAX : Can't create Texture!");
				throw new Exception("TEXTURE MAX : Can't create Texture!");
			}
			m_id = emptyQueue[length-1];
			emptyQueue.length = emptyQueue.length-1;
			
			assert( !(m_id in textures) );
			
			textures[m_id] = new StorableTexture();
			
			m_id = counter++;
			textures[m_id] = new StorableTexture();

//printf("new id = %d\n", m_id);
		} catch (Object e) {
			m_id = 0;
			Log.printError("Texture#_CStr : [%s] remain= %s ", e,toString(), emptyQueue.length);
		}
	}
	
	/// デストラクタ
	~this() {
		try {
			assert( m_id in textures );
			StorableTexture t = getMyTexture();
			if (t) {
				t.release();
				t.releaseBackSurface();
			}
			textures.remove(m_id);
			emptyQueue ~= m_id;

//printf("release id = %d\n", m_id);
		} catch (Object e) {
			Log.printError("Texture Reak! : [%s] id = %s ", e,toString(), m_id);
		}
	}
	
	public static uint getAllTextureBufferSize() {
		uint size = 0;
		foreach (t; textures.values) {
			size += t.getBufferSize();
		}
		Log.printLook("Texture#getAllTextureBufferSize : [%s] KB", (size / 1024UL));
		return 0;
	}

	
	/// テクスチャのデフォルト設定
	public static IGlTextureOption globalOption() {
		return StorableTexture.globalOption;
	}
	
	/// テクスチャのデフォルト設定
	public static IGlTextureOption localOption() {
		return StorableTexture.localOption;
	}
	
	/// 安全にリリースを行う
	static void safeRelease(inout Texture texture) {
		if (texture is null) {
			return;
		}
		try {
			texture.release();
			texture = null;
		} catch (Object o) {
			Log.printError("Texture#safeRelease: [%s]", o.toString());
		}
	}
	
	/// 全テクスチャの解放
	static void releaseAll() {
		try {
			foreach (StorableTexture t; textures ) {
				t.release();
			}
		} catch (Exception e) {
			Log.printError("Texture#releaseAll : Exception [%s]", e.toString());
		}
	}
	
	/// 最後に全てのサーフェイス＆テクスチャを解放するときに呼び出す
	static void releaseAllFinal() {
		try {
			foreach (StorableTexture t; textures ) {
				t.release();
				t.releaseBackSurface();
				t = null;
			}
		} catch (Exception e) {
			Log.printError("Texture#releaseAll : Exception [%s]", e.toString());
		}
	}
	
	/// 全テクスチャの復元
	static void restoreAll() {
		try {
			try {
				std.gc.fullCollect();
			} catch (Exception e) {
				Log.printError("Texture#restoreAll : std.gc.fullCollect [%s]", e.toString());
			}

			foreach (StorableTexture t; textures ) {
				if ( t is null ) {
					Log.print("restore texture null");
				}
				t.restore();
			}
			
			textures = textures.rehash;
			
			Log.print("Texture restore finished!");

		} catch (Exception e) {
			Log.printError("Texture#restoreAll : Exception [%s]", e.toString());
		}
	}

	/// メインスレッドのＩＤを設定する
	synchronized static void setMainThreadId(uint id)
	/**
		別スレッドでテクスチャのロードを行うような場合
		テクスチャを生成するスレッドはメインスレッドでなければならないようで、
		このＩＤをみて、生成するか、遅延するか判断する
	*/
	{
		StorableTexture.setMainThreadId(id);
	}
	
	/// 遅延生成リストに追加する
	synchronized static void markUnReg(Texture t) 
	/**
		リストを参照として保持しているので、
		実体化を待っているままにすると、そのテクスチャはいつまでも解放されない
		従って、しかるべきタイミングで createTextureAll をかならず呼ぶべきである
	*/
	{
	}
	
	/// 生成遅延リストにテクスチャを生成する
	synchronized static bool createTextureAll() {
		return StorableTexture.createTextureAll();
	}
	
	/// 今、メインスレッドから呼びされているか
	synchronized static bool isMainThread() {
		return StorableTexture.isMainThread();
	}
	
	/// 遅延生成になっているテクスチャを有効化する
	bool reg() {
		return getMyTexture().reg();
	}
	
	///	指定されたファイル名の画像を読み込む
	/**
		読み込みに失敗すれば非0が返る。
	*/
	y4d_result load(char[] filename)
	{
		return  getMyTexture().load(filename);
	}

	///	SDL_Surfaceを渡し、それをテクスチャーとする
	y4d_result	setSurface(SDL_Surface* surface){
		return getMyTexture().setSurface(surface); // surface,dup/init
	}

	///	Surfaceを渡し、それをテクスチャーとする
	y4d_result setSurface(Surface surface) {
		return getMyTexture().setSurface(surface); // surface,dup/init
	}

	///	Surfaceを渡し、それをテクスチャーと入れ替える
	y4d_result subSurfaceFast(inout Surface surface)
	/**
		テクスチャ生成時にリサイズを行わない
		かつ、参照されたサーフェイスを再利用させるために解放しない
	*/	
	{
		return getMyTexture().subSurfaceFast(surface);
	}

	///	テクスチャーの解放
	/**
		loadで読み込んだ画像を解放する。
	*/
	void	release() {
		getMyTexture().release();
	}

	///	テクスチャーを貼り付けるときに使う
	/**
		この関数を呼び出したあと、
<PRE>
		glBegin(GL_POLYGON);
			glTexCoord2f(0 , 0); glVertex2f(-0.9 , -0.9);
			glTexCoord2f(0 , 1); glVertex2f(-0.9 , 0.9);
			glTexCoord2f(1 , 1); glVertex2f(0.9 , 0.9);
			glTexCoord2f(1 , 0); glVertex2f(0.9 , -0.9);
		glEnd();
</PRE>
		のようにすれば、テクスチャーをポリゴンにはりつけることが出来ます。
	*/
	void	bind() {
		getMyTexture().bind();
	}

	///	テクスチャーのバインドを解除する
	/**
		bind();
		でバインドしていたテクスチャーをunbindする。
	*/
	void	unbind(){
		getMyTexture().unbind();
	}

	///	画像を読み込んでいるか
	/**
		テクスチャーを生成しているときはtrueを返す。
	*/
	bool	isLoad() {
		return getMyTexture().isLoad();
	}

	///	テクスチャーの幅を取得
	/**
		テクスチャーを読み込んでいないときは0を返す。
	*/
	float getWidth() { return getMyTexture().getWidth(); }

	///	テクスチャーの高さを取得
	/**
		テクスチャーを読み込んでいないときは0を返す。
	*/
	float getHeight() { return getMyTexture().getHeight(); }

	///	テクスチャーの確保された幅を取得
	/**
		テクスチャーは、実際には2のべき乗に切り上げて確保される。
		テクスチャーを読み込んでいないときは0を返す。
	*/
	int	getRWidth() { return getMyTexture().getRWidth(); }

	///	getWidth()/getRWidth() を返す
	float getWidthRate() { return getMyTexture().getWidthRate(); }

	///	getHeight()/getRHeight() を返す
	float getHeightRate() { return getMyTexture().getHeightRate(); }

	///	テクスチャーの確保された高さを取得
	/**
		テクスチャーは、実際には2のべき乗に切り上げて確保される。
		テクスチャーを読み込んでいないときは0を返す。
	*/
	int	getRHeight() { return getMyTexture().getRHeight(); }

	///	確保しているテクスチャーで使用しているメモリサイズを取得する
	/**
		単位は[byte]。cacheシステムでテクスチャーとして使用している
		サイズを計測するのに使用する。
	*/
	ulong getBufferSize() {
		return getMyTexture().getBufferSize();
	}

	///	α転送を無効に
	/**
		ディフォルトではαは有効。
		元情報にαが含まれていない場合で、α付きの転送を行なわないと
		わかっている場合(BGなど)は、無効化しておくことで、
		転送時の高速化をはかれる。<BR>

		抜き色の指定をする場合は、αサーフェースでなければならない。
		(抜き色をαを書き換えて実現しているため)
	*/
	void	disableAlpha() { getMyTexture().disableAlpha(); }

	///	α転送を有効に
	/**
		ディフォルトではαは有効。
		元情報にαが含まれていない場合で、α付きの転送を行なわないと
		わかっている場合(BGなど)は、無効化しておくことで、
		転送時の高速化をはかれる。<BR>

		抜き色の指定をする場合は、αサーフェースでなければならない。
		(抜き色をαを書き換えて実現しているため)<BR>

		また、画像を読み込む(load)するより以前に、この関数を
		呼び出しておかなければならない。
	*/
	void	enableAlpha() { getMyTexture().enableAlpha(); }

	///	抜き色の指定
	/**
		転送時の転送元カラーキー(抜き色)を指定します。
		これを指定して、loadを行なうと、そのサーフェースの該当色の部分の
		α値が0(透過)になる。(ただしαサーフェース作成時のみ)<BR>

		r,g,bはそれぞれ0～255。<BR>

		また、画像を読み込む(load)するより以前に、この関数を
		呼び出しておかなければならない。(loadするときに抜き色部分の
		α値を0に書き換えるので)<BR>

		その後の画像読み込み、解放によっては設定したカラーキーは
		無効にはならない。
	*/
	void	setColorKey(int r_,int g_,int b_){
		getMyTexture().setColorKey(r_,g_,g_);
	}

	///	テクスチャーのある座標をカラーキーにする
	/**
		また、画像を読み込む(load)するより以前に、この関数を
		呼び出しておかなければならない。(loadするときに抜き色部分の
		α値を0に書き換えるので)<BR>

		その後の画像読み込み、解放によっては設定したカラーキーは
		無効にはならない。
	*/
	void	setColorKeyPos(int cx_,int cy_){
		getMyTexture().setColorKeyPos(cx_,cy_);
	}

	///	カラーキーの取り消し
	/**
		setColorKey / setColorKeyPos で設定した情報を無効化します。
	*/
	void	resetColorKey() {
		getMyTexture().resetColorKey();
	}
	
	uint getTextureType() {
		return getMyTexture().getTextureType();
	}

	///	テクスチャを貼るときに線形補間する
	/**
		テクスチャを GL_LINEAR で貼る。
		線形補間されるので拡大・縮小時に綺麗に貼れる。
		むかしのビデオカードだとハードで対応していないので遅くなる。
		ディフォルトではtrue。<BR>

		この関数を設定するなら、loadを行なう前に設定しておくこと。<BR>

		BG用の画像等は、(拡大縮小しないならば) setSmooth(false);　しておけば
		良いと思われる。
	*/
	void	setSmooth(bool b) { getMyTexture().setSmooth(b); }
	
	/// 自分自身をコピーして帰す
	Texture dup() {
		Texture t = new Texture();
		t.load(getMyTexture.getFilename());
		return t;
	}
	
	/// 読み込んでいるファイル名を返却する（デバッグ用）
	char[] getFilename() {
		return 	getMyTexture().getFilename();
	}
	
	
	/// キューの初期化
	static this() {
		emptyQueue = new uint[MAX_TEXURE_NUM];
		foreach(uint i, inout uint l; emptyQueue) {
			l = i;
		}
	}
	
	/// 静的デストラクタ
	static ~this() {
		try {
			foreach (StorableTexture t; textures ) {
				if (t) {
					t.release();
					t = null;
				}
			}
		} catch(Object e) {
			printf("Texutre releaseAll Error\n");
		}
	}
	

protected:
	//	visitor パターンなのだ
	/*
		DrawContextに基づいて拡大縮小してオフセット分、移動させて、
		クリップして描画しないといけない。

		実装面倒くさいですなぁ．．（´Д｀）
	*/
	void	blt(DrawContext context,float x,float y)
	{
		getMyTexture().blt(context,x,y);
	}

	//	visitor パターンなのだ
	/*
		DrawContextに基づいて拡大縮小してオフセット分、移動させて、
		クリップして描画しないといけない。

		実装面倒くさいですなぁ．．（´Д｀）
	*/
	void	blt(DrawContext context,float x,float y,Rect* srcRect){
		getMyTexture().blt(context,x,y,srcRect);
	}

	void	blt(DrawContext context,float x,float y,
		Rect* srcRect,Size* dstSize){
		getMyTexture().blt(context,x,y,srcRect,dstSize);
	}

	//	visitor パターンなのだ
	/*
		DrawContextに基づいて拡大縮小してオフセット分、移動させて、
		クリップして描画しないといけない。
	*/
	void	blt(DrawContext context,Rect* srcRect,Point[4] dstPoint){
		getMyTexture().blt(context,srcRect,dstPoint);
	}

	//	visitor パターンなのだ
	/*
		DrawContextに基づいて拡大縮小してオフセット分、移動させて、
		クリップして描画しないといけない。

		転送元は凸四角形。転送先も凸四角形。超ウザー（ﾟДﾟ）<BR>

	*/
	void	blt(DrawContext context,Point[4] srcPoint,Point[4] dstPoint)
	{
		getMyTexture().blt(context,srcPoint,dstPoint);
	}
	
	/// 自分自身のテクスチャを返す
	StorableTexture getMyTexture() {
		assert( m_id in textures );
		return textures[m_id];
	}
	
private:
	static final uint MAX_TEXURE_NUM = 65535;
	static StorableTexture[uint] textures;
	static uint[] emptyQueue;
	static uint counter = 0;
	final uint m_id;

}