/*
 *   tiff16/256色 -> tiff32k色  変換プログラム 'ぞうさん'
 *                  (C)1993,1994  By N.Takahashi
 *
 *      ver0.61 1994/02/06   パレット無しのTiffデータに対応
 *      ver0.6  1994/02/05   新Tiff関数へ変更(圧縮形式に対応)
 *      ver0.5  1994/01/13   再度､変換ル−チンの高速化
 *      ver0.4  1994/01/05   ワイルドカード対応､バグ修正etc.
 *      ver0.3  1994/01/04   変換ル−チンの高速化
 *      ver0.2  1994/01/04   ファイル分割(zousan.c tiff.c graph.c)
 *      ver0.1  1993/11/11   初版
 */

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <ctype.h>
#include <dos.h>
#include <fcntl.h>
#include <EGB.h>

#include "tiff2.h"		/* 拡張ライブラリIを買わないといけないかな(^^;) */
#include "graph.h"


/* Compile 方法 */
/*   hcc zousan tiff2 graph -stack 200000   */


/* Prototype function */
void sum_ydata( int *ydt, int *sum, int *cnt );
void ren_file( void );
int exp_wild( char *path, char *file, char *dpath, char *dfile );
int isoldfile( char *fname );
void split_path( char *path, char *file, char *wd );
void sum_gdata256( int x, int y, int *cg, int *cr, int *cb );
void sum_gdata16( int x, int y, int *cg, int *cr, int *cb );
void title( void );
void usage( void );
int zousan( void );
void make_color( int *b, int *r, int *g, int b1, int r1, int g1, int hi );
int convert32k( void );
void adj_palette( char *pal, int col );


/* マクロ定義 */
#define	CVCR(b,r,g)	((-(b)*816+(r)*5020-(g)*4203)/10000)
#define CVCB(b,r,g)	(((b)*5020-(r)*1812-(g)*3214)/10000)
#define CVY(b,r,g)	(((b)*1446+(r)*2955+(g)*5899)/10000)
#define CVY2(b,r,g)	(((b)*1446+(r)*2955+(g)*5899))

#define CVB(cr,cb,y) ((y)*9719+(cr)*487+(cb)*17201)
#define CVR(cr,cb,y) ((y)*9707+(cr)*14021-(cb)*516)
#define CVG(cr,cb,y) ((y)*9707-(cr)*7142-(cb)*3957)

#define	ATTR		(_A_NORMAL|_A_RDONLY|_A_HIDDEN)


/* Global variable */

PALETTE16 *Pal16;
PALETTE256 *Pal256;

char *Gdata,*Gpal;	/* 画像データ&パレット */
int SizX;			/* 横サイズ */
int SizY;			/* 縦サイズ */
int Gcol=0;			/* 色数     */
char *Ddata;		/* 変換した画像データ */

int Blur=0;			/*  Blur = 0 - 100 (Blur)          */
int Tone=0;			/*  Tone = 0 - 100 (Tone of color) */

int BlurF=0,ToneF=0,DispF=0;		/* オプション･フラグ */
int CompF=0;

int DirX[4]={0,1,0,-1};
int DirY[4]={-1,0,1,0};

void (*Sum_gdata)( int x, int y, int *cg, int *cr, int *cb );

char *Srcfile=NULL;
char *Dstfile=NULL;

#define PATH_MAX	128
#define FILE_MAX	16
char SPath[PATH_MAX];		/* ﾜｲﾙﾄﾞｶｰﾄﾞ展開に使用 */
char SFile[FILE_MAX];
char DPath[PATH_MAX];
char DFile[FILE_MAX];

int WildF=0,DDirF=0;		/* ﾜｲﾙﾄﾞｶ-ﾄﾞを使用できるようにしたら､
								結構､制御が複雑になってしまった｡ */

char Nowfile[128];			/* 現在､処理中のファイル名 */


/* 使用法 */
void usage( void ) {
	printf( "usage: run386 zousan.exp [option] srcTiff16/256 [dstTiff]\n" );
	printf( "        -b<val>  (Blur)          ,val = 0 - 100\n" );
	printf( "        -t<val>  (Tone of color) ,val = 0 - 100\n" );
	printf( "        -d       (Display only)\n" );
	printf( "        -c       (Compress Tiff file)\n\n" );
	printf( "       default:  16color = -b0 , -t20\n" );
	printf( "                256color = -b0 , -t2\n" );
	exit( 255 );
}


/* タイトル */
void title( void ) {
	printf( "Tiff16/256色 -> Tiff32k色  変換プログラム 'ぞうさん'\n" );
	printf( "                        (C)1993,1994  By N.Takahashi\n\n" );
}


/* メイン */
void main( int argc, char *argv[] ) {
int i;

	title();
	
	if( argc==1 )
		usage();
	for( i=1; i<argc; i++ ) {					/* オプション展開 */
		if( argv[i][0]=='-' || argv[i][0]=='/' )
			switch( argv[i][1] ) {				/* 手抜きしてる(^^;) */
				case 'b':
				case 'B':
					Blur=atoi( argv[i]+2 );
					BlurF=1;
					break;
				case 't':
				case 'T':
					Tone=atoi( argv[i]+2 );
					ToneF=1;
					break;
				case 'd':
				case 'D':
					DispF=1;
					break;
				case 'c':
				case 'C':
					CompF=1;
					break;
				default:
					usage();
					break;
			}
		else {
			if( Srcfile==NULL )
				Srcfile=argv[i];
			else
				Dstfile=argv[i];
		}
	}
	if( Srcfile==NULL )
		usage();

	isoldfile( NULL );		/* 基準時刻のセット */
							/* 二度､変換しないようにするための準備 */
	
	strcpy( SPath, Srcfile );
	split_path( SPath, SFile, "*.TIF" );
	if( strchr( SFile, '*' )!=NULL || strchr( SFile, '?' )!=NULL ) {
		WildF=1;
	}
	
	if( Dstfile==NULL ) {		/* 何も指定されていないとき */
		strcpy( DPath, SPath );
		*DFile='\0';
	}
	else {						/* ファイルが指定されているとき */
		strcpy( DPath, Dstfile );
		split_path( DPath, DFile, "" );
		if( WildF ) {
			strcat( DPath, DFile );
			strcat( DPath, "\\" );
			*DFile='\0';
		}
		if( *DFile=='\0' )			/* ディレクトリのみのとき */
			DDirF=1;
	}
	
	exp_wild( SPath, SFile, DPath, DFile );
}


/* ワイルドカ−ド展開 */
int exp_wild( char *path, char *file, char *dpath, char *dfile ) {
struct find_t fb;
int spl,dpl;
char *c;
	
	spl=strlen( path );
	dpl=strlen( dpath );
	strcat( path, file );
	if( _dos_findfirst( path, ATTR, &fb )==0 ) {
		do {
			path[spl]='\0';
			dpath[dpl]='\0';
			strcat( path, fb.name );
			
			if( isoldfile( path ) ) {
						/* 二度､変換しないようにするため */
			
				sprintf( Nowfile, "%-12s : ", fb.name );
				Srcfile=path;
				Dstfile=dpath;
				if( *dfile=='\0' ) {	/* ファイルが指定されていないとき */
					if( DDirF ) {		/* ディレクトリのみのとき */
						strcat( dpath, fb.name );
						zousan();
					}
					else {				/* 何も指定されていないとき */
						strcat( dpath, fb.name );
						if( ( c=strrchr( dpath, '.' ) )!=NULL ) {
							*c='\0';
						}
						strcat( dpath, ".$$$" );
						if( zousan()==0 )
							ren_file();
					}
				}
				else {					/* ファイルが指定されているとき */
					strcat( dpath, dfile );
					zousan();
				}
			}
		} while( _dos_findnext( &fb )==0 );
		return( 0 );
	}
	else {
		printf( "%-12s : ファイルが見つかりません｡\n", _strupr(file) );
	}
	return( 1 );
}


/* タイムスタンプとシステムタイムとの比較 */
/* 使用前に基準時刻をセットしておくこと => isoldfile( NULL ) */
int isoldfile( char *fname ) {
static unsigned int sdate=0, stime=0;
struct dosdate_t sdt;
struct dostime_t stm;
unsigned int fdt,ftm;
int hd;
	
	if( fname==NULL ) {
		_dos_getdate( &sdt );
		sdate=((sdt.year-1980)<<9)+(sdt.month<<5)+sdt.day;
		_dos_gettime( &stm );
		stime=(stm.hour<<11)+(stm.minute<<5)+(stm.second>>1);
	}
	else {
		if( _dos_open( fname, _O_RDONLY, &hd )!=0 )
			return( -1 ); 
		_dos_getftime( hd, &fdt, &ftm );
		_dos_close( hd );
		if( fdt<sdate || (fdt==sdate && ftm<stime) )
			return( 1 );
	}
	return( 0 );
}


/* ファイル名の交換 */
void ren_file( void ) {
char path[128],*c;
									/* dst.$$$ を dst.TIF へ */
									/* src.TIF を src.$IF へ */
	strcpy( path, Srcfile );
	if( ( c=strrchr( path, '.' ) )!=NULL ) {
		*c='\0';
	}
	strcat( path, ".$IF" );
	rename( Srcfile, path );

	strcpy( path, Dstfile );
	if( ( c=strrchr( path, '.' ) )!=NULL ) {
		*c='\0';
	}
	strcat( path, ".TIF" );
	rename( Dstfile, path );
}


/* ＰＡＴＨ名とファイル名取り出し */
void split_path( char *path, char *file, char *wd ) {
char *c,*c2;

	if( ( c=strrchr( path, ':' ) )==NULL ) {
		c=path;
	}
	if( ( c2=strrchr( c, '\\' ) )==NULL ) {
		c2=c;
	}
	if( *c2==':' || *c2=='\\' ) {
		if( *(c2+1)!='\0' ) {
			strcpy( file, c2+1 ); 
			*(c2+1)='\0';
		}
		else
			strcpy( file, wd ); 
	}
	else {
		strcpy( file , path );
		strcpy( path , "" );
	}
									/* '.' , '..' 対策 */
	if( strcmp( file, "." )==0 || strcmp( file, ".." )==0 ) {
		strcat( path, file );
		strcat( path, "\\" );
		strcpy( file, wd ); 
	}
}


/* ぞうさんメイン */
int zousan( void ) {
Tiff *tp;
int rt;

	tp=Tiff_new();
												/* Tiff形式ロード */
	rt=Tiff_load( Srcfile, tp );
	if( rt!=Tiff__OK ) {
		printf( "%s%s\n", Nowfile, Tiff_err[rt] );
		return( 1 );
	}
	Tiff_decompress( tp );			/* 圧縮されている場合､展開する */
	Tiff_make_palette( tp );		/* パレットがない場合に作成する */
	
	Tiff_getgraph( &SizX, &SizY, &Gcol, &Gdata, &Gpal, tp );
											/* 色数による設定 */
	switch( Gcol ) {
		case 256:
			if( BlurF==0 )			/* デフォルト値 */
				Blur=0;
			if( ToneF==0 )
				Tone=2;
			Pal256=(PALETTE256 *)Gpal;
			Sum_gdata=sum_gdata256;
			break;
		case 16:
			if( BlurF==0 )
				Blur=0;
			if( ToneF==0 )
				Tone=20;
			Pal16=(PALETTE16 *)Gpal;
			Sum_gdata=sum_gdata16;
			break;
		default:
			printf( "%s扱えないデータ形式です｡\n", Nowfile );
			return( 1 );
	}

	if( Blur<0 )
		Blur=0;
	if( Blur>100 )
		Blur=100;
	if( Tone<0 )
		Tone=0;
	if( Tone>100 )
		Tone=100;
	
	adj_palette( Gpal, Gcol );		/* パレットデータの調整 */
									/* Tiff_load直後のパレットデータは､
									   そのままでは使いにくい           */

	if( (Ddata=(char *)malloc( SizX*SizY/2 ))==NULL ) {
		printf( "メモリが足りません｡\n" );
		Tiff_delete( tp );
		return( 1 );
	}
										/* 32K色変換 */
	if( convert32k() ) {
		printf( "強制中断します｡%*c\n", 60, 0x20 );
		G_dosterm();
		exit( 1 );
	}
										/* ディスプレイ表示 */
	if( DispF ) {
		G_screen( 10, 0 );				/* 320*240 3万色 */
		G_screen( 10, 1 );
		G_enlarge( 2, 2, 0, 0, 320, 240 );
		G_active( 1, 3, 1 );
		G_put( 0,0, SizX/2-1,SizY/2-1, Ddata );
		_getch();						/* 何かキー入力が有るまで待つ */
		G_dosterm();					/* DOS画面に戻す */
		Tiff_delete( tp );
		free( Ddata );
		return( 1 );
	}
										/* Tiff形式セーブ */
	if( Dstfile!=NULL && !DispF ) {
		Tiff_delete( tp );
		tp=Tiff_new();
		Tiff_setgraph( tp, SizX/2, SizY/2, 32768, Ddata, NULL );
		if( CompF )
			Tiff_compress( tp );		/* -c 指定時にデータを圧縮する */
		rt=Tiff_save( Dstfile, tp );
		if( rt!=0 ) {
			printf( "%s%s\n", Nowfile, Tiff_err[rt] );
			Tiff_delete( tp );
			return( 1 );
		}
	}
	Tiff_delete( tp );
	return( 0 );
}


/* ３万色変換 */
int convert32k( void ) {
int x,y,a,b,hi,bc,bcc;
int cc,cg,cr,cb;
int yc,yg,yr,yb;
int g0,r0,b0;
int xx,yy,xd,yd;
int ydt[16];

	for( y=0; y<SizY-1; y+=2 ) {
		if( (y&31)==0 ) {
			printf( "%s変換中です｡(%d％)\r", Nowfile, y*100/SizY );
		}
		if( _kbhit() ) {
			if( _getch()==0x1b )
				return( 1 );
		}
		yd=(y/2*SizX)/2;
		for( x=0; x<SizX-1; x+=2 ) {
			xd=x/2;
			cg=cr=cb=cc=yg=yr=yb=yc=0;		/* 色と輝度の積算 4×4 */
			for( a=-1; a<=2; a++ ) {
				xx=x+a;
				for( b=-1; b<=2; b++ ) {
					yy=y+b;
					if( xx>=0 && xx<SizX && yy>=0 && yy<SizY ) {
						Sum_gdata( xx, yy, &g0, &r0, &b0 );
						ydt[a+1+(b+1)*4]=CVY2( b0, r0, g0 );
						cg+=g0;
						cr+=r0;
						cb+=b0;
						cc++;
						if( a>=0 && a<=1 && b>=0 && b<=1 ) {
							yg+=g0;
							yr+=r0;
							yb+=b0;
							yc++;
						}
					}
					else
						ydt[a+1+(b+1)*4]=-1;
				}
			}
			sum_ydata( ydt, &bc, &bcc );
			
			hi=( bcc>0 )?(bc/bcc)/10000 : 0;
			cr/=cc;
			cg/=cc;
			cb/=cc;
			yr/=yc;
			yg/=yc;
			yb/=yc;

			make_color( &cb, &cr, &cg, yb, yr, yg, hi );

			/* 色データ書き込み */
			*((unsigned short *)Ddata+xd+yd)=G_color32k(cg, cr, cb);
		}
	}
	printf( "%s変換終了しました｡(100％)\n", Nowfile );
	
	return( 0 );
}


/* 色作成 */
void make_color( int *bb, int *rr, int *gg, int bb1, int rr1, int gg1, int hi ) {
int y,cr,cb,y0,b;

		cr=CVCR( *bb, *rr, *gg );				/* YC分離 */
		cb=CVCB( *bb, *rr, *gg );
		y=CVY( bb1, rr1, gg1 );
		y0=CVY( *bb, *rr, *gg );

		b=abs(*rr-rr1)+abs(*bb-bb1)+abs(*gg-gg1);
		b=1000/(b+1);
		b+=Blur;
		if( b>100 )
			b=100;
		y+=(y0-y)*b/100;		/* にじみ具合の調整 */
		
		y+=hi*Tone/100;			/* 色調の調整 */

		*bb=CVB( cr, cb, y );					/* RGB色合成 */
		*bb=( *bb>=0 )?(*bb+5000)/10000 : 0;
		*rr=CVR( cr, cb, y );
		*rr=( *rr>=0 )?(*rr+5000)/10000 : 0;
		*gg=CVG( cr, cb, y );
		*gg=( *gg>=0 )?(*gg+5000)/10000 : 0;

		if( *bb>255 )
			*bb=255;
		if( *rr>255 )
			*rr=255;
		if( *gg>255 )
			*gg=255;

}


/* パレット調整 */
void adj_palette( char *pal, int col ) {
int i;
								/* Tiffのパレットデータの調整 */
	for( i=0; i<col*3; i++ ) {
		*((unsigned short *)pal+i)=*((unsigned short *)pal+i)>>8;
	}
}


/* 16色データ取得 */
void sum_gdata16( int x, int y, int *cg, int *cr, int *cb ) {
int cc;
	cc=Gdata[(x+y*SizX)/2];
	if( x&1 )
		cc=cc>>4;
	else
		cc=cc&15;
	*cb=Pal16->blue[cc];	/* パレットによる色変換 */
	*cr=Pal16->red[cc];
	*cg=Pal16->green[cc];
}


/* 256色データ取得 */
void sum_gdata256( int x, int y, int *cg, int *cr, int *cb ) {
int cc;
	cc=Gdata[x+y*SizX];
	*cb=Pal256->blue[cc];	/* パレットによる色変換 */
	*cr=Pal256->red[cc];
	*cg=Pal256->green[cc];
}


/* 輝度差データ取得 */
void sum_ydata( int *ydt, int *sum, int *cnt ) {
int i,a,b,k,yc;

	*sum=*cnt=0;
	for( a=1; a<=2; a++ ) {
		for( b=1; b<=2; b++ ) {
			yc=ydt[a+b*4];						/* 対象点の輝度 */
			for( i=0; i<4; i++ ) {
				k=ydt[a+DirX[i]+(b+DirY[i])*4];	/* 隣接点の輝度 */
				if( k>=0 ) {
					k=yc-k;
					if( k>=0 ) {	/* 対象点の方が輝度が高い時 */
						*sum+=k;
						(*cnt)++;
					}
				}
			}
		}
	}
}
