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

#include "_jpeglib.h"
#include "jpeg.h"
#include "acdata.h"
#include <string.h>	// strlen()

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

static int fputw(unshort w,FILE *fp)
// 単純16bit出力
// return : =0:OK , <>0:error
{
	//printf("<fputw:%02x,%02x>\n",w>>8,(unchar)w);
	fputc((unchar)(w>>8),fp); // fputc()は出力した文字またはEOSを返す
	fputc((unchar)w,fp);
	return(ferror(fp));
}

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

int JpegStart(FILE *fp)
// JPEGデータ開始
{
	return(fputw(SOI,fp));
}

int JpegEnd(FILE *fp)
// JPEGデータ終了
{
	return(fputw(EOI,fp));
}

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

// アプリケーションデータセグメント
static const unchar app0[]={
	 'J','F','I','F',0	// 識別子
	,1,2			// バージョン v1.02フォーマット
	,0			// 密度単位 0=なし
	,0,0			// 横密度
	,0,0			// 縦密度
	,0			// サムネイル横ドット数 0=なし
	,0			// サムネイル縦ドット数 0=なし
};

// フレームヘッダー
static unchar frame[] = {
	 8		// サンプル精度:8bit固定
	,0/*H*/,0/*L*/	// line数＝yドット数；変化
	,0/*H*/,0/*L*/	// １ライン数当たりのサンプル数＝xドット数；変化
	,3		// 成分数:YUV=3 / 白黒モード時は1
	,0,0x11,0	// Yの識別子は0,サンプリングファクタは水平1,垂直1,量子化テーブルは0を使用
			// 白黒時は以下はなし
	,1,0x11,1	// Uの識別子は1,サンプリングファクタは水平1,垂直1,量子化テーブルは1を使用
	,2,0x11,2	// Vの識別子は2,サンプリングファクタは水平1,垂直1,量子化テーブルは2を使用
			// 但し、U/VＤで量子化テーブルを共通利用する時は量子化テーブル=1とする
};

// スキャンヘッダー
static unchar scanC[]={	// フルカラー用
	 3		// 成分数(Y/U/V)
	,0,0x00		// YのハフマンテーブルはDCは0,ACは0
	,1,0x11		// UのハフマンテーブルはDCは1,ACは1（白黒時はなし）
	,2,0x11		// VのハフマンテーブルはDCは1,ACは1（白黒時はなし）
	,0		// 基本DCTでは未使用（固定値）
	,63		// 基本DCTでは未使用（固定値）
	,0		// 基本DCTでは未使用（固定値）
};
static unchar scanBW[]={// 白黒用
	 1		// 成分数(Y)
	,0,0x00		// YのハフマンテーブルはDCは0,ACは0
	,0		// 基本DCTでは未使用（固定値）
	,63		// 基本DCTでは未使用（固定値）
	,0		// 基本DCTでは未使用（固定値）
};

// どの量子化テーブルやハフマンテーブルをDC/ACおよびY/UVのテーブルに割り当てるかは
// 各テーブルに付けた４ビットの番号で識別される。このテーブル番号の付け方は任意である。
// （量子化テーブル、ハフマンテーブルＤＣ、ハフマンテーブルＡＣそれぞれで。）
// ここではたまたま
//	量子化Y=0,量子化U=1,量子化V=2（但しV省略の時はUV=1）
//	ハフマンDC/Y=0,ハフマンDC/UV=1
//	ハフマンAC/Y=0,ハフマンAC/UV=1
// としている。番号を分けた方が解りやすかったかも知れない。
//------------------------------------------------------------------------

static int FwriteQtable(unchar qtable[],FILE *fp)
// 量子化テーブルの出力
{
int i;
	// 量子化テーブルはジグザグシーケンスで出力しなければならない
	for (i=0;i<JWV*JWU;i++) {
		fputc(qtable[_zigzag[i]],fp);
	}
	return(ferror(fp));
}

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

int JpegMakeHeader(FILE *fp,int gwx,int gwy,short rstcnt
		,unchar *qY,unchar *qU,unchar *qV
		,unchar *y_dc ,int y_dc_size ,unchar *uv_dc,int uv_dc_size
		,unchar *y_ac ,int y_ac_size ,unchar *uv_ac,int uv_ac_size
						,long mode,unchar *comment)
// JPEGヘッダー書き込み
// 圧縮率を変更する時には量子化テーブルが変更されるので、外部から与えるようにした
{
int ret;
	// 各マーカーは必ずバイト境界にある
	// ヘッダーの出力順はこの通りにしておかなければならない！！
	// アプリケーションデータセグメント
	fputw(APP0,fp);
	fputw(2+sizeof(app0),fp);	// テーブル長
	fwrite(app0,sizeof(app0),1,fp);
	// コメント
	fputw(COM,fp);
	fputw(2+strlen(comment)+1,fp);	// ここではEOSも入れる
	fwrite(comment,strlen(comment)+1,1,fp);
	// 量子化テーブル
	fputw(DQT,fp);
	if (mode&MD_BW) { // 白黒
		fputw(2+(1+JWU*JWV),fp); // テーブル定義長；2=sizeof(ここ)
	} else { // カラー
		fputw(2+(1+JWU*JWV)+(1+JWU*JWV)
			+(qV!=qV? (1+JWU*JWV):0) // 量子化Vを独立する時
			,fp); // テーブル定義長；2=sizeof(ここ)
	}
	fputc(0x00,fp);	// 上位４ビット：エレメント精度（０＝８ビット固定）
			// 下位４ビット：テーブル識別子；0=Y（フレームヘッダーで定義）
	ret=FwriteQtable(qY,fp);
	if (ret) return(ret);
	
	if (!(mode&MD_BW)) { // カラー
		fputc(0x01,fp);	// 上位４ビット：エレメント精度（０＝８ビット固定）
				// 下位４ビット：テーブル識別子；1=U（フレームヘッダーで定義）
		ret=FwriteQtable(qU,fp);
		if (ret) return(ret);
		if (qV!=qU) { // 量子化テーブルVを独立指定する時
			fputc(0x02,fp);	// 上位４ビット：エレメント精度（０＝８ビット固定）
					// 下位４ビット：テーブル識別子；2=V（フレームヘッダーで定義）
			ret=FwriteQtable(qV,fp);
			if (ret) return(ret);
		}
	}
	// ハフマンテーブル
	fputw(DHT,fp);
	if (mode&MD_BW) {
		fputw(2+(1+y_dc_size)+(1+y_ac_size),fp);
		// テーブル定義長；2=sizeof(ここ),1=テーブルクラス＆識別子
		fputc(0x00,fp);	// 上位４ビット：テーブルクラス；0=DC,1=AC
				// 下位４ビット：テーブル識別子；0=Y ,1=UV（フレームヘッダーで定義）
		fwrite(y_dc ,y_dc_size,1,fp);
		fputc(0x10,fp);	// AC,Y
		fwrite(y_ac ,y_ac_size,1,fp);
	} else {
		fputw(2+(1+y_dc_size)+(1+uv_dc_size)+(1+y_ac_size)+(1+uv_ac_size),fp);
		fputc(0x00,fp);	// 上位４ビット：テーブルクラス；0=DC,1=AC
				// 下位４ビット：テーブル識別子；0=Y ,1=UV（フレームヘッダーで定義）
		fwrite(y_dc ,y_dc_size,1,fp);
		fputc(0x01,fp);	// DC,UV
		fwrite(uv_dc,uv_dc_size,1,fp);
		fputc(0x10,fp);	// AC,Y
		fwrite(y_ac ,y_ac_size,1,fp);
		fputc(0x11,fp);	// AC,UV
		fwrite(uv_ac,uv_ac_size,1,fp);
	}
	
	if (rstcnt>0) { // リスタートマーカーが有効な時
		// リスタートインターバル定義
		unchar buff[sizeof(unshort)];
		fputw(DRI,fp);
		fputw(2+sizeof(unshort),fp);	// テーブル長
		stoc(rstcnt,buff); // H,Lの順であること
		fwrite(buff,sizeof(buff),1,fp);
	}
	
	// フレームヘッダー
	stoc(gwy,&frame[1]);		// line数＝yドット数；変化
	stoc(gwx,&frame[3]);		// １ライン数当たりのサンプル数＝xドット数；変化
	frame[5]=(mode&MD_BW? 1:3);	// Yのみ:YUV
	switch (mode&MD_AMODE) {
		case MD_111: frame[7]=0x11; break;
		case MD_2I11:frame[7]=0x12; break;
		case MD_2_11:frame[7]=0x21; break;
		case MD_411: frame[7]=0x22; break;
	}
	frame[14]=(qV!=qU? 2:1); // 量子化テーブルU/V独立:共通
	fputw(SOF0,fp);
	{ int size;
	size=sizeof(frame);
	if (mode&MD_BW) size-=6;
	fputw(2+size,fp);		// テーブル長
	fwrite(frame,size,1,fp);
	}
	// スキャンヘッダー
	if (mode&MD_BW) {
		fputw(SOS,fp);
		fputw(2+sizeof(scanBW),fp);	// テーブル長
		fwrite(scanBW,sizeof(scanBW),1,fp);
	} else {
		fputw(SOS,fp);
		fputw(2+sizeof(scanC),fp);	// テーブル長
		fwrite(scanC,sizeof(scanC),1,fp);
	}
	//
	return(ferror(fp));
}

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