/*
 *	cliprodg.c
 *
 *	ROaD GENerator
 *
 *	* Fri Jun 02 21:33:04 JST 2017 Naoyuki Sawa
 *	- 1st リリース。
 *	* Sat Jun 03 21:50:28 JST 2017 Naoyuki Sawa
 *	- RodGen_DrawLine2()を追加しました。
 *	  既存のRodGen_DrawLine1()とRodGen_DrawLine2()の違いは、引数の指定方法の違いだけであり、どちらを使っても同じ描画結果が得られます。
 *	  アプリケーション次第で使い易い方の関数を使って下さい。
 *	  一般的には、RodGen_DrawLine1()の方が柔軟性が高いですが、RodGen_DrawLine2()の方が直感的で使い易いです。
 *	  RodGen_DrawLine1()の方が柔軟性が高い理由は、RodGen_DrawLine1()で(zoom_n=0,zoom_d<0)に相当する描画が、RodGen_DrawLine2()では指定出来ないからです。
 *	  とは言え、RodGen_DrawLine1()で(zoom_n=0,zoom_d<0)に相当する描画に依存するようなアプリケーションは稀だと思うので、あまり気にする必要は有りません。
 *	* Sun Jun 04 22:03:50 JST 2017 Naoyuki Sawa
 *	- RodGen_DrawSeg()を追加しました。
 */
#include "clip.h"
/****************************************************************************
 *	グローバル関数
 ****************************************************************************/
//管理構造体を初期化する。
//[in]
//	pRodGen			管理構造体。
//	vram			VRAMの先頭アドレス。
//	vram_width		VRAMの1ライン当たりのピクセル数。			1〜UINT16_MAX
//	vram_pitch		VRAMの次のラインへのバイトオフセット。			1〜UINT16_MAX
//	rgfx			道のパターンの先頭アドレス。
//	rgfx_width		道のパターンの1パターン当たりのピクセル数。		1〜UINT16_MAX
//	rgfx_pitch		道のパターンの次のパターンへのバイトオフセット。	1〜UINT16_MAX
//[note]
//	- VRAM,及び,道のパターンは、1ピクセル1バイトと見なします。
//	  P/ECEの場合、VRAMはPIECE_VRAMの形式に一致していますが、道のパターンはPIECE_BMPの形式に一致していない事に注意して下さい。
//	  道のパターンのソースとしてPIECE_BMPを使用する場合は、あらかじめ1ピクセル1バイトの形式に展開してから当モジュールに指定して下さい。
void RodGen_Init(ST_RodGen* pRodGen, uint8_t* vram, int vram_width, int vram_pitch, const uint8_t* rgfx, int rgfx_width, int rgfx_pitch) {
	memset(pRodGen, 0, sizeof(ST_RodGen));
	if((((vram_width < 1) || (vram_width > UINT16_MAX)) || ((vram_pitch < 1) || (vram_pitch > UINT16_MAX))) ||
	   (((rgfx_width < 1) || (rgfx_width > UINT16_MAX)) || ((rgfx_pitch < 1) || (rgfx_pitch > UINT16_MAX)))) { DIE(); }
	pRodGen->vram		= vram;
	pRodGen->vram_width	= vram_width;
	pRodGen->vram_pitch	= vram_pitch;
	pRodGen->rgfx		= rgfx;
	pRodGen->rgfx_width	= rgfx_width;
	pRodGen->rgfx_pitch	= rgfx_pitch;
}
/*--------------------------------------------------------------------------*/
//画面上の指定されたラインに、指定された道のパターンを、指定された位置が道の端になるように描画します。
//[in]
//	pRodGen			管理構造体。
//	iLine			画面上のライン番号。
//	iRoad			道のパターン番号。
//	x1			道のパターンの左端ピクセルを、画面上の左から何ピクセル目に描画するか。
//	x2			道のパターンの右端ピクセルを、画面上の左から何ピクセル目に描画するか。
//[note]
//	- x1,x2はピクセル座標系であり、グリッド座標系ではない事に注意して下さい。
//	  x2の隣の位置が道のパターンの右端ピクセルに相当するのではなく、x2自身の位置が道のパターンの右端ピクセルに相当するように描画します。
//	  既存のdraw_scanline_woz_2bit()関数等はグリッド座標系でしたが、当関数はピクセル座標系である事に注意して下さい。
//	  ピクセル座標系にした理由は、道の描画においては左端と右端の座標計算に違いは無いので、ピクセル座標系の方が使い易いと思ったからです。
#ifndef PIECE
void RodGen_DrawLine2(ST_RodGen* pRodGen, int iLine, int iRoad, int x1, int x2) {
	int zoom_n = x2 - x1;
	if(zoom_n >= 0) {
		zoom_n++;
	} else {
		zoom_n--;
	}
	RodGen_DrawLine1(pRodGen, iLine, iRoad, x1, zoom_n, pRodGen->rgfx_width);
}
#else //PIECE
void RodGen_DrawLine2(ST_RodGen* pRodGen, int iLine, int iRoad, int x1, int x2);
asm("
		.code
		.align		1
		.global		RodGen_DrawLine2
RodGen_DrawLine2:
		xsub		%sp, %sp, 8
		;//%r12     := pRodGen
		;//%r13     := iLine
		;//%r14     := iRoad
		;//%r15     := x1
		;//[%sp+ 0] := (zoom_n)
		;//[%sp+ 4] := (zoom_d)
		;//[%sp+ 8] := retp
		;//[%sp+12] := x2
		xld.w		%r10, [%sp+12]			;//%r10    :=          x2
		xld.uh		%r11, [%r12+12]			;//%r11    := zoom_d = pRodGen->rgfx_width		*anti-interlock*
		sub		%r10, %r15			;//%r10    := zoom_n = x2 - x1
		jrge.d		3				;//if(zoom_n >= 0) {
		 add		%r10, 1				;//         %r10 := zoom_n++				*delay*
		 sub		%r10, 2				;//} else { %r10 := zoom_n-- }
		xld.w		[%sp+0], %r10			;//[%sp+0] := zoom_n
		xld.w		[%sp+4], %r11			;//[%sp+4] := zoom_d
		xcall		RodGen_DrawLine1		;//RodGen_DrawLine1(pRodGen, iLine, iRoad, x1, zoom_n, zoom_d)
		xadd		%sp, %sp, 8
		ret
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
//道のセグメントを描画します。
//[in]
//	pRodGen			管理構造体。
//	iLine1,iRoad1,x11,x21	開始ラインのパラメータ。開始ラインは(画面の描画範囲内ならば)描きます。┬パラメータの意味はRodGen_DrawLine2()の説明を参照して下さい。
//	iLine2,iRoad2,x12,x22	目標ラインのパラメータ。目標ラインは(画面の描画範囲内でも)描きません。┘
//	iLineMin,iLineMax	画面の描画範囲。iLineMin以上,iLineMax以下が描画範囲となります。
//[note]
//	- 画面の描画範囲によるクリッピングは、予め計算で行うのでなく、ループ内で毎ライン比較して行う事にしました。
//	  そうした理由は、一般的な当関数の使用方法では、画面の描画範囲外になるラインの数は少ないと思ったからです。
//	  予め計算を行うコストよりも、ループ内で毎ライン比較するコストが少ないと思ったので、こうする事にしました。
//	  また、予め計算を行うよりも、ループ内で毎ライン比較する方が実装が単純なので、コードサイズが節約出来ます。
//	- 単純な道のパターンのセグメントを描くだけならば、(iRoad1==iRoad2)を指定して下さい。
//	  一つのセグメント内の道に模様を描く場合は、一連の道のパターン内に模様を付けておき、(iRoad1!=iRoad2)を指定して下さい。
void RodGen_DrawSeg(ST_RodGen* pRodGen, int iLine1/*含む*/,   int iRoad1, int x11, int x21,	//開始ライン(描く)	┬(iLine1<iLine2)でも(iLine1>iLine2)でも(iLine1==iLine2)でも構いません。
                                        int iLine2/*含まず*/, int iRoad2, int x12, int x22,	//目標ライン(描かない)	┘
                                        int iLineMin/*含む*/, int iLineMax/*含む*/) {		//画面の描画範囲
	//ライン数(+/-)を求める。
	int dLine = iLine2 - iLine1;
	//ライン数が0でなければ…
	if(dLine) {
		//DDAセットアップ処理を行う。
		struct dda dda_iRoad, dda_x1, dda_x2;
		dda_setup(&dda_iRoad, dLine, iRoad1, iRoad2);
		dda_setup(&dda_x1, dLine, x11, x12);
		dda_setup(&dda_x2, dLine, x21, x22);
		//現在のラインが目標ラインに到達するまで…
		do {
			//DDAステップ実行を行う。
			int iRoad = dda_step(&dda_iRoad);
			int x1 = dda_step(&dda_x1);
			int x2 = dda_step(&dda_x2);
			//現在のラインが画面の描画範囲内ならば…
			if((iLine1 >= iLineMin) && (iLine1 <= iLineMax)) {
				//1ライン分の描画を行う。
				RodGen_DrawLine2(pRodGen, iLine1, iRoad, x1, x2);
			}
			//現在のラインを目標ラインの方へ進める。
			if(iLine1 < iLine2) { iLine1++; }
			if(iLine1 > iLine2) { iLine1--; }
		} while(iLine1 != iLine2);	//目標ライン自体は描かない事に注意せよ。
	}
}
