/*	
 *	clippnm.c
 *
 *	Netpbm format
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2016 Naoyuki Sawa
 *
 *	* Sat Apr 09 21:06:53 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 *	- 画像を、PBMテキスト形式(P1),又は,PGMテキスト形式(P2),又は,PPMテキスト形式(P3)で保存します。
 *	  直接ファイルに保存する関数(〜_save())と、ストリームに出力する関数(〜_dump()を用意しました。
 *	- 当モジュールを作成した主な目的は、P/ECEの画面を簡単に保存するためです。
 *	  BMP形式やTGA形式等で保存するよりも、PNM形式で保存する方が簡単でコード量が少なく出来るので、PNM形式を選択しました。
 *	  想定する使い方は、P/ECEのフラッシュメモリ上にファイルとして保存するか、又は、標準エラー出力でUSB経由で出力します。
 *	  その後、ImageMagick等を使って、PNM形式よりも汎用的な形式(BMP形式やPNG形式等)へ変換して下さい。
 *	- P/ECEの画像形式に適しているのはPGM形式ですから、実際にはほとんどpgm_save(),又は,pgm_dump()しか使わないと思います。
 *	  しかし、PBM形式とPPM形式もほとんど同じ処理で対応出来るので、PBM形式とPPM形式も対応しておく事にしました。
 *	- PNM形式で保存する事が目的であり、読み込む事は目的ではないので、PNM形式の読み込み関数は作成していません。
 *	- □参照資料「Wikipedia - Netpbm format」(http://en.wikipedia.org/wiki/Netpbm_format)
 *	  │A Netpbm format is any graphics format used and defined by the Netpbm project.
 *	  │The portable pixmap format (PPM), the portable graymap format (PGM) and the portable bitmap format (PBM) are image file formats designed to be easily exchanged between platforms.
 *	  │They are also sometimes referred to collectively as the portable anymap format (PNM), not to be confused with the related portable arbitrary map format.
 */
#include "clip.h"
/*****************************************************************************
 *	ローカル関数
 *****************************************************************************/
static char* complete_extension(char* dstPath, const char* srcPath, const char* defExt) {
	char drive[MAX_DRIVE], dir[MAX_DIR], fname[MAX_FNAME], ext[MAX_EXT];
	splitpath(srcPath, drive, dir, fname, ext);
	makepath(dstPath, drive, dir, fname, (*ext ? ext : defExt));
	return dstPath;
}
/*****************************************************************************
 *	Portable bitmap format (PBM)
 *****************************************************************************/
void pbm_save(const char* path/*拡張子省略可*/, int w, int h, int (*fnPoint)(int x, int y, void* arg), void* arg) {
	char tmpPath[MAX_PATH];
	FILE* fp = fopen(complete_extension(tmpPath, path, "pbm"), "w");
	if(!fp) { DIE(); }
	pbm_dump(fp, w, h, fnPoint, arg);
	fclose(fp);
}
/*---------------------------------------------------------------------------*/
void pbm_dump(FILE* fp, int w, int h, int (*fnPoint)(int x, int y, void* arg), void* arg) {
	int x, y;
	fprintf(fp, "P1\n");
	fprintf(fp, "%d %d\n", w, h);
	for(y = 0; y < h; y++) {
		for(x = 0; x < w; x++) {
			int c = (*fnPoint)(x, y, arg);
			if((unsigned)c > 1) { c = 1; }
			if(x) { fputc(' ', fp); }
			fprintf(fp, "%d", c);
		}
		fputc('\n', fp);
	}
}
/*****************************************************************************
 *	Portable graymap format (PGM)
 *****************************************************************************/
void pgm_save(const char* path/*拡張子省略可*/, int w, int h, int maxVal, int (*fnPoint)(int x, int y, void* arg), void* arg) {
	char tmpPath[MAX_PATH];
	FILE* fp = fopen(complete_extension(tmpPath, path, "pgm"), "w");
	if(!fp) { DIE(); }
	pgm_dump(fp, w, h, maxVal, fnPoint, arg);
	fclose(fp);
}
/*---------------------------------------------------------------------------*/
void pgm_dump(FILE* fp, int w, int h, int maxVal, int (*fnPoint)(int x, int y, void* arg), void* arg) {
	int x, y, maxLen = (int)log10(maxVal + 0.5) + 1;
	//                                     ~~~誤差を吸収するために必要です。こうしないと'(int)log10(10)=0'になってしまいます。0.5足しておけば'(int)log10(10.5)=1'になります。
	fprintf(fp, "P2\n");
	fprintf(fp, "%d %d\n", w, h);
	fprintf(fp, "%d\n", maxVal);
	for(y = 0; y < h; y++) {
		for(x = 0; x < w; x++) {
			int c = (*fnPoint)(x, y, arg);
			if((unsigned)c > (unsigned)maxVal) { c = maxVal; }
			if(x) { fputc(' ', fp); }
			fprintf(fp, "%*d", maxLen, c);
		}
		fputc('\n', fp);
	}
}
/*****************************************************************************
 *	Portable pixmap format (PPM)
 *****************************************************************************/
void ppm_save(const char* path/*拡張子省略可*/, int w, int h, int maxVal, int (*fnPoint)(int x, int y, void* arg), void* arg) {
	char tmpPath[MAX_PATH];
	FILE* fp = fopen(complete_extension(tmpPath, path, "ppm"), "w");
	if(!fp) { DIE(); }
	ppm_dump(fp, w, h, maxVal, fnPoint, arg);
	fclose(fp);
}
/*---------------------------------------------------------------------------*/
void ppm_dump(FILE* fp, int w, int h, int maxVal, int (*fnPoint)(int x, int y, void* arg), void* arg) {
	int x, y, maxLen = (int)log10(maxVal + 0.5) + 1;
	//                                     ~~~誤差を吸収するために必要です。こうしないと'(int)log10(10)=0'になってしまいます。0.5足しておけば'(int)log10(10.5)=1'になります。
	fprintf(fp, "P3\n");
	fprintf(fp, "%d %d\n", w, h);
	fprintf(fp, "%d\n", maxVal);
	for(y = 0; y < h; y++) {
		for(x = 0; x < w; x++) {
			int c = (*fnPoint)(x, y, arg);
			int r = (unsigned char)(c >> 16);
			int g = (unsigned char)(c >>  8);
			int b = (unsigned char)(c >>  0);
			if((unsigned)r > (unsigned)maxVal) { r = maxVal; }
			if((unsigned)g > (unsigned)maxVal) { g = maxVal; }
			if((unsigned)b > (unsigned)maxVal) { b = maxVal; }
			if(x) { fputc(' ', fp); }
			fprintf(fp, "%*d %*d %*d", maxLen, r, maxLen, g, maxLen, b);
		}
		fputc('\n', fp);
	}
}
/*****************************************************************************
 *	使用例
 *****************************************************************************/
#if 0
//下記の例はサーフェイス全体を保存する例ですが、一部分だけを保存する事も出来ます。
#ifndef LDIRECT_INTERVAL
int fnPoint(int x, int y, void* arg) { return  3 - surface_point(arg, x, y); }
void test() { pgm_save("test", surface.w, surface.h,  3, fnPoint, &surface); }
#else //LDIRECT_INTERVAL
int fnPoint(int x, int y, void* arg) { return 15 - surface_point(arg, x, y); }
void test() { pgm_save("test", surface.w, surface.h, 15, fnPoint, &surface); }
#endif//LDIRECT_INTERVAL
#endif
