//------------------------------------------------------------------------
// JPEGヘッダーの解釈
//------------------------------------------------------------------------

#include "jpeg.h"
#include <stdio.h>
#include "acdata.h"	// ctos()

#define	BUFSZ	2048

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

static int fgetw(FILE *fp)
// 単純16bit入力
// return : >=0:読み込み値 , EOF:error
{
int c;
	c=fgetc(fp)<<8;
	c|=fgetc(fp);
	return(ferror(fp)? EOF:c);
}

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

static int SkipSegment(FILE *fp,int *size,unchar buff[BUFSZ],int fdisp,int fcomm)
// 処理しないセグメントを読み飛ばす
{
int sz1,sz,c;
	c=fgetw(fp); // マーカーの読みだし
	if (    (c>=SOF1  && c<=SOF3)
	    ||	(c>=SOF5  && c<=SOF7)
	    ||	(c>=SOF9  && c<=SOF11)
	    ||	(c>=SOF13 && c<=SOF15)
	) {
		printf("基本ＤＣＴ以外は処理出来ません:$%04x\n",c);
		return(EOF-1); // エラーにする
	}
	switch (c) {
		case EOF: // エラー
			return(EOF);
		default: // その他のセグメント
			if ((c&0xff00)!=0xff00) return(EOF); // マーカーがおかしい
			if (fdisp) {
				if (c==APP0) printf("APP0\n");
				else printf("MAKER=$%04x\n",c);
			}
		case COM:// コメントセグメントは複数回出てくることもある
			*size=sz1=fgetw(fp)-2; // テーブル長
			do { // 本体
				if (sz1>BUFSZ) sz=BUFSZ; else sz=sz1;
				if (fread(buff,1,sz,fp)!=sz) {
					printf("ファイルが読めません\n");
					return(EOF-1); // エラー
				}
				if (c==COM && fcomm) { // コメント表示
					int i;
					for (i=0;i<sz;i++) {
						if (buff[i]!=0) printf("%c",buff[i]);
					}
				}
				sz1-=sz;
			} while (sz1>0);
			if (c==COM && fcomm) printf("\n");
			break;
		case SOS: // スキャンヘッダー
		case SOF0:// 基本ＤＣＴフレームヘッダー
		case DRI: // リスタートインターバル
		case DQT: // 量子化テーブル　：複数回来ることもある
		case DHT: // ハフマンテーブル：複数回来ることもある
			*size=fgetw(fp)-2; // テーブル長
		case SOI: // 全体の先頭（これにはサイズはない）
			break;
	}
	return(c); // >=0
}

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

JPEGERR JpegReadHeader(FILE *fp,int *gwx,int *gwy,long *mode,short *rstcnt
				,unchar *qY,unchar *qU,unchar *qV
				,int fdisp,int fcomm)
// JPEGヘッダー解釈
// 一部手抜き（フレームヘッダーとスキャンヘッダーの識別番号対応をさせてない＝格納順に依存している）
{
int size,c,i;
unchar buff[BUFSZ],*b;
	// SOIの確認
	if ((c=fgetw(fp))==EOF) return(JPEG_READ_ERR);
	if (c!=SOI) {
		// MACバイナリヘッダー付きJPEG対応
		// バイナリヘッダーは読み飛ばす
		while ((c=fgetc(fp))!=EOF) {
			if (c==JPGMAKHED) {
				if ((c=fgetc(fp))==EOF) break;
				if (c==(SOI&0xff)) {
					if (fdisp) printf("MACバイナリヘッダー付き\n");
					goto HEAD;
				}
			}
		}
		printf("このファイルはＪＰＥＧではありません\n");
		return(JPEG_NO_JPEG);
	}
    HEAD:;
	fseek(fp,-2,SEEK_CUR); // SOIまで戻す
	*rstcnt=0; // 一端リスタート定義を０にする
	do {
	    c=SkipSegment(fp,&size,buff,fdisp,fcomm);
	    switch (c) {
	    case EOF:
		printf("マーカーが異常です\n");
	    case EOF-1:
		return(JPEG_MAKER_ERR);
	    case SOI:
		if (fdisp) printf("SOI\n");
		break;
	    case DRI: // リスタートインターバル定義
		fread(buff,size,1,fp);
		*rstcnt=ctos(buff);
		if (fdisp) printf("リスタートインターバル(%d)=%d\n",size,*rstcnt);
		break;
	    case DQT: // 量子化テーブル
		// 先にテーブルを一気に読み込む
		fread(buff,size,1,fp);
		// buff=(Class|識別子),tableの順
		// 有り得るパターンは
		// １、輝度のみ
		// ２、色差のみ
		// ３、輝度→色差のみ
		// ４、色差→輝度のみ
		if (fdisp) printf("量子化テーブル(%d)：",size);
		b=buff;
		do {
		    switch ((*b)&0x0f) {
			case 0x0: // 輝度
				if (fdisp) printf("輝度.");
				b++;
				for (i=0;i<JWV*JWU;i++) {
					qY[_zigzag[i]]=*b++;
					// Zigzagスキャンを解除しながら読み出す
				}
				break;
			// 手抜き：色差がU/Vで分離されている時は必ずU->Vの順としている
			case 0x1: // 色差U
				if (fdisp) printf("色差U.");
				b++;
				for (i=0;i<JWV*JWU;i++) {
					qV[_zigzag[i]]=
					qU[_zigzag[i]]=*b++;
					// Vが省略されていることがあるのでとりあえずVにも入れておく
					// Zigzagスキャンを解除しながら読み出す
				}
				break;
			case 0x2: // 色差V
				if (fdisp) printf("色差V.");
				b++;
				for (i=0;i<JWV*JWU;i++) {
					qV[_zigzag[i]]=*b++;
					// Zigzagスキャンを解除しながら読み出す
				}
				break;
			default:
				printf("量子化テーブルが異常です:$%x\n",(*b)&0x0f);
				return(JPEG_ILL_HEADER);
		    }
		    size-=(1+JWU*JWV);
		    // ここはファイル読み込みでエラーがあると無限ループに陥るので
		    // エラーチェックしておく
		    if (ferror(fp)) return(JPEG_READ_ERR);
		} while (size>0);
		if (fdisp) printf("\n");
		break;
	    case DHT: // ハフマンテーブル
		// 先にテーブルを一気に読み込む
		fread(buff,size,1,fp);
		// buff=(Class|識別子),tableの順
		if (fdisp) printf("ハフマンテーブル(%d)：",size);
		b=buff;
		do {
		    unchar *b0;
		    int sz;
		    b0=b;
		    switch (*b) {
			case 0x00: // 輝度DC
				if (fdisp) printf("輝度DC.");
				b0=b++;
				MakeiHuff(&b,iHuffDC_Y,cntDC_Y);
				break;
			case 0x10: // 輝度AC
				if (fdisp) printf("輝度AC.");
				b0=b++;
				MakeiHuff(&b,iHuffAC_Y,cntAC_Y);
				break;
			case 0x01: // 色差DC
				if (fdisp) printf("色差DC.");
				b0=b++;
				MakeiHuff(&b,iHuffDC_UV,cntDC_UV);
				break;
			case 0x11: // 色差AC
				if (fdisp) printf("色差AC.");
				b0=b++;
				MakeiHuff(&b,iHuffAC_UV,cntAC_UV);
				break;
			default:
				printf("ハフマンテーブルが異常です\n");
				return(JPEG_ILL_HEADER);
		    }
		    sz=(int)(b-b0);
		    size-=sz;
		    // ここはファイル読み込みでエラーがあると無限ループに陥るので
		    // エラーチェックしておく
		    if (ferror(fp)) return(JPEG_READ_ERR);
		} while (size>0);
		if (fdisp) printf("\n");
		break;
	    case SOF0:	// フレームヘッダー
		#if 0
		unchar frame[] = {
			 8		// サンプル精度:8bit固定
			,0,0		// line数(H,L順)
			,0,0		// １ライン数当たりのサンプル数＝xドット数(H,L順)
			,3		// 成分数:YUV=3 / 白黒化で1
			// 4:1:1の設定
			,0,0x22,0	// Yの識別子は0,サンプリングは水平2,垂直2,量子化テーブルは0
			// 白黒化では以下がない
			,1,0x11,1	// Uの識別子は1,サンプリングは水平1,垂直1,量子化テーブルは1
			,2,0x11,1	// Yの識別子は2,サンプリングは水平1,垂直1,量子化テーブルは1
		};
		#endif
		if (fdisp) printf("フレームヘッダー(%d)\n",size);
		fread(buff,size,1,fp);	// 読みは全部
		*gwy=ctos(&buff[1]); // 68フォーマット
		*gwx=ctos(&buff[3]); // 68フォーマット
		if (buff[5]==1) { // 白黒モード時
			// 強制的に1:1:1にする
			*mode=(*mode&~MD_AMODE)|MD_111|MD_BW;
		} else {
		    switch ((buff[7]<<16)|(buff[10]<<8)|buff[13]) {
		    case 0x221111: // 4:1:1
			*mode=(*mode&~MD_AMODE)|MD_411;
			break;
		    case 0x211111: // 2-:1:1
			*mode=(*mode&~MD_AMODE)|MD_2_11;
			break;
		    case 0x121111: // 2I:1:1
			*mode=(*mode&~MD_AMODE)|MD_2I11;
			break;
		    case 0x111111: // 1:1:1
			*mode=(*mode&~MD_AMODE)|MD_111;
			break;
		    default: // 来ないはずだけど念のため
			printf("このサンプリングファクタには対応してません:%06x\n",mode);
			return(JPEG_ILL_HEADER);
		    }
		}
		break;
	    case SOS: // スキャンヘッダー
		#if 0
		unchar scan[]={
			 3	// 成分数(Y/U/V) / 白黒化で1
			,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では未使用（固定値）
		};
		#endif
		if (fdisp) printf("スキャンヘッダー(%d)\n",size);
		fread(buff,size,1,fp);	// 読みは全部
		break;
	    } // switch
	} while (c!=SOS);
	//
	if (ferror(fp)) return(JPEG_READ_ERR);
	if (fdisp) printf("データ本体位置=$%lx\n",ftell(fp));
	return(JPEG_OK);
}

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