/* * bm2ecsp -- 画像ファイルをESC/P形式に変換 * * revision history: * 0.0: Sep. 14, 2004 by Dai ISHIJIMA * 0.1: Sep. 16, 2004 BMPファイル対応 (FS誤差拡散など) * 0.2: Sep. 19, 2004 PGM, PPMファイル対応 */ #include #include #include #define EOS '\0' /* 量子化法 (BMPを変換するとき) */ #define FLOYD_STEINBERG 'f' #define RANDOM_DITHER 'r' /* デバッグ */ #define PBM_OUTPUT 1 /* ファイル識別 */ #define MAGIC_LENGTH 3 #define MAGIC_BMP "BM6" #define MAGIC_PBM "P4\n" #define MAGIC_PGM "P5\n" #define MAGIC_PPM "P6\n" /* なんとBMP形式は寸法その他を決め打ちだ! (苦笑) */ #define BMPWIDTH 480 #define BMPHEIGHT 640 #define BMPFILSIZ 921654 #define BMPSKIP (BMPFILSIZ - 3 * BMPWIDTH * BMPHEIGHT - MAGIC_LENGTH) #define BMPMAXVAL 255 /* プリンタの縦ドット数 */ #define NBITS 24 /* プリンタ初期化 */ #define PINIT "\x1b@\x1bx1\x1b\x33\x18" /* ESC @ ESC x 1 ESC 3 24 */ #define BITIMG "\x1b*\x27" /* ビットイメージモード */ char *prog; #define shift --argc; ++argv /* PBMファイルを一度に印刷する分だけ読む */ int fillpbm(FILE *fp, unsigned char *bitmap, int xbytes, int height) { bzero(bitmap, xbytes * height); return(fread(bitmap, xbytes, height, fp)); } /* PBM形式データをESC/P形式に変換して出力 (1行分) */ void putpbm(unsigned char *bitmap, int width, int height) { int xbytes; int x, y; int xmask, ymask; int bits; xbytes = (width + 7) / 8; printf("%s%c%c", BITIMG, width & 255, width / 256); xmask = 0x80; for (x = 0; x < width; x++) { bits = 0; ymask = 0x80; for (y = 0; y < height; y++) { if (bitmap[y * xbytes + x / 8] & xmask) { bits |= ymask; } ymask >>= 1; if (ymask == 0) { putc(bits, stdout); ymask = 0x80; bits = 0; } } xmask >>= 1; if (xmask == 0) { xmask = 0x80; } } printf("\r\n"); } /* デバッグ用 (量子化の確認など) にPBM形式データをそのまま出力 */ void debug_putpbm(unsigned char *bitmap, int width, int height) { fwrite(bitmap, (width + 7) / 8, height, stdout); } /* PBMファイルを読んで、ESC/P形式に変換 */ void pbmtoescp(FILE *fp, int debug) { char s[BUFSIZ]; int width, height; int xbytes; int y; unsigned char *bitmap; fgets(s, BUFSIZ, fp); if (sscanf(s, "%d %d", &width, &height) != 2) { fprintf(stderr, "%s: can't get image size\n", prog); exit(1); } xbytes = (width + 7) / 8; bitmap = (unsigned char *)calloc(xbytes, NBITS); if (debug & PBM_OUTPUT) { printf("P4\n%d %d\n", width, height); } else { printf(PINIT); } for (y = 0; y < height; y += NBITS) { if (fillpbm(fp, bitmap, xbytes, NBITS) <= 0) { break; } if (debug & PBM_OUTPUT) { debug_putpbm(bitmap, width, NBITS); } else { putpbm(bitmap, width, NBITS); } } free(bitmap); } /* RGBをグレースケールに変換 */ #define rgb2gray(r,g,b,m) (( (double)(r) * 0.30 \ + (double)(g) * 0.59 \ + (double)(b) * 0.11) / (double)(m)) /* グレースケールマップをクリアする */ void cleargraymap(float *graymap, int width, int height) { int x, y; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { graymap[width * y + x] = 0.0; } } } /* 一度に印刷する分だけBMPファイルを読んで、グレースケールに変換 */ int fillbmp(FILE *fp, float *graymap, int width, int height, int maxval) { int x, y; int r, g, b; int n; n = 0; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { if ((r = getc(fp)) != EOF) { /* エエ加減なチェック (^^; */ ++n; g = getc(fp); b = getc(fp); /* 左右を反転して格納する */ graymap[width * y + (width - x - 1)] += rgb2gray(r, g, b, maxval); } else { /* データがないときは「白」にしておく */ graymap[width * y + (width - x - 1)] = 1.0; } } } return(n); } /* 誤差拡散法 (Floyd-Steinberg) で二値化してPBM形式に変換 */ void fs_gray2pbm(float *graymap, unsigned char *pbm, int width, int height) { int xbytes; int x, y; float val, error; int bit; xbytes = (width + 7) / 8; bzero(pbm, xbytes * height); for (y = 0; y < height; y++) { bit = 0x80; for (x = 0; x < width; x++) { if (graymap[y * width + x] > 0.5) { val = 1.0; } else { val = 0.0; pbm[xbytes * y + (x / 8)] |= bit; } bit >>= 1; if (bit <= 0) { bit = 0x80; } error = graymap[y * width + x] - val; if (x < width - 1) { graymap[y * width + x + 1] += error * 7.0 / 16.0; graymap[(y + 1) * width + x + 1] += error * 1.0 / 16.0; } if (x > 0) { graymap[(y + 1) * width + x - 1] += error * 3.0 / 16.0; } graymap[(y + 1) * width + x] += error * 5.0 / 16.0; graymap[y * width + x] = 0.0; } } /* 最終列の誤差を次のところにコピー */ for (x = 0; x < width; x++) { graymap[x] = graymap[height * width + x]; graymap[height * width + x] = 0.0; } } /* ランダムディザ法で二値化してPBM形式に変換 */ void rd_gray2pbm(float *graymap, unsigned char *pbm, int width, int height) { int xbytes; int x, y; int bit; float val; xbytes = (width + 7) / 8; bzero(pbm, xbytes * height); for (y = 0; y < height; y++) { bit = 0x80; for (x = 0; x < width; x++) { val = (double)random() / (double)RAND_MAX; if (graymap[y * width + x] < val) { pbm[xbytes * y + (x / 8)] |= bit; } bit >>= 1; if (bit <= 0) { bit = 0x80; } graymap[y * width + x] = 0.0; } } } /* BMPファイルを二値化してESC/P形式に変換 */ void bmptoescp(FILE *fp, int q, int debug) { float *graymap; unsigned char *bitmap; int width, height; int maxval; int y; int xbytes; /* 寸法決め打ち (^^; */ width = BMPWIDTH; height = BMPHEIGHT; maxval = BMPMAXVAL; fseek(fp, BMPSKIP, SEEK_CUR); graymap = (float *)calloc(width * (NBITS + 1), sizeof(float)); cleargraymap(graymap, width, NBITS + 1); xbytes = (width + 7) / 8; bitmap = (unsigned char *)calloc(xbytes, NBITS); if (debug & PBM_OUTPUT) { printf("P4\n%d %d\n", width, height); } else { printf(PINIT); } for (y = 0; y < height; y += NBITS) { if (fillbmp(fp, graymap, width, NBITS, maxval) <= 0) { break; } if (q == RANDOM_DITHER) { rd_gray2pbm(graymap, bitmap, width, NBITS); } else { fs_gray2pbm(graymap, bitmap, width, NBITS); } if (debug & PBM_OUTPUT) { debug_putpbm(bitmap, width, NBITS); } else { putpbm(bitmap, width, NBITS); } } free(bitmap); free(graymap); } /* 一度に印刷する分だけPGMファイルを読んで、グレースケールに変換 */ int fillpgm(FILE *fp, float *graymap, int width, int height, int maxval) { int x, y; int g; int n; n = 0; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { if ((g = getc(fp)) != EOF) { /* エエ加減なチェック (^^; */ ++n; graymap[width * y + x] += (double)g / (double)maxval; } else { /* データがないときは「白」にしておく */ graymap[width * y + (width - x - 1)] = 1.0; } } } return(n); } /* PGMファイルを読んで、ESC/P形式に変換 */ void pgmtoescp(FILE *fp, int q, int debug) { char s[BUFSIZ]; int width, height; int maxval; int xbytes; int y; unsigned char *bitmap; float *graymap; fgets(s, BUFSIZ, fp); if (sscanf(s, "%d %d", &width, &height) != 2) { fprintf(stderr, "%s: can't get image size\n", prog); exit(1); } fgets(s, BUFSIZ, fp); if (sscanf(s, "%d", &maxval) != 1) { fprintf(stderr, "%s: can't get maxval\n", prog); exit(1); } graymap = (float *)calloc(width * (NBITS + 1), sizeof(float)); cleargraymap(graymap, width, NBITS + 1); xbytes = (width + 7) / 8; bitmap = (unsigned char *)calloc(xbytes, NBITS); if (debug & PBM_OUTPUT) { printf("P4\n%d %d\n", width, height); } else { printf(PINIT); } for (y = 0; y < height; y += NBITS) { if (fillpgm(fp, graymap, width, NBITS, maxval) <= 0) { break; } if (q == RANDOM_DITHER) { rd_gray2pbm(graymap, bitmap, width, NBITS); } else { fs_gray2pbm(graymap, bitmap, width, NBITS); } if (debug & PBM_OUTPUT) { debug_putpbm(bitmap, width, NBITS); } else { putpbm(bitmap, width, NBITS); } } free(bitmap); free(graymap); } /* 一度に印刷する分だけPPMファイルを読んで、グレースケールに変換 */ int fillppm(FILE *fp, float *graymap, int width, int height, int maxval) { int x, y; int r, g, b; int n; n = 0; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { if ((r = getc(fp)) != EOF) { /* エエ加減なチェック (^^; */ ++n; g = getc(fp); b = getc(fp); graymap[width * y + x] += rgb2gray(r, g, b, maxval); } else { /* データがないときは「白」にしておく */ graymap[width * y + (width - x - 1)] = 1.0; } } } return(n); } /* PPMファイルを読んで、ESC/P形式に変換 */ void ppmtoescp(FILE *fp, int q, int debug) { char s[BUFSIZ]; int width, height; int maxval; int xbytes; int y; unsigned char *bitmap; float *graymap; fgets(s, BUFSIZ, fp); if (sscanf(s, "%d %d", &width, &height) != 2) { fprintf(stderr, "%s: can't get image size\n", prog); exit(1); } fgets(s, BUFSIZ, fp); if (sscanf(s, "%d", &maxval) != 1) { fprintf(stderr, "%s: can't get maxval\n", prog); exit(1); } graymap = (float *)calloc(width * (NBITS + 1), sizeof(float)); cleargraymap(graymap, width, NBITS + 1); xbytes = (width + 7) / 8; bitmap = (unsigned char *)calloc(xbytes, NBITS); if (debug & PBM_OUTPUT) { printf("P4\n%d %d\n", width, height); } else { printf(PINIT); } for (y = 0; y < height; y += NBITS) { if (fillppm(fp, graymap, width, NBITS, maxval) <= 0) { break; } if (q == RANDOM_DITHER) { rd_gray2pbm(graymap, bitmap, width, NBITS); } else { fs_gray2pbm(graymap, bitmap, width, NBITS); } if (debug & PBM_OUTPUT) { debug_putpbm(bitmap, width, NBITS); } else { putpbm(bitmap, width, NBITS); } } free(bitmap); free(graymap); } /* 画像ファイルを読んでESC/Pに変換 */ void convert(FILE *fp, int q, int debug) { char magic[MAGIC_LENGTH]; if (fread(magic, 1, MAGIC_LENGTH, fp) != MAGIC_LENGTH) { fprintf(stderr, "%s: can't read magic\n", prog); exit(1); } if (strncmp(magic, MAGIC_PBM, MAGIC_LENGTH) == 0) { /* PBM */ pbmtoescp(fp, debug); } else if (strncmp(magic, MAGIC_PGM, MAGIC_LENGTH) == 0) { /* PGM */ pgmtoescp(fp, q, debug); } else if (strncmp(magic, MAGIC_PPM, MAGIC_LENGTH) == 0) { /* PPM */ ppmtoescp(fp, q, debug); } else if (strncmp(magic, MAGIC_BMP, MAGIC_LENGTH) == 0) { /* BMP */ bmptoescp(fp, q, debug); } else { fprintf(stderr, "%s: unknown file format\n", prog); exit(1); } exit(0); } void usage() { fprintf(stderr, "Usage: %s [-d] [-f] [-r] [file]...\n", prog); } int main(int argc, char *argv[]) { int q; int debug; FILE *fp; prog = *argv; shift; debug = 0; q = FLOYD_STEINBERG; while ((argc > 0) && (argv[0][0] == '-') && (argv[0][1] != EOS)) { if (argv[0][1] == 'f') { q = FLOYD_STEINBERG; } else if (argv[0][1] == 'r') { q = RANDOM_DITHER; } else if (argv[0][1] == 'd') { debug |= PBM_OUTPUT; } else { usage(); exit(1); } shift; } if (argc > 0) { while (argc > 0) { if (argv[0][0] == '-') { fp = stdin; } else if ((fp = fopen(*argv, "r")) == NULL) { fprintf(stderr, "%s: can't open %s\n", prog, *argv); exit(1); } convert(fp, q, debug); if (argv[0][0] != '-') { fclose(fp); } } } else { convert(stdin, q, debug); } exit(0); } /* Local Variables: */ /* compile-command:"gcc -Wall -o bm2escp bm2escp.c" */ /* End: */