//------------------------------------------------------------------------
// JPEG Loader&Saver
//------------------------------------------------------------------------

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

char prog[]="@@> JPGSL.C : 2002/04/16 TUE : COMMAND : JPGSL";

// 2002/02/18	初版
// 2002/04/16	U/V独立量子化テーブルに対応

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

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

#define	MAXCOM		128		// コメント最大長（＜16bit）

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

// ワーク
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];

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

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 qrate;	// 圧縮率指定
int fsaver=0,fcls,fcenter,fcom,f512,fdisp;
FILE *fp;
unchar comm[MAXCOM+1];	// +1 for EOS
unchar fname[MAXPATH];
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];
	qV=qVV; // デフォルト
	
	clear_param();
	if (helpcom(argc,argv,0,"JPGSL","[Options] jpegfile[.jpg]")) exit(255);
	
	// いろいろ表示
	fdisp=check_param2("D",argv,argc,0);
	
	// 範囲指定
	{
	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)) {
		fsaver=1;
		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)) {
		fsaver=2;
		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];
	
	// 読み込み開始座標、展開サイズ指定
	// /Lglx,gly,ewx,ewy : 全て省略可能
	num[0]=num[1]=num[2]=num[3]=0;
	if (check_param3n("L",argv,argc,num,0,4,0,NULL)) {
		fsaver=0; // 圧縮モード強制解除
		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;
	}
	if (fsaver) { // 圧縮
		if (fdisp) printf("圧縮領域：(%d,%d)〜(%d,%d)；補正前\n",gsx,gsy,gsx+gwx-1,gsy+gwy-1);
		// 圧縮方法設定
		mode=MD_111; // 1:1:1圧縮展開（デフォルト）
		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("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;
		}
		if (mode&MD_BW) { // 白黒時
			mode=(mode&~MD_AMODE)|MD_111;
			printf("白黒ファイル出力時は強制的に1:1:1にします。\n");
			// それがJPEGの規格なので仕方ないが、自己圧縮展開の時は可能なので許す
		}
		// リスタートマーカー
		{
		int Irstcnt=0; // chec_param3はintしか読めないため
		if (check_param3("R",argv,argc,&Irstcnt,0)) {
			if (Irstcnt<0) Irstcnt=0;
			// これ以上の補正はPressParamX()で行う
		}
		if (fdisp) printf("リスタートマーカー指定（0=なし）:%d\n",Irstcnt);
		rstcnt=(short)Irstcnt;
		}
		// コメント
		comm[0]=0;
		ret=check_param2("C",argv,argc,BIT3|BIT4);
		if (ret) {
			znstrcpy3(comm,argv[ret],MAXCOM);
		} else {
			// 省略時はこのプログラムの名前を入れる
			strcpy(comm,argv[0]);
		}
		
		// 量子化テーブル＆圧縮率指定：Ｙ／ＵＶ別
		// 0        : 全て1にする＝量子化無し
		// 1/-1     : 元の量子化テーブルのまま
		// 255〜2   : 量子化テーブルにこの値を掛けて処理する
		// -255〜-2 : 量子化テーブルをこの値の絶対値で割って処理する
		#define	QORIGIN	1
		qrate=QORIGIN; // 元の量子化テーブルのまま
		    check_param3("QY" ,argv,argc,&qrate,0);  // Y圧縮率
		if (fdisp) 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);
			if (fdisp) printf(",Y量子化テーブル:%s\n",qname);
			if (LoadQtable(qname,qY,qrate)) {
				exit(20);
			}
		} else {
			// 省略時はデフォルトテーブルを使う
			SetQtable(qrate,qY,QtableY);
		}
		if (fdisp) printf("\n");
		
		qrate=QORIGIN; // 元の量子化テーブルのまま
		    check_param3("QU",argv,argc,&qrate,0);  // U圧縮率
		if (fdisp) 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);
			if (fdisp) printf(",U(/V)量子化テーブル:%s\n",qname);
			if (LoadQtable(qname,qU,qrate)) {
				exit(20);
			}
		} else {
			// 省略時はデフォルトテーブルを使う
			SetQtable(qrate,qU,QtableUV);
		}
		qV=qU; // U/Vテーブル共通
		if (fdisp) printf("\n");
		
		qrate=QORIGIN; // 元の量子化テーブルのまま
		    check_param3("QV",argv,argc,&qrate,0);  // V圧縮率
		if (fdisp) 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);
			if (fdisp) printf(",V量子化テーブル:%s\n",qname);
			qV=qVV;
			if (LoadQtable(qname,qV,qrate)) {
				exit(20);
			}
		} // 省略時はU/V共通テーブルを使う
		if (fdisp) printf("\n");
	} else { // 展開
		// 強制展開方法設定
		if (check_param2("W" ,argv,argc,0)) mode=(mode&~MD_EMODE)|MD_BAIE; // 縦倍展開
		if (check_param2("12",argv,argc,0)) mode=(mode&~MD_EMODE)|MD_1_2E; // 縦横1/2
		if (check_param2("14",argv,argc,0)) mode=(mode&~MD_EMODE)|MD_1_4E; // 縦横1/4
		if (check_param2("18",argv,argc,0)) mode=(mode&~MD_EMODE)|MD_1_8E; // 縦横1/8
		if (check_param2("BW",argv,argc,0)) mode|=MD_BWE; // 白黒展開のみ
		
		// コメント表示
		fcom=check_param2("C",argv,argc,0);
		
		// 再生画像のサイズがX68GWX*X68GWY未満の時、画面中央に表示する
		fcenter=check_param2("CEN",argv,argc,0);
		// 画面設定
		fcls=check_param2("CLS",argv,argc,0); // 展開画像表示前に画面を常にクリアする
		f512=check_param2("512",argv,argc,0); // 512*512モードで表示する
	}
	
	/* オプションチェック */
	ret=check_param_error(argv,argc);
	if (ret) {
		printsn("オプションエラーです：",argv[ret]);
		exit(3);
	}
	
	// ファイル名取得
	ret=getfname(argc,argv,1,NULL);
	if (ret!=0) { // 指定有り
		fn_ext0(argv[ret],fname,"JPG"); // 拡張子補足付き
	} else { // 指定なし
		printf("JPEGファイルを指定してください\n");
		exit(4);
	}
	
	if (!fsaver) { // 展開
		fp=fopen(fname,"rb");
		if (fp==NULL) {
			printf("ファイルが見つかりません:%s\n",fname);
			exit(1);
		}
		if (JpegReadHeader(fp,&gwx,&gwy,&mode,&rstcnt,qY,qU,qV,0,fcom)) {
			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;
			}
		}
		if (f512) {
			CRTMOD(SCRN);	// 画面設定
			G_CLR_ON();	// グラフィック画面をクリアして表示
		} else {
			if (fcls) G_CLR_ON(); // グラフィック画面をクリアして表示
			CRTC768x512G(1); // 768*512表示で512*512/65535色モード
		}
		
		if (fdisp) {
			printf("展開ファイル：%s\n",fname);
			printf("展開中:(%d,%d)〜幅:%d,%d\n",glx,gly,ewx,ewy);
		}
		ret=JpegExpand(fp,glx,gly,gwx,gwy,ewx,ewy,mode,rstcnt,qY,qU,qV,y,u,v,suv);
		if (ret) {
			printf("ファイルから読み込めません:%d\n",ret);
			fclose(fp);
			exit(5);
		}
		fclose(fp);
		exit(0);
	}
	
	// 圧縮
	// ファイル作成
	fp=fopen(fname,"w+b");
	if (fp==NULL) {
		printf("ファイルが作成出来ません:%s\n",fname);
		exit(1);
	}
	
	// 圧縮用ハフマンテーブルからファイル用ハフマンデータを構築する
	// Huff?C_Y/UVは内部にある固定ハフマンテーブル
	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);
	if (fdisp) printf("リスタートマーカー実際（0=なし）:%d\n",(int)rstcnt);
	
	// ヘッダー書き込み
	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;
	
	if (fdisp) printf("圧縮中:(%d,%d)〜幅:%d,%d\n",gsx,gsy,gwx,gwy);
	ret=JpegPress(fp,gsx,gsy,gwx,gwy,mode,rstcnt,mcux,w,qY,qU,qV,y,u,v,suv);
	if (ret) goto WFILE_ERR;
	
	ret=JpegEnd(fp);
	if (ret) goto WFILE_ERR;
	
	fclose(fp);
	exit(0);
}

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