/*	
 *	framdobj.c
 *
 *	高速RAMに転送するモジュール
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2002 Naoyuki Sawa
 *
 *	* Tue Nov 19 12:30:00 JST 2002 Naoyuki Sawa
 *	- 旧clipdobj.cとclipmod.cから、コピー・修正して新規作成しました。
 *	* Sun Dec 8 05:43:00 JST 2002 Naoyuki Sawa
 *	- mod_mix()のループ音バグを修正。
 *	- テンポエフェクト対応。
 *	* Fri Jan 31 21:57:00 JST 2003 Naoyuki Sawa
 *	- new_LCDDrawObject()のPIXEL(p,m)マクロのmask判定部分を改良。
 *	 「(mask >> (m) & 1)」→「(mask & (1 << (m)))」に変更しました。
 *	  約3%高速化(全画面転送時)、コードサイズは20バイト少なくなりました。
 *	  この改良は「ryukの薫製工房」さん(http://www.interq.or.jp/earth/ryuk/)によるものです。
 *	- new_LCDDrawObject()のクリッピング時画面外判定処理を改良。
 *	  これまで、反転なし/ありそれぞれの処理の中で行っていたものを、共通化してまとめました。
 *	  コードサイズが4バイト少なくなりました。
 *	- 以上の変更を行った結果、現時点のコードサイズは0xfe0(4064)バイトです。
 *	* Sat Feb 1 14:08:00 JST 2003 Naoyuki Sawa
 *	- new_LCDDrawObject()にて、残りライン数の取得「y = obj.dh」を共通化、容量節約しました。
 *	- MODドライバを可変チャネル数に対応しました。
 *	* Tue Apr 1 19:00:00 JST 2003 Naoyuki Sawa
 *	- clipfram.cを分割し、このファイルを新設しました。
 */
#include "clip.h"

/****************************************************************************
 *	高速版pceLCDDrawObject
 ****************************************************************************/

/* * 高速版pceLCDDrawObjectには、次のような制限があります。
 * - 転送方法がDRW_NOMAL、かつ、転送元ビットマップが2bitデータmask付きである場合以外は、
 *   本物のpceLCDDrawObjectに処理を任せます。呼び出しが増える分、少し遅くなります。
 * - 転送元矩形に、転送元ビットマップをはみ出す領域を指定すると、正しく動作しません。
 * - 転送元ビットマップは、横8ピクセル単位のサイズで作成されていなければいけません。
 *   この制限は、P/ECEビットマップ形式の制限でもあるので、特に注意する必要はありません。
 * * アプリケーション起動時に一度だけhook_LCDDrawObject()を呼び、
 *   アプリケーション終了時には必ずunhook_LCDDrawObject()を読んでください。
 */

extern int (*old_LCDDrawObject)(DRAW_OBJECT);	/* 本物のpceLCDDrawObjectを保存 -> clippce.c */

int
new_LCDDrawObject(DRAW_OBJECT obj)
{
	PIECE_VRAM dest;
	PIECE_BMP src;
	int n, x, y, step, dest_stride, src_stride;
	unsigned char *dest_buf, *src_mask, mask;
	unsigned short *src_buf, pixel;

	/* 転送元ビットマップ情報を取り出しておきます。
	 * 2bitデータまたは2bitデータmask付きでなければ、本物に任せます。
	 */
	src = *obj.src;
	if(src.header.bpp == 2) {
		/* 転送先VRAM情報を取得。 */
		if(obj.dest != NULL) {
			dest = *obj.dest;
		} else {
			dest.w = DISP_X;
			dest.h = DISP_Y;
			dest.buf = pceLCDSetBuffer(INVALIDPTR);
		}

		/* 転送先クリッピング。 */
		if(!(obj.param & DRW_REVX)) {
			if(obj.dx < obj.clip.left) {
				n = obj.clip.left - obj.dx; /* 左にはみ出たピクセル数 */
				obj.dx = obj.clip.left;
				obj.dw -= n;
				obj.sx += n;
			}
			if(obj.dx + obj.dw > obj.clip.right) {
				obj.dw = obj.clip.right - obj.dx;
			}
		} else {
			if(obj.dx < obj.clip.left) {
				n = obj.clip.left - obj.dx; /* 左にはみ出たピクセル数 */
				obj.dx = obj.clip.left;
				obj.dw -= n;
			}
			if(obj.dx + obj.dw > obj.clip.right) {
				n = obj.dx + obj.dw - obj.clip.right; /* 右にはみ出たピクセル数 */
				obj.dw -= n;
				obj.sx += n;
			}
		}
		if(obj.dw <= 0) return 0; /* 画面外 */
		if(!(obj.param & DRW_REVY)) {
			if(obj.dy < obj.clip.top) {
				n = obj.clip.top - obj.dy; /* 上にはみ出たピクセル数 */
				obj.dy = obj.clip.top;
				obj.dh -= n;
				obj.sy += n;
			}
			if(obj.dy + obj.dh > obj.clip.bottom) {
				obj.dh = obj.clip.bottom - obj.dy;
			}
		} else {
			if(obj.dy < obj.clip.top) {
				n = obj.clip.top - obj.dy; /* 上にはみ出たピクセル数 */
				obj.dy = obj.clip.top;
				obj.dh -= n;
			}
			if(obj.dy + obj.dh > obj.clip.bottom) {
				n = obj.dy + obj.dh - obj.clip.bottom; /* 下にはみ出たピクセル数 */
				obj.dh -= n;
				obj.sy += n;
			}
		}
		if(obj.dh <= 0) return 0; /* 画面外 */

		/* 転送元クリッピングは行いません。
		 * 元々指定された転送元矩形が転送元ビットマップ内に収まっていたならば、
		 * 転送先クリッピングによって転送元がはみ出すことはありえないからです。
		 * しかし本物は転送元クリッピングも行っているので、この点は非互換です。
		 */

		/* 転送元ピクセルアドレスを求めます。 */
		n = (src.header.w * obj.sy + obj.sx) / 8;
		src_buf = (unsigned short*)src.buf + n;
		src_mask = src.mask + n;
		src_stride = src.header.w / 8 - ((obj.sx & 7) + obj.dw - 1) / 8;
		/*                                                     ~~~      */
		/* 8ステップ完了時に進むのではなく、9ステップ目の先頭で進むから */

		/* 残り転送ライン数を用意しておきます。 */
		y = obj.dh;

		/* 反転方向によって、処理を分けます。(※DRW_NOMAL=0と仮定)
		 * 反転方向以外のフラグが指定されていたら、本物に任せます。
		 */
		switch(obj.param) {
		case 0/*反転なし*/:
			/* 転送先ピクセルアドレスを求めます。 */
			dest_buf = dest.buf + dest.w * obj.dy + obj.dx;
			dest_stride = dest.w - obj.dw;

			switch(src.header.mask) {
			case 0/*2bitデータ*/:
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_01: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
#define PIXEL(p) \
						/* ピクセルを、描きます。 */ \
						*dest_buf++ = pixel >> (p) * 2 & 3; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if(--x == 0) break;
					case 0: PIXEL(3);
					case 1: PIXEL(2);
					case 2: PIXEL(1);
					case 3: PIXEL(0);
					case 4: PIXEL(7);
					case 5: PIXEL(6);
					case 6: PIXEL(5);
					default/*7*/: PIXEL(4); goto LOOP_01;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf += dest_stride;
					src_buf += src_stride;
				} while(--y != 0);
				return 1; /* 描画完了 */

			case 1/*2bitデータmask付き*/:
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */
					mask = *src_mask;	/* ピクセル並びは MSB:01234567:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_02: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
						mask = *++src_mask;
#define PIXEL(p, m) \
						/* 不透明ピクセルなら、描きます。 */ \
						if(mask & (1 << (m))) *dest_buf = pixel >> (p) * 2 & 3; \
						dest_buf++; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if(--x == 0) break;
					case 0: PIXEL(3, 7);
					case 1: PIXEL(2, 6);
					case 2: PIXEL(1, 5);
					case 3: PIXEL(0, 4);
					case 4: PIXEL(7, 3);
					case 5: PIXEL(6, 2);
					case 6: PIXEL(5, 1);
					default/*7*/: PIXEL(4, 0); goto LOOP_02;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf += dest_stride;
					src_buf += src_stride;
					src_mask += src_stride;
				} while(--y != 0);
				return 1; /* 描画完了 */
			}
			break; /* 本物に任せます */

		case DRW_REVX:
			/* 転送先ピクセルアドレスを求めます。 */
			dest_buf = dest.buf + dest.w * obj.dy + obj.dx + obj.dw - 1;
			dest_stride = dest.w + obj.dw;

			switch(src.header.mask) {
			case 0/*2bitデータ*/:
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_11: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
#define PIXEL(p) \
						/* ピクセルを、描きます。 */ \
						*dest_buf-- = pixel >> (p) * 2 & 3; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if(--x == 0) break;
					case 0: PIXEL(3);
					case 1: PIXEL(2);
					case 2: PIXEL(1);
					case 3: PIXEL(0);
					case 4: PIXEL(7);
					case 5: PIXEL(6);
					case 6: PIXEL(5);
					default/*7*/: PIXEL(4); goto LOOP_11;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf += dest_stride;
					src_buf += src_stride;
				} while(--y != 0);
				return 1; /* 描画完了 */

			case 1/*2bitデータmask付き*/:
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */
					mask = *src_mask;	/* ピクセル並びは MSB:01234567:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_12: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
						mask = *++src_mask;
#define PIXEL(p, m) \
						/* 不透明ピクセルなら、描きます。 */ \
						if(mask & (1 << (m))) *dest_buf = pixel >> (p) * 2 & 3; \
						dest_buf--; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if(--x == 0) break;
					case 0: PIXEL(3, 7);
					case 1: PIXEL(2, 6);
					case 2: PIXEL(1, 5);
					case 3: PIXEL(0, 4);
					case 4: PIXEL(7, 3);
					case 5: PIXEL(6, 2);
					case 6: PIXEL(5, 1);
					default/*7*/: PIXEL(4, 0); goto LOOP_12;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf += dest_stride;
					src_buf += src_stride;
					src_mask += src_stride;
				} while(--y != 0);
				return 1; /* 描画完了 */
			}
			break; /* 本物に任せます */

		case DRW_REVY:
			/* 転送先ピクセルアドレスを求めます。 */
			dest_buf = dest.buf + dest.w * (obj.dy + obj.dh - 1) + obj.dx;
			dest_stride = -dest.w - obj.dw;

			switch(src.header.mask) {
			case 0/*2bitデータ*/:
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_21: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
#define PIXEL(p) \
						/* ピクセルを、描きます。 */ \
						*dest_buf++ = pixel >> (p) * 2 & 3; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if(--x == 0) break;
					case 0: PIXEL(3);
					case 1: PIXEL(2);
					case 2: PIXEL(1);
					case 3: PIXEL(0);
					case 4: PIXEL(7);
					case 5: PIXEL(6);
					case 6: PIXEL(5);
					default/*7*/: PIXEL(4); goto LOOP_21;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf += dest_stride;
					src_buf += src_stride;
				} while(--y != 0);
				return 1; /* 描画完了 */

			case 1/*2bitデータmask付き*/:
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */
					mask = *src_mask;	/* ピクセル並びは MSB:01234567:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_22: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
						mask = *++src_mask;
#define PIXEL(p, m) \
						/* 不透明ピクセルなら、描きます。 */ \
						if(mask & (1 << (m))) *dest_buf = pixel >> (p) * 2 & 3; \
						dest_buf++; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if(--x == 0) break;
					case 0: PIXEL(3, 7);
					case 1: PIXEL(2, 6);
					case 2: PIXEL(1, 5);
					case 3: PIXEL(0, 4);
					case 4: PIXEL(7, 3);
					case 5: PIXEL(6, 2);
					case 6: PIXEL(5, 1);
					default/*7*/: PIXEL(4, 0); goto LOOP_22;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf += dest_stride;
					src_buf += src_stride;
					src_mask += src_stride;
				} while(--y != 0);
				return 1; /* 描画完了 */
			}
			break; /* 本物に任せます */

		case DRW_REVX | DRW_REVY:
			/* 転送先ピクセルアドレスを求めます。 */
			dest_buf = dest.buf + dest.w * (obj.dy + obj.dh - 1) + obj.dx + obj.dw - 1;
			dest_stride = -dest.w + obj.dw;

			switch(src.header.mask) {
			case 0/*2bitデータ*/:
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_31: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
#define PIXEL(p) \
						/* ピクセルを、描きます。 */ \
						*dest_buf-- = pixel >> (p) * 2 & 3; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if(--x == 0) break;
					case 0: PIXEL(3);
					case 1: PIXEL(2);
					case 2: PIXEL(1);
					case 3: PIXEL(0);
					case 4: PIXEL(7);
					case 5: PIXEL(6);
					case 6: PIXEL(5);
					default/*7*/: PIXEL(4); goto LOOP_31;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf += dest_stride;
					src_buf += src_stride;
				} while(--y != 0);
				return 1; /* 描画完了 */

			case 1/*2bitデータmask付き*/:
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */
					mask = *src_mask;	/* ピクセル並びは MSB:01234567:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_32: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
						mask = *++src_mask;
#define PIXEL(p, m) \
						/* 不透明ピクセルなら、描きます。 */ \
						if(mask & (1 << (m))) *dest_buf = pixel >> (p) * 2 & 3; \
						dest_buf--; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if(--x == 0) break;
					case 0: PIXEL(3, 7);
					case 1: PIXEL(2, 6);
					case 2: PIXEL(1, 5);
					case 3: PIXEL(0, 4);
					case 4: PIXEL(7, 3);
					case 5: PIXEL(6, 2);
					case 6: PIXEL(5, 1);
					default/*7*/: PIXEL(4, 0); goto LOOP_32;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf += dest_stride;
					src_buf += src_stride;
					src_mask += src_stride;
				} while(--y != 0);
				return 1; /* 描画完了 */
			}
			break; /* 本物に任せます */
		}
	}

	/* 高速ルーチンが対応していない描画方式なら、本物に任せます。 */
	return old_LCDDrawObject(obj);
}

