/*
 *	dpbmppp - ビットマッププリプロセッサ
 *	Copyright (C) 2007-2017 Naoyuki Sawa
 *
 *	* Thu Jan 11 21:03:55 JST 2007 Naoyuki Sawa
 *	- 1st リリース。
 *	* Sat Jan 13 19:45:45 JST 2007 Naoyuki Sawa
 *	- 3ピクセルの元画像を"-s50%"で2ピクセル画像に縮小するような場合の処理を変更しました。
 *	- これまでは、単純に3ピクセル→2ピクセルに縮小していました。
 *	  しかし、これでは66%縮小となり、厳密にはユーザーの期待する結果と異なります。
 *	- そこで、出力画像のキャンバスサイズは2ピクセルですが、3ピクセル→1.5ピクセルと考えて縮小するよう、処理を変更しました。
 *	  元画像を4ピクセルと考えて、4ピクセル→2ピクセルの50%縮小と言い換えることもできます。
 *	  本来、元画像には含まれていない4ピクセル目からカラーを取得する際にはは、透明と考えて処理します。
 *	  すなわち、"-b"を指定すればそのカラーと考えて加重平均し、"-b"を指定しなければ加重平均に含めません。
 *	- 以上の変更により、9x9ピクセルフォント画像を50%縮小するような場合に、これまでよりも厳密に50%縮小の結果が期待できます。
 *	  出力画像サイズは5x5ピクセルでこれまでと同じですが、画像の内容が4.5x4.5ピクセル相当へのリサンプルとなるからです。
 *	- "-w??%","-h??%","-s??%"を使わず、"-w??","-h??","-s??"で直接ピクセル数を指定した場合は、これまでと同じ結果となります。
 *	* Sun Jan 14 00:01:23 JST 2007 Naoyuki Sawa
 *	- 画像情報の表示を追加しました。
 *	- 画像情報を表示しない、"-q"オプションを追加しました。
 *	* Thu Jan 18 01:34:05 JST 2007 Naoyuki Sawa
 *	 - resample()関数のアルゴリズムチェックコード自体が誤っていたので、チェックコードを削除しました。
 *	   前バージョンでは、正しい処理に対してもアサートしてしまうという不具合が発生していました。
 *	   今回のチェックコード削除により、無用なアサートを回避しました。
 *	* Mon Feb 05 12:44:07 JST 2007 Naoyuki Sawa
 *	- -w,-h,-sオプションに、分数指定を追加しました。
 *	  分数指定を追加した理由は、以下のとおりです。
 *	- これまで、VGA解像度の入力画像を、P/ECE解像度に変換する場合、ピクセル指定や%指定では高さが割り切れず、厳密に正確な指定ができませんでした。
 *	  たとえば、256x256ピクセルの入力画像に上述の変換を行う場合、100×88/480=46.933となり、ピクセル指定では厳密に正確な指定ができません。
 *	  また、88/480=0.18333となり、パーセント指定でも厳密に正確な指定ができません。
 *	- 今回追加した分数指定を使えば、"-h88/480"と指定することにより、厳密に正確な指定が可能となりました。
 *	* Sun May 21 21:27:29 JST 2017 Naoyuki Sawa
 *	- '-r'オプションを追加しました。
 *	  パレットが整理されていなくて、グレースケール部分(不透明)と非グレースケール部分(透明を意味する)が並んでいない画像を、P/ECE素材として適した画像に変換するために追加しました。
 *	  PictBear等でフルカラー形式の画像をパレット形式の画像に変換すると、上記のような使い辛い画像になり易いので、そのような画像を変換するために追加した機能です。
 *	  尚、dpbmpcnv.exeにも似た機能として減色オプション(-d)が有りますが、そちらは透明色を含まない画像のディザ付き減色のための機能であり、今回当ツールに追加した機能とは目的が異なります。
 *	* Mon May 22 22:18:20 JST 2017 Naoyuki Sawa
 *	- '-r'オプションの内部動作を変更しました。
 *	  詳細は、以下の通りです。
 *	- 変更前は、入力画像ロード⇒パレットリマップ処理⇒リサンプル処理⇒出力画像セーブ、の順で処理を行っていました。
 *	  変更後は、入力画像ロード⇒(擬似的に255諧調に変換する)⇒リサンプル処理⇒指定された諧調数にパレットリマップ⇒出力画像セーブ、の順で処理を行うようにしました。
 *	  パレットリマップ処理を後で行うようにした理由は、パレットリマップは(一般的には)諧調を減らす処理であり、諧調を減らした後にリサンプル処理を行うよりも、諧調を高いままでリサンプル処理を行う方が、綺麗にリサンプル出来ると思ったからです。
 *	  ただし、'-r'オプションが指定されるような入力画像のパレット並びは、必ずしも等間隔のグレースケールになっているとは限らないので(だから'-r'オプションを指定しているので)、まず擬似的に等間隔な255諧調に変換してから、リサンプルを行うようにしました。
 *	  256諧調ではなく255諧調とした理由は、元画像の非グレースケール色(=透明色)用の1エントリが必要だからです。
 *	  当該画像が透明色を使っていなければ、256諧調フルに使う事も可能なのですが、P/ECEの画像素材で256諧調を厳密に維持する必要が有るようなケースはまず無いので、実用上問題無いと思います。
 *	  実際に、4諧調や、16諧調ならば、最終的に誤差は影響しません。
 *	- '-r'オプションに、ディザ機能を追加しました。
 *	  上記の変更を行った事により、出力画像セーブの直前まで、255諧調を維持できるようになったので、出力画像セーブの直前で最終的なパレットリマップを行う直前で、ディザを掛けられるようになったからです。
 *	  変更前は、最初にパレットリマップを行っていたため、ディザを書けた結果に対してリサンプル(拡大/縮小)を行ってもあまり意味が無いので、ディザ機能は搭載していなかったのですが、
 *	  今回の変更により、上記の通り効果的にディザが掛けられるようになったので、ディザ機能を追加する事にしました。
 *	  ディザ機能を有効にするには、'-r'オプションの引数の数値の後に、'd'を指定して下さい。(例:'-r4d')
 *	- 今回の変更は全て、'-r'オプションを指定した場合だけに影響の有る変更であり、'-r'オプションを指定しなかった場合の動作には、一切の影響は有りません。
 *	  従って、既存の、'-r'オプションを指定せずに当ツールを使用していたアプリケーションに対して、今回の変更が悪影響を及ぼす事は有りません。
 *	* Thu May 25 21:01:04 JST 2017 Naoyuki Sawa
 *	- '-w',及び,'-h'オプションに、auto指定を追加しました。
 *	  追加した理由は、以下の通りです。
 *	- 既存のオプションでは、幅(又は高さ)ピクセル数を明示的に指定して、アスペクト比を保って高さ(又は幅)ピクセルを自動的に決定する事が出来ませんでした。
 *	  幅(又は高さ)の元サイズが既知ならば、'-s出力サイズ/元サイズ'オプションを使って指定する事は一応可能だったのですが、元サイズが既知でない場合に指定する事が出来ませんでした。
 *	  それでは不便なので、上記の目的を達成するためのオプションとして、'-w',及び,'-h'オプションにauto指定を追加しました。
 *	- 今回追加したauto指定を使って、以下のように指定する事が出来るようになりました。
 *	  <例>
 *	  ・「-w128 -hauto」…幅  の出力サイズを128にし、アスペクト比を保つように高さの出力サイズを決定する。(「-hauto -w128」と指定しても同じです。)
 *	  ・「-h88  -wauto」…高さの出力サイズを 88にし、アスペクト比を保つように幅  の出力サイズを決定する。(「-wauto  -h88」と指定しても同じです。)
 */
#define STRICT
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <math.h>

#define VERSION	"20170525"

/****************************************************************************
 *	
 ****************************************************************************/

char src_path[_MAX_PATH];	/* 入力ファイル名 */
char dst_path[_MAX_PATH];	/* 出力ファイル名 */
int opt_w1;	/* 幅指定                                            */
int opt_w2;	/*   指定無し      w1=0         , w2=0               */
		/*   ピクセル指定  w1=ピクセル数, w2=0               */
		/*   %指定         w1=%         , w2=100             */
		/*   分数指定      w1=分子      , w2=分母            */
		/*   auto指定      w1=-1        , w2=(不問)          */			//{{2017/05/25追加:'-w',及び,'-h'オプションに、auto指定を追加しました。}}
		/*   (どの場合も、解析後に分子/分母に設定し直します) */
int opt_h1;	/* 高さ指定                                          */
int opt_h2;	/*   (opt_w1,w2と同様。上コメントを参照してください) */
int opt_b;	/* カラー0と未設定を区別するために、+1しておきます */
int opt_c;	/* 不透明色数。256ならば透明色無しです */
int opt_r;	/* パレットリマップ諧調数。0ならばパレットリマップを行わない。 */	//{{2017/05/21追加:'-r'オプションを追加しました。}}
int opt_r_d;	/* リマップ処理時にディザリングを行う。 */				//{{2017/05/22追加:'-r'オプションの内部動作を変更しました。}}
int opt_q;	/* 画像情報を表示しません */

#define MAX_SIZE	4096
unsigned char src_bits[MAX_SIZE][MAX_SIZE];	/* 入力画像 */
unsigned char dst_bits[MAX_SIZE][MAX_SIZE];	/* 出力画像 */
RGBQUAD pal[256];				/* パレット */
int trans;					/* これ以上の色は、透明色と見なします */

int src_w;	/* 入力キャンバスの幅   (ピクセル単位) */
int src_h;	/* 入力キャンバスの高さ (ピクセル単位) */
int dst_w;	/* 出力キャンバスの幅   (ピクセル単位) */
int dst_h;	/* 出力キャンバスの高さ (ピクセル単位) */

void err(const char* fmt, ...);
void usage();
void cmdline(int argc, char* argv[]);
void loadbmp();
void resample();
void savebmp();

/****************************************************************************
 *	main
 ****************************************************************************/

int
main(int argc, char* argv[])
{
	/* コマンドラインを解析します。 */
	cmdline(argc, argv);

	/* 入力画像をロードします。 */
	loadbmp();

	/* 出力画像サイズを決定します。 */
//{{2017/05/25変更:'-w',及び,'-h'オプションに、auto指定を追加しました。
//	if(!opt_w2) { /* 指定無し or ピクセル指定 */
//		opt_w2 = src_w;
//		if(!opt_w1) { /* 指定無し */
//			opt_w1 = src_w;
//		}
//	}
//	dst_w = (src_w * opt_w1 + (opt_w2 - 1)) / opt_w2;
//	if(dst_w > MAX_SIZE) err("出力画像が大きすぎます");
//	//
//	if(!opt_h2) { /* 指定無し or ピクセル指定 */
//		opt_h2 = src_h;
//		if(!opt_h1) { /* 指定無し */
//			opt_h1 = src_h;
//		}
//	}
//	dst_h = (src_h * opt_h1 + (opt_h2 - 1)) / opt_h2;
//	if(dst_h > MAX_SIZE) err("出力画像が大きすぎます");
//↓2017/05/25変更:'-w',及び,'-h'オプションに、auto指定を追加しました。
	//不正なオプション指定を検出する。
	if((opt_w1 == -1) && (opt_h1 == -1)) { err("'-wauto'と'-hauto'の同時指定,又は,'-sauto'を指定する事は出来ません｡"); }
	if(opt_w1 == -1) {				//'-wauto'が指定されていたら…
		if(!((opt_h1 > 0) && (opt_h2 == 0))) {	//'-h縦ﾋﾟｸｾﾙ数'が指定されていなければ…
			err("'-wauto'ｵﾌﾟｼｮﾝは､必ず'-h縦ﾋﾟｸｾﾙ数'ｵﾌﾟｼｮﾝと併用して下さい｡");
		}
	}
	if(opt_h1 == -1) {				//'-hauto'が指定されていたら…
		if(!((opt_w1 > 0) && (opt_w2 == 0))) {	//'-w横ﾋﾟｸｾﾙ数'が指定されていなければ…
			err("'-hauto'ｵﾌﾟｼｮﾝは､必ず'-w横ﾋﾟｸｾﾙ数'ｵﾌﾟｼｮﾝと併用して下さい｡");
		}
	}
	//指定無し,又は,ピクセル指定を展開する。
	if(!opt_w2) { /* 指定無し or ピクセル指定 */
		opt_w2 = src_w;
		if(!opt_w1) { /* 指定無し */
			opt_w1 = src_w;
		}
	}
	if(!opt_h2) { /* 指定無し or ピクセル指定 */
		opt_h2 = src_h;
		if(!opt_h1) { /* 指定無し */
			opt_h1 = src_h;
		}
	}
	//auto指定を展開する。
	if(opt_w1 == -1) {	/* auto指定 */
		opt_w1 = opt_h1;
		opt_w2 = opt_h2;
	}
	if(opt_h1 == -1) {	/* auto指定 */
		opt_h1 = opt_w1;
		opt_h2 = opt_w2;
	}
	//出力画像サイズを決定する。
	dst_w = (src_w * opt_w1 + (opt_w2 - 1)) / opt_w2;
	if(dst_w > MAX_SIZE) err("出力画像が大きすぎます");
	dst_h = (src_h * opt_h1 + (opt_h2 - 1)) / opt_h2;
	if(dst_h > MAX_SIZE) err("出力画像が大きすぎます");
//}}2017/05/25変更:'-w',及び,'-h'オプションに、auto指定を追加しました。

	/* 画像情報を表示します。 */
	if(!opt_q) {
		printf("■入力画像\n");
		printf("　ﾌｧｲﾙ名    : %s\n", src_path);
		printf("　ｷｬﾝﾊﾞｽｻｲｽﾞ: %d x %d\n", src_w, src_h);
		printf("　ｲﾒｰｼﾞ ｻｲｽﾞ: %f x %f\n", (double)src_w * opt_w2 / opt_w2,
						  (double)src_h * opt_h2 / opt_h2);
		printf("■出力画像\n");
		printf("　ﾌｧｲﾙ名    : %s\n", dst_path);
		printf("　ｷｬﾝﾊﾞｽｻｲｽﾞ: %d x %d\n", dst_w, dst_h);
		printf("　ｲﾒｰｼﾞ ｻｲｽﾞ: %f x %f\n", (double)src_w * opt_w1 / opt_w2,
						  (double)src_h * opt_h1 / opt_h2);
	}

	/* リサンプル処理を行います。 */
	resample();

	/* 出力画像をセーブします。 */
	savebmp();

	return 0;
}

/****************************************************************************
 *	err
 ****************************************************************************/

void
err(const char* fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	 fprintf(stderr, "### ");
	vfprintf(stderr, fmt, ap);
	 fprintf(stderr, "\n");
	va_end(ap);
	abort();
}

/****************************************************************************
 *	usage
 ****************************************************************************/

void
usage()
{
	fprintf(stderr, "dpbmppp - ﾋﾞｯﾄﾏｯﾌﾟﾌﾟﾘﾌﾟﾛｾｯｻ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2007-2017 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "使い方:\n");
	fprintf(stderr, "    dpbmppp [ｵﾌﾟｼｮﾝ] 入力ﾌｧｲﾙ名[.bmp] 出力ﾌｧｲﾙ名[.bmp]\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "ｵﾌﾟｼｮﾝ:\n");
	fprintf(stderr, "    -w横ﾋﾟｸｾﾙ数      <例> -w128     横ｻｲｽﾞを128ﾋﾟｸｾﾙにﾘｻﾝﾌﾟﾙします｡\n");
	fprintf(stderr, "    -w横拡大率%%      <例> -w200%%    横ｻｲｽﾞを入力画像の200%%にﾘｻﾝﾌﾟﾙします｡\n");
	fprintf(stderr, "    -w分子/分母      <例> -w128/640 横ｻｲｽﾞを128/640の割合でﾘｻﾝﾌﾟﾙします｡\n");
//{{2017/05/25追加:'-w',及び,'-h'オプションに、auto指定を追加しました。
	fprintf(stderr, "    -wauto           <例> -wauto    ｱｽﾍﾟｸﾄ比を保つように､横ｻｲｽﾞを決定します｡\n");
	fprintf(stderr, "                                    必ず'-h縦ﾋﾟｸｾﾙ数'ｵﾌﾟｼｮﾝと併用して下さい｡\n");
//}}2017/05/25追加:'-w',及び,'-h'オプションに、auto指定を追加しました。
	fprintf(stderr, "    -h縦ﾋﾟｸｾﾙ数      <例> -h88      縦ｻｲｽﾞを88ﾋﾟｸｾﾙにﾘｻﾝﾌﾟﾙします｡\n");
	fprintf(stderr, "    -h縦拡大率%%      <例> -h75%%     縦ｻｲｽﾞを入力画像の75%%にﾘｻﾝﾌﾟﾙします｡\n");
	fprintf(stderr, "    -h分子/分母      <例> -w88/480  縦ｻｲｽﾞを88/480の割合でﾘｻﾝﾌﾟﾙします｡\n");
//{{2017/05/25追加:'-w',及び,'-h'オプションに、auto指定を追加しました。
	fprintf(stderr, "    -hauto           <例> -hauto    ｱｽﾍﾟｸﾄ比を保つように､縦ｻｲｽﾞを決定します｡\n");
	fprintf(stderr, "                                    必ず'-w横ﾋﾟｸｾﾙ数'ｵﾌﾟｼｮﾝと併用して下さい｡\n");
//}}2017/05/25追加:'-w',及び,'-h'オプションに、auto指定を追加しました。
	fprintf(stderr, "    -s縦横ﾋﾟｸｾﾙ数    <例> -s256     縦横ｻｲｽﾞを256ﾋﾟｸｾﾙにﾘｻﾝﾌﾟﾙします｡\n");
	fprintf(stderr, "    -s縦横拡大率%%    <例> -s50%%     縦横ｻｲｽﾞを入力画像の50%%にﾘｻﾝﾌﾟﾙします｡\n");
	fprintf(stderr, "    -s分子/分母      <例> -s2/3     縦横ｻｲｽﾞを2/3の割合でﾘｻﾝﾌﾟﾙします｡\n");
	fprintf(stderr, "    -b境界背景色     <例> -b0       ﾘｻﾝﾌﾟﾙ時に透明ﾋﾟｸｾﾙと不透明ﾋﾟｸｾﾙを混合する場合､\n");
	fprintf(stderr, "                                    不透明ﾋﾟｸｾﾙをこの色と仮定して加重平均を取ります｡\n");
	fprintf(stderr, "                                    透明背景に黒一色で描いたﾃｷｽﾄ画像を縮小する場合\n");
	fprintf(stderr, "                                    などにこのｵﾌﾟｼｮﾝを使って縮小すると､実行時､明るい\n");
	fprintf(stderr, "                                    背景画像にうまく溶け込むﾃｷｽﾄ画像を作成できます｡\n");
	fprintf(stderr, "                                    このｵﾌﾟｼｮﾝを指定しなければ､透明ﾋﾟｸｾﾙは加重0です｡\n");
	fprintf(stderr, "                          -b3       白一色のﾃｷｽﾄ画像と暗い背景色の組合せの場合はこう｡\n");
	fprintf(stderr, "                          -b15      同上の組合せで､16階調用の白ﾃｷｽﾄ画像の場合は､こう｡\n");
	fprintf(stderr, "    -c不透明色数     <例> -c2       ｶﾗｰ2とそれ以降を透明色と見なします｡(1bitﾏｽｸ形式)\n");
	fprintf(stderr, "                          -c4       ｶﾗｰ4とそれ以降を透明色と見なします｡(2bitﾏｽｸ形式)\n");
	fprintf(stderr, "                          -c16      ｶﾗｰ16とそれ以降を透明色と見なします｡(4bitﾏｽｸ形式)\n");
	fprintf(stderr, "                          -c256     透明色無しと見なします。(1bit､2bit､4bit形式)\n");
	fprintf(stderr, "                                    このｵﾌﾟｼｮﾝを指定しなければ､ｸﾞﾚｰｽｹｰﾙでない最初の\n");
	fprintf(stderr, "                                    色とそれ以降を透明色と見なして､自動検出します｡\n");
	fprintf(stderr, "                                    通常は､このｵﾌﾟｼｮﾝを指定する必要はありません｡\n");
	fprintf(stderr, "                                    何らかの理由で､不透明色に仮にｸﾞﾚｰｽｹｰﾙ以外の色を\n");
	fprintf(stderr, "                                    設定しているような､特殊な場合に指定してください｡\n");
//{{2017/05/21追加:'-r'オプションを追加しました。
//	fprintf(stderr, "    -rﾘﾏｯﾌﾟ諧調数    <例> -r2       2諧調のﾊﾟﾚｯﾄを作成し､元画像のｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙをﾘﾏｯﾌﾟします｡\n");
//	fprintf(stderr, "                                    元画像の非ｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙは透明と見なして色番号2にﾘﾏｯﾌﾟします｡\n");
//	fprintf(stderr, "                          -r4       4諧調のﾊﾟﾚｯﾄを作成し､元画像のｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙをﾘﾏｯﾌﾟします｡\n");
//	fprintf(stderr, "                                    元画像の非ｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙは透明と見なして色番号4にﾘﾏｯﾌﾟします｡\n");
//	fprintf(stderr, "                          -r16      16諧調のﾊﾟﾚｯﾄを作成し､元画像のｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙをﾘﾏｯﾌﾟします｡\n");
//	fprintf(stderr, "                                    元画像の非ｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙは透明と見なして色番号16にﾘﾏｯﾌﾟします｡\n");
//	fprintf(stderr, "                          -r256     256諧調のﾊﾟﾚｯﾄを作成し､元画像のｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙをﾘﾏｯﾌﾟします｡\n");
//	fprintf(stderr, "                                    元画像の非ｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙが存在していたらｴﾗｰ停止します｡\n");
//	fprintf(stderr, "                     -cｵﾌﾟｼｮﾝと-rｵﾌﾟｼｮﾝを同時に指定する事は出来ません｡\n");
//↓2017/05/22変更:'-r'オプションの内部動作を変更しました。
	fprintf(stderr, "    -rﾘﾏｯﾌﾟ諧調数[d] <例> -r2       2諧調のﾊﾟﾚｯﾄを作成し､元画像のｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙをﾘﾏｯﾌﾟします｡\n");
	fprintf(stderr, "                                    元画像の非ｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙは透明と見なして色番号2にﾘﾏｯﾌﾟします｡\n");
	fprintf(stderr, "                          -r4       4諧調のﾊﾟﾚｯﾄを作成し､元画像のｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙをﾘﾏｯﾌﾟします｡\n");
	fprintf(stderr, "                                    元画像の非ｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙは透明と見なして色番号4にﾘﾏｯﾌﾟします｡\n");
	fprintf(stderr, "                          -r16      16諧調のﾊﾟﾚｯﾄを作成し､元画像のｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙをﾘﾏｯﾌﾟします｡\n");
	fprintf(stderr, "                                    元画像の非ｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙは透明と見なして色番号16にﾘﾏｯﾌﾟします｡\n");
	fprintf(stderr, "                          -r256     256諧調のﾊﾟﾚｯﾄを作成し､元画像のｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙをﾘﾏｯﾌﾟします｡\n");
	fprintf(stderr, "                                    元画像の非ｸﾞﾚｰｽｹｰﾙﾋﾟｸｾﾙが存在していたらｴﾗｰ停止します｡\n");
	fprintf(stderr, "                     ※諧調数の後に'd'を付けると､ﾘﾏｯﾌﾟ処理時にﾃﾞｨｻﾞﾘﾝｸﾞを行います｡(例:'-r4d')\n");
	fprintf(stderr, "                     ※-cｵﾌﾟｼｮﾝと-rｵﾌﾟｼｮﾝを同時に指定する事は出来ません｡\n");
//}}2017/05/22変更:'-r'オプションの内部動作を変更しました。
	fprintf(stderr, "    -q               <例> -q     画像情報を表示しません｡\n");
	exit(1);
}

/****************************************************************************
 *	cmdline
 ****************************************************************************/

static void
cmdline_whs(char* arg, int* p1, int* p2)
{
	if(*p1 || *p2) usage();
//{{2017/05/25追加:'-w',及び,'-h'オプションに、auto指定を追加しました。
	if(!strcmp(arg, "auto")) {
		*p1 = -1;
		return;	//ここまで
	}
//}}2017/05/25追加:'-w',及び,'-h'オプションに、auto指定を追加しました。
	*p1 = strtol(arg, &arg, 10);
	if(*p1 <= 0) usage();
	if(*arg) {
		switch(*arg) {
		case '%':
			arg++;
			*p2 = 100;
			break;
		case '/':
			arg++;
			*p2 = strtol(arg, &arg, 10);
			if(*p2 <= 0) usage();
			break;
		//default:
		//	//↓すぐ下でまとめてエラー扱い
		}
		if(*arg) usage(); /* 余分な後続文字 */
	}
}

void
cmdline(int argc, char* argv[])
{
	int i;
	char opt;
	char* arg;
	char drive[_MAX_DRIVE];
	char dir[_MAX_DIR];
	char fname[_MAX_FNAME];
	char ext[_MAX_EXT];

	for(i = 1; i < argc; i++) {
		arg = argv[i];
		if(*arg == '-' ||
		   *arg == '/') {
			arg++;
			opt = *arg++;
			switch(opt) {
			case 'w':
				cmdline_whs(arg, &opt_w1, &opt_w2);
				break;
			case 'h':
				cmdline_whs(arg, &opt_h1, &opt_h2);
				break;
			case 's':
				cmdline_whs(arg, &opt_w1, &opt_w2);
				cmdline_whs(arg, &opt_h1, &opt_h2);
				break;
			case 'b':
				if(opt_b) usage();
				opt_b = strtol(arg, &arg, 10) + 1;	/* カラー0と未設定を区別するために、+1しておきます */
				if((opt_b < 1) || (opt_b > 256) || *arg) usage();
				break;
			case 'c':
				if(opt_c) usage();
				opt_c = strtol(arg, &arg, 10);
				if((opt_c < 1) || (opt_c > 256) || *arg) usage();
				break;
//{{2017/05/21追加:'-r'オプションを追加しました。
			case 'r':
				if(opt_r) usage();
				opt_r = strtol(arg, &arg, 10);
				if(*arg == 'd') { opt_r_d = 1; arg++; }	//諧調数の後に'd'を付けると､ﾘﾏｯﾌﾟ処理時にﾃﾞｨｻﾞﾘﾝｸﾞを行います｡(例:'-r4d')	//{{2017/05/22追加:'-r'オプションの内部動作を変更しました。}}
				if((opt_r < 1) || (opt_r > 256) || *arg) usage();
				break;
//}}2017/05/21追加:'-r'オプションを追加しました。
			case 'q':
				if(opt_q) usage();
				opt_q = 1;
				if(*arg) usage();
				break;
			default:
				usage();	/* 不明なオプション */
			}
		} else {
			_splitpath(arg, drive, dir, fname, ext);
			if(!*src_path) {							/* 入力ファイル名が未設定ならば... */
				_makepath(src_path, drive, dir, fname, *ext ? ext : ".bmp");	/* 入力ファイル名に格納します。 */
			} else if(!*dst_path) {							/* 出力ファイル名が未設定ならば... */
				_makepath(dst_path, drive, dir, fname, *ext ? ext : ".bmp");	/* 出力ファイル名に格納します。 */
			} else {
				usage();	/* ファイル名が三つ以上指定されてました。不正なコマンドライン書式です。 */
			}
		}
	}
//{{2017/05/21追加:'-r'オプションを追加しました。
	if(opt_c && opt_r) { err("-cｵﾌﾟｼｮﾝと-rｵﾌﾟｼｮﾝを同時に指定する事は出来ません｡"); }
//}}2017/05/21追加:'-r'オプションを追加しました。
	if(!*src_path || !*dst_path) usage();	/* 入力ファイル名、または、出力ファイル名が指定されていません。 */
	if(!strcmpi(src_path, dst_path)) err("入力ﾌｧｲﾙ名と出力ﾌｧｲﾙ名に同じﾌｧｲﾙ名を指定しないでください");
}

/****************************************************************************
 *	loadbmp
 ****************************************************************************/

void
loadbmp()
{
	FILE* fp;

	int retval;
	int i;
	int y;
	int pitch;
	BITMAPFILEHEADER bf;
	BITMAPINFOHEADER bi;

	/* 入力ファイルを開きます。 */
	fp = fopen(src_path, "rb");
	if(!fp) err("入力ﾌｧｲﾙが開けません");

	/* ファイルヘッダを読み込みます。 */
	retval = fread(&bf, 1, sizeof(BITMAPFILEHEADER), fp);
	if(retval != sizeof(BITMAPFILEHEADER)) err("ﾌｧｲﾙﾍｯﾀﾞが不正です");
	if(memcmp(&bf.bfType, "BM", 2)) err("ﾌｧｲﾙﾍｯﾀﾞが不正です");

	/* ビットマップヘッダを読み込みます。 */
	retval = fread(&bi, 1, sizeof(BITMAPINFOHEADER), fp);
	if(retval != sizeof(BITMAPINFOHEADER)) err("ﾋﾞｯﾄﾏｯﾌﾟﾍｯﾀﾞが不正です");
	if(bi.biSize < sizeof(BITMAPINFOHEADER)) err("ﾋﾞｯﾄﾏｯﾌﾟﾍｯﾀﾞが不正です");
	if(bi.biPlanes != 1) err("256色BMP形式以外は未対応です");
	if(bi.biBitCount != 8) err("256色BMP形式以外は未対応です");
	if(bi.biCompression != BI_RGB) err("256色BMP形式以外は未対応です");

	/* パレットを読み込みます。 */
	i = bi.biClrUsed != 0 ? bi.biClrUsed
	                      : bi.biPlanes << bi.biBitCount;
	if(i < 0 || 256 < i) err("ﾊﾟﾚｯﾄが不正です");
	retval = fread(pal, sizeof(RGBQUAD), i, fp);
	if(retval != i) err("ﾊﾟﾚｯﾄが不正です");

	/* グレイスケールでない最初の色とそれ以降を、透明色と見なします。 */
	if(opt_c) {
		trans = opt_c;
	} else {
		for(i = 0; i < 256; i++) {
			if(pal[i].rgbRed != pal[i].rgbGreen ||
			   pal[i].rgbRed != pal[i].rgbBlue) {
				break;
			}
		}
		trans = i; /* 透明色無しならば256になります */
	}

	/* 入力画像サイズを求めます。 */
	src_w = bi.biWidth;
	src_h = bi.biHeight;
	if((src_w > MAX_SIZE) || (src_h > MAX_SIZE)) err("入力画像が大きすぎます");

	/* ピッチを求めます。 */
	pitch = (src_w + 3) & ~3; /* 仕様 */

	/* 入力画像を読み込みます。 */
	retval = fseek(fp, bf.bfOffBits, SEEK_SET);
	if(retval) err("ｼｰｸｴﾗｰです");
	for(y = src_h - 1; y >= 0; y--) { /* ボトムアップ */
		retval = fread(&src_bits[y][0], 1, pitch, fp);
		if(retval != pitch) err("読み込みｴﾗｰです");
	}

	/* 入力ファイルを閉じます。 */
	fclose(fp);

//{{2017/05/21追加:'-r'オプションを追加しました。
	//'-r'オプションが指定されていたら…
	if(opt_r) {
		int x, y, i;
		//入力画像の各ピクセルについて…
		for(y = 0; y < src_h; y++) {
			for(x = 0; x < src_w; x++) {
				i = src_bits[y][x];
				//グレイスケールならば不透明と見なす。
				if(pal[i].rgbRed == pal[i].rgbGreen &&
				   pal[i].rgbRed == pal[i].rgbBlue) {
	//{{2017/05/22変更:'-r'オプションの内部動作を変更しました。
	//				//opt_r諧調のグレースケールパレットに対するインデクスに変換する。
	//				i = opt_r * pal[i].rgbRed / 255;	//[0,255]を[0,opt_r-1]へ均等に変換する。				┬/clip/tool/dpavidot4/app.cの、[0,255]を[1,31]へ均等に変換する処理と、同様のアルゴリズムです。
	//				i = min(i, opt_r - 1);			//変換前が255だった場合のみ変換結果がopt_rになるのでopt_r-1に補正する。	┘
	//				i =       (opt_r - 1) - i;		//[0,opt_r-1]を[opt_r-1,0]に反転する。
	//↓2017/05/22変更:'-r'オプションの内部動作を変更しました。
					//擬似的に255諧調に変換する。
					i = pal[i].rgbRed;			//i = 0〜255
					i = (int)(i * 254.0 / 255.0 + 0.5);	//i = 0〜254
					i = 254 - i;				//i = 254〜0
	//}}2017/05/22変更:'-r'オプションの内部動作を変更しました。
					src_bits[y][x] = i;
				//グレイスケールでなければ透明と見なす。
				} else {
					//透明パレットのエントリを指すように変換する。
	//{{2017/05/22変更:'-r'オプションの内部動作を変更しました。
	//				if(opt_r > 255) { err("透明なﾋﾟｸｾﾙが存在してしますが､透明ﾊﾟﾚｯﾄ用のｴﾝﾄﾘが足りません｡-r255以下を指定して下さい｡"); }
	//				src_bits[y][x] = opt_r;
	//↓2017/05/22変更:'-r'オプションの内部動作を変更しました。
					src_bits[y][x] = 255;
	//}}2017/05/22変更:'-r'オプションの内部動作を変更しました。
				}
			}
		}
	//{{2017/05/22追加:'-r'オプションの内部動作を変更しました。
		//'-r'が指定された場合、リサンプル処理は擬似的な255諧調に対して行い、その際、255を透明色と見なす。
		trans = 255;
	//}}2017/05/22追加:'-r'オプションの内部動作を変更しました。
		//opt_r諧調のグレースケールパレットを作成する。		//{{2017/05/22コメント追記:この時点では入力画像はopt_r諧調ではなく擬似的な255諧調になっていますが、リサンプル処理においてはパレットを参照しないので、この時点でopt_r諧調のグレースケールパレットを作成してしまって問題有りません。もし今後処理を変更する場合は、'-r'オプションを指定した場合のリサンプル処理においては、入力画像の色番号(擬似的な255諧調)とパレットの内容(opt_r諧調)が一致していない事に注意して下さい。}}
		for(i = 0; i < 256; i++) {
			//0〜opt_r-1をグレースケールにする。
			if(i < opt_r) {
				pal[i].rgbRed   = (opt_r > 1) ? (255 - (255 * i / (opt_r - 1))) : 255;
				pal[i].rgbGreen = pal[i].rgbRed;
				pal[i].rgbBlue  = pal[i].rgbRed;
			//opt_r〜255を緑にする。(グレースケール以外の色ならば緑以外の色でも構わない。)
			} else {
				pal[i].rgbRed   = 0;
				pal[i].rgbGreen = 255;
				pal[i].rgbBlue  = 0;
			}
		}
	//{{2017/05/22削除:'-r'オプションの内部動作を変更しました。
	//	//opt_r以降を透明色と見なす。
	//	trans = opt_r;
	//}}2017/05/22削除:'-r'オプションの内部動作を変更しました。
	}
//}}2017/05/21追加:'-r'オプションを追加しました。
}

/****************************************************************************
 *	resample
 ****************************************************************************/

/* 伸縮アルゴリズムは、CLiPライブラリのsurface_stretch()関数と同じです。
 * surface_stretch()関数のコメントを参照してください。
 * ↓Sat Jan 13 19:45:45 JST 2007 Naoyuki Sawa
 * 3ピクセル→50%→1.5ピクセル縮小のような、サブピクセル単位の処理に変更したため、
 * surface_stretch()関数とは、やや、アルゴリズムが異なるようになりました。
 */
void
resample()
{
	unsigned __int64 dx;
	unsigned __int64 dy;
	unsigned __int64 sx;
	unsigned __int64 sy;
	unsigned __int64 x1;
	unsigned __int64 x2;
	unsigned __int64 x3;
	unsigned __int64 y1;
	unsigned __int64 y2;
	unsigned __int64 y3;
	unsigned __int64 xy;
	unsigned __int64 c1;
	unsigned __int64 c2;
	unsigned __int64 save_sx;
	unsigned __int64 save_sy;
	unsigned __int64 save_x1;
	unsigned __int64 save_y1;
	int opaque;
	int c;

	/*{{転送先の垂直ループ(転送先1ピクセルづつ)*/
	sy = 0;
	y1 = opt_h1;
	for(dy = 0; dy < dst_h; dy++) {
		/*{{転送先の水平ループ(転送先1ピクセルづつ)*/
		sx = 0;
		x1 = opt_w1;
		save_sy = sy;
		save_y1 = y1;
		for(dx = 0; dx < dst_w; dx++) {
			sy = save_sy;
			y1 = save_y1;
			opaque = 0;
			c1 = 0;
			c2 = 0;
			/*{{転送元の垂直ループ(転送先1ピクセルにかかる範囲の転送元ピクセルの分)*/
			y2 = opt_h2;
			save_sx = sx;
			save_x1 = x1;
			for(;;) {
				sx = save_sx;
				x1 = save_x1;
				y3 = y1 < y2 ? y1 : y2;
				/*{{転送元の水平ループ(転送先1ピクセルにかかる範囲の転送元ピクセルの分)*/
				x2 = opt_w2;
				for(;;) {
					x3 = x1 < x2 ? x1 : x2;
					xy = x3 * y3;
					if((sx < src_w) && (sy < src_h)) {
						c = src_bits[sy][sx];
					} else {
						//{{2007/01/18削除
						///* たとえば、3ピクセル画像を"-s50%"で2ピクセルに縮小する場合に、
						// * 元画像の4ピクセル目を参照するので、透明と見なします。
						// * 元画像の5ピクセル目以降を参照することは有り得ません。
						// * もしそうなったら、アルゴリズムが誤っています。
						// */
						//if(((int)sx > src_w) || ((int)sy > src_h)) err("resample()関数の内部ｴﾗｰです");
						//}}2007/01/18削除
						// * Thu Jan 18 01:34:05 JST 2007 Naoyuki Sawa
						// - resample()関数のアルゴリズムチェックコード自体が誤っていたので、チェックコードを削除しました。
						//   前バージョンでは、正しい処理に対してもアサートしてしまうという不具合が発生していました。
						//   今回のチェックコード削除により、無用なアサートを回避しました。
						// - 具体的には、上述のアルゴリズムに関する考察が間違っていました。
						//   2ピクセル以上外側を参照することも、充分に有り得ます。
						//   たとえば、32ピクセル→20%縮小の場合、有効イメージ6.4ピクセルに対して7ピクセルのキャンバスとなりますから、
						//   32×(7÷6.4)=35、すなわち、元画像の35ピクセル目まで参照します。(これは正しい動作です)
						// - そんなわけで、このチェックコード自体が誤りであり、正しい処理に対してアサートしてしまうことがありました。
						//   よって、チェックコードを削除しました。
						c = trans;
					}
					if(c < trans) {
						opaque = 1;
						c1 += xy;
						c2 += xy * c;
					} else {
						if(opt_b) {
							c1 += xy;
							c2 += xy * (opt_b - 1);
						}
					}
					if(!(x1 -= x3)) {
						x1 += opt_w1;
						sx++;
					}
					if(!(x2 -= x3)) {
						break;
					}
				}
				/*}}転送元の水平ループ(転送先1ピクセルにかかる範囲の転送元ピクセルの分)*/
				if(!(y1 -= y3)) {
					y1 += opt_h1;
					sy++;
				}
				if(!(y2 -= y3)) {
					break;
				}
			}
			/*}}転送元の垂直ループ(転送先1ピクセルにかかる範囲の転送元ピクセルの分)*/
			if(opaque) {
				c = (int)((c2 + (c1 / 2)) / c1);
			} else {
				c = trans;
			}
			dst_bits[dy][dx] = c;
		}
		/*}}転送先の水平ループ(転送先1ピクセルづつ)*/
	}
	/*}}転送先の垂直ループ(転送先1ピクセルづつ)*/
}

/****************************************************************************
 *	savebmp
 ****************************************************************************/

void
savebmp()
{
	FILE* fp;

	int retval;
	int y;
	int pitch;
	BITMAPFILEHEADER bf;
	BITMAPINFOHEADER bi;

//{{2017/05/22追加:'-r'オプションの内部動作を変更しました。
	//'-r'オプションが指定されていたら…
	if(opt_r) {
		//□C:/Home/Share/Piece/clip/tool/dpbmpcnv/app.cからコピーして来ました。
		static const int DITHER1[16][16] = {
			  0,128, 32,160,  8,136, 40,168,  2,130, 34,162, 10,138, 42,170,
			192, 64,224, 96,200, 72,232,104,194, 66,226, 98,202, 74,234,106,
			 48,176, 16,144, 56,184, 24,152, 50,178, 18,146, 58,186, 26,154,
			240,112,208, 80,248,120,216, 88,242,114,210, 82,250,122,218, 90,
			 12,140, 44,172,  4,132, 36,164, 14,142, 46,174,  6,134, 38,166,
			204, 76,236,108,196, 68,228,100,206, 78,238,110,198, 70,230,102,
			 60,188, 28,156, 52,180, 20,148, 62,190, 30,158, 54,182, 22,150,
			252,124,220, 92,244,116,212, 84,254,126,222, 94,246,118,214, 86,
			  3,131, 35,163, 11,139, 43,171,  1,129, 33,161,  9,137, 41,169,
			195, 67,227, 99,203, 75,235,107,193, 65,225, 97,201, 73,233,105,
			 51,179, 19,147, 59,187, 27,155, 49,177, 17,145, 57,185, 25,153,
			243,115,211, 83,251,123,219, 91,241,113,209, 81,249,121,217, 89,
			 15,143, 47,175,  7,135, 39,167, 13,141, 45,173,  5,133, 37,165,
			207, 79,239,111,199, 71,231,103,205, 77,237,109,197, 69,229,101,
			 63,191, 31,159, 55,183, 23,151, 61,189, 29,157, 53,181, 21,149,
			255,127,223, 95,247,119,215, 87,253,125,221, 93,245,117,213, 85,
		};
		int x, y, i;
		//出力画像の各ピクセルについて…
		for(y = 0; y < dst_h; y++) {
			for(x = 0; x < dst_w; x++) {
				i = dst_bits[y][x];						//i = 0〜255
				//不透明ピクセルならば…
				if(i < trans/*255*/) {
					double d = 0.5;						//i = 0.5
					if(opt_r_d) { d = DITHER1[y & 15][x & 15] / 256.0; }	//i = 0〜255/256
					i = (int)(i * (opt_r - 1) / 254.0 + d);			//i = 0〜opt_r-1
					dst_bits[y][x] = i;
				//透明ピクセルならば…
				} else {
					if(opt_r > 255) { err("透明なﾋﾟｸｾﾙが存在してしますが､透明ﾊﾟﾚｯﾄ用のｴﾝﾄﾘが足りません｡-r255以下を指定して下さい｡"); }
					dst_bits[y][x] = opt_r;
				}
			}
		}
		//opt_r以降を透明色と見なす。
		trans = opt_r;	//もうこの後でtransを参照する事は無いので(trans==255)のままでも構わないが、ソースコードの判り易さのために設定しておく事にした。
	}
//}}2017/05/22追加:'-r'オプションの内部動作を変更しました。

	/* 出力ファイルを作成します。 */
	fp = fopen(dst_path, "wb");
	if(!fp) err("出力ﾌｧｲﾙが作成できません");

	/* ピッチを求めます。 */
	pitch = (dst_w + 3) & ~3; /* 仕様 */

	/* ファイルヘッダを書き込みます。 */
	memset(&bf, 0, sizeof(BITMAPFILEHEADER));
	memcpy(&bf.bfType, "BM", 2);
	bf.bfSize = sizeof(BITMAPFILEHEADER)
		  + sizeof(BITMAPINFOHEADER)
		  + sizeof(RGBQUAD) * 256
		  + pitch * dst_h;
	bf.bfOffBits = sizeof(BITMAPFILEHEADER)
		     + sizeof(BITMAPINFOHEADER)
		     + sizeof(RGBQUAD) * 256;
	retval = fwrite(&bf, 1, sizeof(BITMAPFILEHEADER), fp);
	if(retval != sizeof(BITMAPFILEHEADER)) err("書き込みｴﾗｰです");

	/* ビットマップヘッダを書き込みます。 */
	memset(&bi, 0, sizeof(BITMAPINFOHEADER));
	bi.biSize = sizeof(BITMAPINFOHEADER);
	bi.biWidth = dst_w;
	bi.biHeight = dst_h;
	bi.biPlanes = 1;
	bi.biBitCount = 8;
	retval = fwrite(&bi, 1, sizeof(BITMAPINFOHEADER), fp);
	if(retval != sizeof(BITMAPINFOHEADER)) err("書き込みｴﾗｰです");

	/* パレットを書き込みます。 */
	retval = fwrite(pal, sizeof(RGBQUAD), 256, fp);
	if(retval != 256) err("書き込みｴﾗｰです");

	/* 出力画像を書き込みます。 */
	for(y = dst_h - 1; y >= 0; y--) { /* ボトムアップ */
		retval = fwrite(&dst_bits[y][0], 1, pitch, fp);
		if(retval != pitch) err("書き込みｴﾗｰです");
	}

	/* 出力ファイルを閉じます。 */
	fclose(fp);
}

