// YUV→RGB；不完全ＭＣＵ対応＋倍率付き
// ＧＲＡＭ構成が機種依存。
// X68は1dot 1wordである。

// #define NOMCUで不完全MCU＋倍率付き処理版が出来る

#include "_jpeglib.h"
#include "jpeg.h"

#ifndef NOMCU
// 完全MCU処理版
#define	yuv2rgb0	YUV2RGB0
#define	ww		X68GWX
#define	IfCheckArea(w,d)
#else
// 不完全MCU＋倍率処理版
#define	IfCheckArea(w,d)	if ((w)<(d))
#endif


#ifdef NOMCU
void YUV2RGB1x(unshort *gram,short *y,short *u,short *v,int w,int dx,int dy)
#else
void YUV2RGB1( unshort *gram,short *y,short *u,short *v)
#define	dy	(JWY)
#endif
// JWU*JWVドット単位としてYUV→RGB変換する
// 1:1:1用
{
short wx,wy;
unshort *gram0;
#ifdef NOMCU
void (*yuv2rgb0)(unshort *,short ,short ,short );
int ww;
	switch (w) {
	case -1: // 縦倍
		yuv2rgb0=YUV2RGB01;
		ww=X68GWX*2;
		goto BAI;
	case 1: // 等倍
		yuv2rgb0=YUV2RGB0;
		ww=X68GWX;
	    BAI:;
#endif
		for (wy=0;wy<dy;wy++) {
			gram0=gram;
			for (wx=0;wx<JWX;wx++) {
				IfCheckArea(wx,dx) {
					yuv2rgb0(gram++,*y,*u,*v);
				}
				y++;
				u++;
				v++;
			}
			gram=gram0+ww;
		} // for wy
#ifdef NOMCU
		break;
	case 2: // 1/2
	case 4: // 1/4
	case 8: // 1/8
		for (wy=0;wy<dy;wy+=w) {
			gram0=gram;
			for (wx=0;wx<JWX;wx+=w) {
				if (wx<dx) {
					YUV2RGB0(gram++,*y,*u,*v);
				} // wxの越える時は描画をしない
				// 描画しなくてもデータポインタは移動させる必要がある
				y+=w;
				u+=w;
				v+=w;
			}
			// wy方向の越える部分はデータを完全に捨てれば良い
			gram=gram0+X68GWX;
			{int j;
			for (j=1;j<w;j++) {
				y+=JWX; // 次のw-1ライン分のデータを飛ばす
				u+=JWX;
				v+=JWX;
			}
			}
		} // for wy
		break;
	} // switch (w)
#endif
}

#ifdef NOMCU
void YUV2RGB4x(unshort *gram,short *y,short *u,short *v,int w,int dx,int dy)
#else
void YUV2RGB4( unshort *gram,short *y,short *u,short *v)
#define	dy	(JWY*2)
#endif
// JWU*JWVドット単位をZ順でYUV→RGB変換する
// Yは全ドット、U/Vは縦横2ドット分展開する
// 4:1:1用
{
short wx,wy,*yy;
unshort *gram0;
#ifdef NOMCU
void (*yuv2rgb0)(unshort *,short ,short ,short );
int ww;
#endif
	yy=&y[JWX*JWY]; // 隣のブロック分先頭
#ifdef NOMCU
	switch (w) {
	case -1: // 縦倍
		yuv2rgb0=YUV2RGB01;
		ww=X68GWX*2;
		w=1; // 後は等倍と同じ
		goto BAI4;
	case 1: // 等倍
		yuv2rgb0=YUV2RGB0;
		ww=X68GWX;
	    BAI4:;
#endif
		for (wy=0;wy<dy;wy+=2) { // ２ライン同時処理
		    gram0=gram;
		    // 横２ブロックを一気に処理する（u,vのため）
		    for (wx=0;wx<JWX;wx+=2) { // 左ブロック
			// ２＊２一気展開
			IfCheckArea(wx,dx) {
				yuv2rgb0(gram,*y,*u,*v);
	 			IfCheckArea(wx+1,dx) {
	 				yuv2rgb0(gram+1,*(y+1),*u,*v);
	 			}
			}
			IfCheckArea(wy+1,dy) {
				IfCheckArea(wx,dx) {
					yuv2rgb0(gram+ww,*(y+JWX),*u,*v);
		 			IfCheckArea(wx+1,dx) {
		 				yuv2rgb0(gram+ww+1,*(y+JWX+1),*u,*v);
		 			}
				}
			}
			gram+=2; // ２ドット先へ
			y+=2;
			u++;
			v++;
		    } //  for wx(左)
		    y+=JWX; // 下１行分も既に展開済みなので飛ばす
		    for (;wx<JWX*2;wx+=2) { // 右ブロック
			// ２＊２一気展開
			IfCheckArea(wx,dx) {
				yuv2rgb0(gram,*yy,*u,*v);
	 			IfCheckArea(wx+1,dx) {
	 				yuv2rgb0(gram+1,*(yy+1),*u,*v);
	 			}
			}
			IfCheckArea(wy+1,dy) {
				IfCheckArea(wx,dx) {
					yuv2rgb0(gram+ww,*(yy+JWX),*u,*v);
	 				IfCheckArea(wx+1,dx) {
	 					yuv2rgb0(gram+ww+1,*(yy+JWX+1),*u,*v);
	 				}
				}
			}
			gram+=2;
			yy+=2;
			u++;
			v++;
		    } //  for wx(右)
		    yy+=JWX; // 下１行分も既に展開済みなので飛ばす
		    // ２ライン終了
		    gram=gram0+ww*2;
		    if (wy==JWY-2) { // 次：上2ブロック→下2ブロック（下8ライン分）
		    	y=yy;
			yy=&y[JWX*JWY]; // 隣のブロック分先頭
		    }
		} // for wy
#ifdef NOMCU
	    	break;
	case 2: // 1/2
	case 4: // 1/4
	case 8: // 1/8
		for (wy=0;wy<dy;wy+=w) {
		    // 横２ブロックを一気に処理する（u,vのため）
		    gram0=gram;
		    for (wx=0;wx<JWX;wx+=w) { // 左ブロック
			if (wx<dx) YUV2RGB0(gram++,*y,*u,*v);
			y+=w;
			u+=w/2;
			v+=w/2;
		    } //  for wx(左)
		    for (;wx<JWX*2;wx+=2) { // 右ブロック
			// ２行分一気展開
			if (wx<dx) YUV2RGB0(gram++,*yy,*u,*v);
			yy+=w;
			u+=w/2;
			v+=w/2;
		    } //  for wx(右)
		    // ２ライン終了
		    {int j;
		    for (j=1;j<w;j++) { // 次のw-1ライン分のデータを飛ばす
			y+=JWX;
			yy+=JWX;
		    }
		    for (j=1;j<w/2;j++) { // 次のw-1ライン分のデータを飛ばす
			u+=JWX/2;
			v+=JWX/2;
		    }
		    }
		    gram=gram0+X68GWX;
		    if (wy==JWY-w) { // 上2ブロック→下2ブロック（下8ライン分）
		    	y=yy;
			yy=&y[JWX*JWY]; // 隣のブロック分先頭
		    }
		} // for wy
		break;
	} // switch (w)
#endif
}

#ifdef NOMCU
void YUV2RGB2_x(unshort *gram,short *y,short *u,short *v,int w,int dx,int dy)
#else
void YUV2RGB2_( unshort *gram,short *y,short *u,short *v)
#define	dy	(JWY)
#endif
// JWU*JWVドット単位としてYUV→RGB変換する
// Yは全ドット、U/Vは横2ドット縦1ドット分展開する
// 2-:1:1用
{
short wx,wy,*yy;
unshort *gram0;
#ifdef NOMCU
void (*yuv2rgb0)(unshort *,short ,short ,short );
int ww;
#endif
	yy=&y[JWX*JWY]; // 隣のブロック分先頭
#ifdef NOMCU
	switch (w) {
	case -1: // 縦倍
		yuv2rgb0=YUV2RGB01;
		ww=X68GWX*2;
		goto BAI2_;
	case 1: // 等倍
		yuv2rgb0=YUV2RGB0;
		ww=X68GWX;
	    BAI2_:;
#endif
		for (wy=0;wy<dy;wy++) {
			gram0=gram;
		    	// 横２ブロックを一気に処理する（u,vのため）
			for (wx=0;wx<JWX;wx+=2) { // 左ブロック
				IfCheckArea(wx,dx) {
					yuv2rgb0(gram++,*y,*u,*v);
					IfCheckArea(wx+1,dx) {
						yuv2rgb0(gram++,*(y+1),*u,*v);
					}
				}
				y+=2;
				u++;
				v++;
			}
			for (;wx<JWX*2;wx+=2) { // 右ブロック
				IfCheckArea(wx,dx) {
					yuv2rgb0(gram++,*yy,*u,*v);
					IfCheckArea(wx+1,dx) {
						yuv2rgb0(gram++,*(yy+1),*u,*v);
					}
				}
				yy+=2;
				u++;
				v++;
			}
			gram=gram0+ww;
		}
#ifdef NOMCU
		break;
	case 2: // 1/2
	case 4: // 1/4
	case 8: // 1/8
		for (wy=0;wy<dy;wy+=w) {
			gram0=gram;
		    	// 横２ブロックを一気に処理する（u,vのため）
			for (wx=0;wx<JWX;wx+=w) { // 左ブロック
				if (wx<dx) {
					YUV2RGB0(gram++,*y,*u,*v);
				}
				y+=w;
				u+=w/2;
				v+=w/2;
			}
			for (;wx<JWX*2;wx+=w) { // 右ブロック
				if (wx<dx) {
					YUV2RGB0(gram++,*yy,*u,*v);
				}
				yy+=w;
				u+=w/2;
				v+=w/2;
			}
			gram=gram0+X68GWX;
			{int j;
			for (j=1;j<w;j++) {
				y +=JWX; // 次のw-1ライン分のデータを飛ばす
				yy+=JWX; // 次のw-1ライン分のデータを飛ばす
				u+=JWX/2;
				v+=JWX/2;
			}
			}
		} // for wy
		break;
	} // switch (w)
#endif
}

#ifdef NOMCU
void YUV2RGB2Ix(unshort *gram,short *y,short *u,short *v,int w,int dx,int dy)
#else
void YUV2RGB2I( unshort *gram,short *y,short *u,short *v)
#define	dy	(JWY*2)
#endif
// JWU*JWVドット単位をI順でYUV→RGB変換する
// Yは全ドット、U/Vは横1ドット縦2ドット分展開する
// 2I:1:1用
{
short wx,wy,*yy;
unshort *gram0;
#ifdef NOMCU
void (*yuv2rgb0)(unshort *,short ,short ,short );
int ww;
	switch (w) {
	case -1: // 縦倍
		yuv2rgb0=YUV2RGB01;
		ww=X68GWX*2;
		goto BAI2i;
	case 1: // 等倍
		yuv2rgb0=YUV2RGB0;
		ww=X68GWX;
	    BAI2i:;
#endif
		for (wy=0;wy<dy;wy+=2) { // ２ライン同時処理
			yy=y+JWX;
			gram0=gram;
			for (wx=0;wx<JWX;wx++) {
				IfCheckArea(wx,dx) {
					yuv2rgb0(gram,*y,*u,*v); // 1ライン目
					// １ライン下はY独立、U/V共通
					IfCheckArea(wy+1,dy) {
						yuv2rgb0(gram+ww,*yy,*u,*v);
					}
					gram++;
				}
				y++;
				yy++;
				u++;
				v++;
			}
			y=yy;
			gram=gram0+ww*2;
		}
#ifdef NOMCU
		break;
	case 2: // 1/2
	case 4: // 1/4
	case 8: // 1/8
		for (wy=0;wy<dy;wy+=w) {
			gram0=gram;
			for (wx=0;wx<JWX;wx+=w) {
				if (wx<dx) {
					YUV2RGB0(gram++,*y,*u,*v);
				}
				y+=w;
				u+=w;
				v+=w;
			}
			gram=gram0+X68GWX;
			// 次のw-1ライン分のデータを飛ばす
			{int j;
			for (j=1;j<w;j++) {
				y+=JWX;
			}
			for (j=1;j<w/2;j++) {
				u+=JWX;
				v+=JWX;
			}
			}
		} // for wy
		break;
	} // switch (w)
#endif
}
