//------------------------------------------------------------------------
// これはX68上で圧縮展開を行うサンプルプログラム

#include "defines.h"
#include <iocslib.h>
#include <stdio.h>
#include <stdlib.h>	// exit,system
#include <conio.h>	// getch
#include <string.h>
#include <time.h>
#include "_jpeglib.h"
#include "jpeg.h"
#include "aparam.h"
#include "astdio.h"
#include "astring.h"
#include "ascreen.h"

// 画面設定値
#define	SCRN	12	// 512*512,512*512/1page,31KHz,Graphic on

// 圧縮エリアサイズ（デフォルト）
#define	defGSX	0
#define	defGSY	0
#define	defGWX	(X68GWX/2)
#define	defGWY	(X68GWY/2)

#ifdef ONMEM
unchar jbuff[X68GWX*X68GWY*3],*jb;	// ONMEM時の中間ワーク：Y/U/V各８ビット分
#else
#define	JPGFILE1	"JPG.DAT"	// ONMEMでないときの中間ファイル名
#define	JPGFILE2	"JPGf.JPG"	// /F時のデフォルトファイル名
#define	MAXCOM		128		// コメント最大長（＜16bit）
#endif

//------------------------------------------------------------------------

// ワーク
short y[4][JWY*JWX],u[JWY*JWX],v[JWY*JWX];
short suv[JWV*JWU];

// RemakeHuff()/JpegReadHeader()で参照するワーク
// extern宣言はjpeg.h内で行われている
iHUFFMAN iHuffDC_Y[HUFF_DC_Y_SIZE],iHuffDC_UV[HUFF_DC_UV_SIZE];
iHUFFMAN iHuffAC_Y[HUFF_AC_Y_SIZE],iHuffAC_UV[HUFF_AC_UV_SIZE];
unchar cntDC_Y[CNT_DC_Y_SIZE],cntDC_UV[CNT_DC_UV_SIZE];
unchar cntAC_Y[CNT_AC_Y_SIZE],cntAC_UV[CNT_AC_UV_SIZE];

//------------------------------------------------------------------------

static int ChkExt(unchar *argv,unchar ec)
// 手抜き拡張子判定（拡張子の先頭１文字のみ）
{
int len;
	len=strlen(argv);
	return (argv[len-4]=='.' && toupper(argv[len-3])==ec);
}

//------------------------------------------------------------------------

int LoadQtable(unchar *fname,unchar q[JWU*JWV],int qrate)
// 量子化テーブルを読み込んで圧縮率を設定する
{
FILE *fp;
int ret,i;
	fp=fopen(fname,"rb");
	if (fp==NULL) { // ファイルがない
		printf("ファイルが見つかりません：%s\n",fname);
		return(1);
	}
	ret=fread(q,sizeof(unchar),JWU*JWV,fp);
	fclose(fp);
	if (ret<JWU*JWV) { // 足りない
		printf("量子化テーブルの内容が足りません：%s\n",fname);
		return(2);
	}
	// 内容を調べる
	for (i=0;i<JWU*JWV;i++) {
		if (q[i]==0) { // 内容がおかしい
			printf("量子化テーブルの内容が異常です：%s\n",fname);
			return(3);
		}
	}
	// 圧縮率設定
	SetQtable(qrate,q,q);
	return(0);
}

//------------------------------------------------------------------------

void main(int argc,unchar *argv[])
{
int ret,gsx,gsy,gwx,gwy,glx,gly,ewx,ewy;
int mcux,w;
short rstcnt;
long mode;	// 動作モード
int fcls;	// 展開画像表示前に画面をクリアする
int qrate;	// 圧縮率指定
#ifdef ONMEM	// オンメモリ版
#define	jdata	&jb
#else		// ファイル版
int floader,fcomm,fdisp,fcenter;
FILE *fp;
unchar comm[MAXCOM+1];	// +1 for EOS
unchar fname[MAXPATH];
#define	jdata	fp
int ffile;	// JPEGファイル出力モード
#endif
unchar y_dc[40],uv_dc[40]; // ファイル用ハフマンテーブルDC
unchar y_ac[256],uv_ac[256]; // ファイル用ハフマンテーブルAC
int y_dc_size,uv_dc_size,y_ac_size,uv_ac_size; // ハフマンテーブルサイズ
unchar qY[JWU*JWV],qU[JWU*JWV],*qV,qVV[JWU*JWV];
time_t ts;
	clear_param();
	
	#ifndef ONMEM	// オンファイル版
	floader=fcomm=0;
	if (check_param2("LF",argv,argc,0)) { // JPEGファイルを表示する（ヘッダー情報表示無し）
		floader=1;
		fdisp=0;
	}
	if (check_param2("LFD",argv,argc,0)) { // JPEGファイルを表示する（ヘッダー情報表示有り）
		floader=1;
		fdisp=fcomm=1; // fdisp時は必ずfcommとする
	}
	// 再生画像のサイズがX68GWX*X68GWY未満の時、画面中央に表示する
	fcenter=check_param2("CEN",argv,argc,0);
	#endif
	
	//　以下面倒なので、/LF時も圧縮オプションの指定は出来るが無効となる
	
	#ifndef ONMEM	// オンファイル版
	// ファイル出力モード指定
	ffile=0;
	ret=check_param2("F",argv,argc,BIT3|BIT4);
	if (ret) { // /Fあり
		znstrcpy3(fname,argv[ret],MAXPATH);
		if (fname[0]==0) strcpy(fname,JPGFILE2); // /Fのみのとき
		ffile=ret;
	} else { // 指定無し＝ファイル出力無し
		strcpy(fname,JPGFILE1);
	}
	#endif
	
	// 範囲指定
	{
	int num[4];
	#define CheckArea(num,max) {		\
		if (num<   0) num=0; 		\
		if (num>=max) num=max-1;	\
	}
	#define CheckSize(num,min,max) {	\
		if (num<min ) num=min; 		\
		if (num>=max) num=max;		\
	}
	#define swap(a,b) {	\
		int c;		\
		c=a; a=b; b=c;	\
	}
	
	// 圧縮範囲指定１：座標指定
	// /Sgsx,gsy,gex,gey  : 全て省略可能
	num[0]=defGSX;
	num[1]=defGSY;
	num[2]=defGSX+defGWX-1;
	num[3]=defGSY+defGWY-1;
	if (check_param3n("S",argv,argc,num,0,4,0,NULL)) {
		CheckArea(num[0],X68GWX);
		CheckArea(num[1],X68GWY);
		CheckArea(num[2],X68GWX);
		CheckArea(num[3],X68GWY);
		if (num[2]<num[0]) swap(num[0],num[2]); // gex>gsxのこと
		if (num[3]<num[1]) swap(num[1],num[3]); // gwy>gsxのこと
	}
	gsx=num[0];
	gsy=num[1];
	gwx=num[2]-gsx+1;
	gwy=num[3]-gsy+1;
	
	// 圧縮範囲指定２：範囲指定
	// /SWgsx,gsy,gwx,gwy : 全て省略可能
	// num[0],[1]は/Sの設定のまま
	num[2]=gwx;
	num[3]=gwy;
	if (check_param3n("SW",argv,argc,num,0,4,0,NULL)) {
		CheckArea(num[0],X68GWX);
		CheckArea(num[1],X68GWY);
		CheckSize(num[2],1,X68GWX);
		CheckSize(num[3],1,X68GWY);
	}
	gsx=num[0];
	gsy=num[1];
	gwx=num[2];
	gwy=num[3];
	
	#ifndef ONMEM	// オンファイル版
	// 読み込み開始座標、展開サイズ指定
	// ffile時は無効だが、指定だけは出来るようにしておく
	// /Lglx,gly,ewx,ewy : 全て省略可能
	num[0]=num[1]=num[2]=num[3]=0;
	if (check_param3n("L",argv,argc,num,0,4,0,NULL)) {
		CheckArea(num[0],X68GWX);
		CheckArea(num[1],X68GWY);
		CheckSize(num[2],0,X68GWX);
		CheckSize(num[3],0,X68GWY);
	}
	glx=num[0];
	gly=num[1];
	ewx=num[2];
	ewy=num[3];
	//if (ewx==0||ewx>gwx) ewx=gwx;
	//if (ewy==0||ewy>gwy) ewy=gwy;
	#endif // ifndef ONMEM
	}
	printf("圧縮領域：(%d,%d)〜(%d,%d)；補正前\n",gsx,gsy,gsx+gwx-1,gsy+gwy-1);
	#ifndef ONMEM	// オンファイル版
	if (!ffile) {
		printf("表示領域：(%d,%d)〜(%d,%d)\n",glx,gly,glx+ewx-1,gly+ewy-1);
	}
	#endif
	
	// 圧縮方法設定
	mode=MD_111; // 1:1:1圧縮展開（デフォルト）
	if (check_param2("W" ,argv,argc,0)) mode=(mode&~MD_BMODE)|MD_BAI;  // 縦倍圧縮
	if (check_param2("W" ,argv,argc,0)) mode=(mode&~MD_BMODE)|MD_BAI;  // 縦倍圧縮
	if (check_param2("12",argv,argc,0)) mode=(mode&~MD_BMODE)|MD_1_2;  // 縦横1/2
	if (check_param2("14",argv,argc,0)) mode=(mode&~MD_BMODE)|MD_1_4;  // 縦横1/4
	if (check_param2("18",argv,argc,0)) mode=(mode&~MD_BMODE)|MD_1_8;  // 縦横1/8
	if (check_param2("BW",argv,argc,0)) mode|=MD_BW;  // 白黒圧縮展開
	
	// 展開方法設定
	if (check_param2("WE" ,argv,argc,0)) mode=(mode&~MD_EMODE)|MD_BAIE; // 縦倍展開
	if (check_param2("12E",argv,argc,0)) mode=(mode&~MD_EMODE)|MD_1_2E; // 縦横1/2
	if (check_param2("14E",argv,argc,0)) mode=(mode&~MD_EMODE)|MD_1_4E; // 縦横1/4
	if (check_param2("18E",argv,argc,0)) mode=(mode&~MD_EMODE)|MD_1_8E; // 縦横1/8
	if (check_param2("BWE",argv,argc,0)) mode|=MD_BWE; // 白黒展開のみ
	
	// サンプリングファクター
	// 本来同時設定は出来ないが、した場合は下のリストの下のものが有効
	if (check_param2("2I",argv,argc,0)) { // 2|:1:1圧縮展開
		mode=(mode&~MD_AMODE)|MD_2I11;
		// X68上のビューワーは全て2I画像をサポートしていないので注意
		// JPEGED/DJ/JPGL全て駄目、GV/Susieは大丈夫。
	}
	if (check_param2("2-",argv,argc,0)
	 || check_param2("2_",argv,argc,0)) { // 2-:1:1圧縮展開
		mode=(mode&~MD_AMODE)|MD_2_11;
	}
	if (check_param2("4" ,argv,argc,0)) { // 4:1:1圧縮展開
		mode=(mode&~MD_AMODE)|MD_411;
	}
	if (check_param2("1" ,argv,argc,0)) { // 1:1:1圧縮展開
		mode=(mode&~MD_AMODE)|MD_111;
	}
	
	#ifndef ONMEM	// オンファイル版のみ
	if (ffile) {
		if (mode&MD_BW) { // 白黒時
			mode=(mode&~MD_AMODE)|MD_111;
			printf("白黒ファイル出力時は強制的に1:1:1にします。\n");
			// それがJPEGの規格なので仕方ないが、自己圧縮展開の時は可能なので許す
		}
	}
	#endif
	
	// リスタートマーカー
	{
	int Irstcnt=0; // chec_param3はintしか読めないため
	if (check_param3("R",argv,argc,&Irstcnt,0)) {
		if (Irstcnt<0) Irstcnt=0;
		// これ以上の補正はPressParamX()で行う
	}
	printf("リスタートマーカー指定（0=なし）:%d\n",Irstcnt);
	rstcnt=(short)Irstcnt;
	}
	#ifndef ONMEM	// オンファイル版のみ
	// コメント
	comm[0]=0;
	ret=check_param2("C",argv,argc,BIT3|BIT4);
	if (ret) {
		znstrcpy3(comm,argv[ret],MAXCOM);
		fcomm=ret;
	} else {
		// 省略時はこのプログラムの名前を入れる
		strcpy(comm,argv[0]);
	}
	#endif
	
	// 量子化テーブル＆圧縮率指定：Ｙ／Ｕ／Ｖ別
	// 0        : 全て1にする＝量子化無し
	// 1/-1     : 元の量子化テーブルのまま
	// 255〜2   : 量子化テーブルにこの値を掛けて処理する
	// -255〜-2 : 量子化テーブルをこの値の絶対値で割って処理する
	#define	QORIGIN	1
	qrate=QORIGIN; // 元の量子化テーブルのまま
	    check_param3("QY" ,argv,argc,&qrate,0);  // Y圧縮率
	printf("Y圧縮率:%d（%d=変更無し）",qrate,QORIGIN);
	ret=check_param2("QYF",argv,argc,BIT3|BIT4); // Y量子化テーブル
	if (ret) {
		unchar qname[MAXPATH];
		znstrcpy3(qname,argv[ret],MAXFILE);
		printf(",Y量子化テーブル:%s\n",qname);
		if (LoadQtable(qname,qY,qrate)) {
			exit(2);
		}
	} else {
		// 省略時はデフォルトテーブルを使う
		SetQtable(qrate,qY,QtableY);
	}
	printf("\n");
	
	qrate=QORIGIN; // 元の量子化テーブルのまま
	    check_param3("QU",argv,argc,&qrate,0);  // U圧縮率
	printf("U(/V)圧縮率:%d（%d=変更無し）",qrate,QORIGIN);
	ret=check_param2("QUF",argv,argc,BIT3|BIT4); // U(/V)量子化テーブル
	if (ret) {
		unchar qname[MAXPATH];
		znstrcpy3(qname,argv[ret],MAXFILE);
		printf(",U(/V)量子化テーブル:%s\n",qname);
		if (LoadQtable(qname,qU,qrate)) {
			exit(2);
		}
	} else {
		// 省略時はデフォルトテーブルを使う
		SetQtable(qrate,qU,QtableUV);
	}
	qV=qU; // U/Vテーブル共通
	printf("\n");
	
	qrate=QORIGIN; // 元の量子化テーブルのまま
	    check_param3("QV",argv,argc,&qrate,0);  // V圧縮率
	printf("V圧縮率:%d（%d=変更無し）",qrate,QORIGIN);
	ret=check_param2("QVF",argv,argc,BIT3|BIT4); // V量子化テーブル
	if (ret) {
		unchar qname[MAXPATH];
		znstrcpy3(qname,argv[ret],MAXFILE);
		printf(",V量子化テーブル:%s\n",qname);
		qV=qVV;
		if (LoadQtable(qname,qV,qrate)) {
			exit(2);
		}
	} // 省略時はU/V共通テーブルを使う
	printf("\n");
	
	// 画面設定
	fcls =check_param2("CLS",argv,argc,0); // 展開画像表示前に画面をクリアする
	#ifndef ONMEM
	if (floader) { // /LF時は常に画面を初期化する
		if (check_param2("512",argv,argc,0)) {
			CRTMOD(SCRN);	// 画面設定
			G_CLR_ON();	// グラフィック画面をクリアして表示
		} else {
			CRTC768x512G(1); // 768*512表示で512*512/65535色モード
		}
	}
	else
	#endif
	{
		if (!check_param2("NC",argv,argc,0)) {
			if (check_param2("768",argv,argc,0)) {
				CRTC768x512G(1); // 768*512表示で512*512/65535色モード
			} else {
				CRTMOD(SCRN);	// 画面設定
				G_CLR_ON();	// グラフィック画面をクリアして表示
			}
		}
	}
	
	/* オプションチェック */
	ret=check_param_error(argv,argc);
	if (ret) {
		printsn("オプションエラーです：",argv[ret]);
		exit(3);
	}
	
	// ファイル名取得
	ret=getfname(argc,argv,1,NULL);
	if (ret!=0) {
		#ifndef ONMEM
		if (floader) { // 表示時のファイル名
			fn_ext0(argv[ret],fname,"JPG"); // 拡張子補足付き
		}
		else
		#endif
		{
	    		unchar line[128];
			if (ChkExt(argv[ret],'P')) { // 手抜きpic判定
				//sprintf(line,"hapic -k %s",argv[ret]);
				sprintf(line,"pic %s",argv[ret]);
			} else {
				sprintf(line,"jpeged -k %s",argv[ret]);
			}
			system(line);
		}
	} else {
		#ifndef ONMEM
		if (floader) { // 表示時のファイル名省略は不可
			printf("JPEGファイルを指定してください\n");
			exit(4);
		}
		// 圧縮時はファイル名の指定がない時は現在の画面を使って実行する
		#endif
	}
	
	#ifndef ONMEM	// オンファイル版
	if (floader) { // ファイル展開
		printf("展開ファイル：%s\n",fname);
		fp=fopen(fname,"rb");
		if (fp==NULL) {
			printf("ファイルが見つかりません:%s\n",fname);
			exit(1);
		}
		if (JpegReadHeader(fp,&gwx,&gwy,&mode,&rstcnt,qY,qU,qV,fdisp,fcomm)) {
			exit(2);
		}
		// ここで量子化テーブルを読み込むから、
		// ローダーモードでは圧縮率の変更や量子化テーブルの変更は無効となる。
		#if 0
		{int i;
		printf("輝度量子化");
		for (i=0;i<JWU*JWY;i++) {
			if ((i%8)==0) printf("\n");
			printf("%3d ",qY[i]);
		}
		printf("\n色差U量子化");
		for (i=0;i<JWU*JWY;i++) {
			if ((i%8)==0) printf("\n");
			printf("%3d ",qU[i]);
		}
		printf("\n色差V量子化");
		for (i=0;i<JWU*JWY;i++) {
			if ((i%8)==0) printf("\n");
			printf("%3d ",qV[i]);
		}
		}
		printf("\n");
		#endif
		
		ewx=gwx;
		ewy=gwy;
		if (fcenter) { // 中央表示
			if (gwx<=X68GWX && gwy<=X68GWY) {
				glx=(X68GWX-gwx)/2;
				gly=(X68GWY-gwy)/2;
			}
		}
		goto LOADER;
	}
	#endif
	
	// 圧縮→展開
	#ifndef ONMEM
	// ファイル作成
	fp=fopen(fname,"w+b");
	if (fp==NULL) {
		printf("ファイルが作成出来ません:%s\n",fname);
		exit(1);
	}
	#endif
	
	// 圧縮用ハフマンテーブルからファイル用ハフマンデータを構築する
	// Huff?C_Y/UVは内部にある固定ハフマンテーブル
	// JpegExpand()のための展開用ハフマンテーブルを作成するのにも使うので
	// ファイル版でなくても作成する必要がある
	y_dc_size =MakeHuffDC(y_dc ,HuffDC_Y ,17 /*sizeof(HuffDC_Y )/sizeof(HUFFMAN)*/);
	y_ac_size =MakeHuffAC(y_ac ,HuffAC_Y ,ACLEN*ACBIT /*sizeof(HuffAC_Y )/sizeof(HUFFMAN)*/);
	if (!(mode&MD_BW)) {
	uv_dc_size=MakeHuffDC(uv_dc,HuffDC_UV,17 /*sizeof(HuffDC_UV)/sizeof(HUFFMAN)*/);
	uv_ac_size=MakeHuffAC(uv_ac,HuffAC_UV,ACLEN*ACBIT /*sizeof(HuffAC_UV)/sizeof(HUFFMAN)*/);
	}
	
	// 圧縮用パラメーター一部計算
	mcux=PressParamX(gsx,gsy,&gwx,&gwy,mode,&rstcnt,&w);
	printf("リスタートマーカー実際（0=なし）:%d\n",(int)rstcnt);
	
	#ifndef ONMEM
	if (ffile) {
		// ヘッダー書き込み
		ret=JpegStart(fp);
		if (ret) {
		    WFILE_ERR:;
			printf("ファイルに書き込めません:%d\n",ret);
			fclose(fp);
			exit(1);
		}
		ret=JpegMakeHeader(fp,gwx,gwy+((mode&MD_BMODE)==MD_BAI? gwy:0)
				,rstcnt
				,qY,qU,qV
				,y_dc,y_dc_size ,uv_dc,uv_dc_size
				,y_ac,y_ac_size ,uv_ac,uv_ac_size
							,mode,comm);
		if (ret) goto WFILE_ERR;
	}
	#endif
	
	// 圧縮
	#ifdef ONMEM
	jb=jbuff;
	#endif
	printf("圧縮中:(%d,%d)〜幅:%d,%d\n",gsx,gsy,gwx,gwy);
	ts=clock();
	ret=m(JpegPress(jdata,gsx,gsy,gwx,gwy,mode,rstcnt,mcux,w,qY,qU,qV,y,u,v,suv));
	printf("\r\x1b[0K%d\n",clock()-ts);
	#ifndef ONMEM
	if (ret) goto WFILE_ERR;
	#endif
	
	#ifndef ONMEM
	if (ffile) {
		ret=JpegEnd(fp);
		if (ret) goto WFILE_ERR;
	} else {
	    rewind(fp);	// 読み出しに変更のため
	#endif
	    if (getch()!=ESC) {
		// 展開
		RemakeHuff(y_dc,uv_dc,y_ac,uv_ac,mode); // 圧縮用→展開用ハフマンテーブルの構築
		// 拡大縮小による幅の調整
		switch (mode&MD_BMODE) { // 圧縮側
			case MD_BAI:
				gwy*=2; ewy*=2;
				break;
			case MD_1_2:
			case MD_1_4:
			case MD_1_8:
				gwx/=w; gwy/=w;
				ewx/=w; ewy/=w;
				break;
		}
		#ifdef ONMEM
		jb=jbuff;
		#else
	    LOADER:;
		#endif
		if (fcls) G_CLR_ON(); // グラフィック画面をクリアして表示
		
		printf("展開中:(%d,%d)〜幅:%d,%d\n",glx,gly,ewx,ewy);
		ts=clock();
		ret=m(JpegExpand(jdata,glx,gly,gwx,gwy,ewx,ewy,mode,rstcnt,qY,qU,qV,y,u,v,suv));
		#ifndef ONMEM
		if (ret) {
		    RFILE_ERR:;
			printf("ファイルから読み込めません:%d\n",ret);
			fclose(fp);
			exit(5);
		}
		#endif
		printf("\r\x1b[0K%d\n",clock()-ts);
	    }
	#ifndef ONMEM
	}
	fclose(fp);
	#endif
	exit(0);
}

//------------------------------------------------------------------------
