/* * bm2ecsp -- 画像ファイルをESC/P形式に変換 * * revision history: * 0.0: Sep. 14, 2004 by Dai ISHIJIMA * 0.1: Sep. 16, 2004 BMPファイル対応 (FS誤差拡散など) */ #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_PBM "P4\n" #define MAGIC_BMP "BM6" /* なんと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 *canvas, int xbytes, int height) { bzero(canvas, xbytes * height); return(fread(canvas, xbytes, height, fp)); } /* PBM形式データをESC/P形式に変換して出力 (1行分) */ void putpbm(unsigned char *canvas, 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 (canvas[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 *canvas, int width, int height) { fwrite(canvas, (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 *canvas; 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; canvas = (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, canvas, xbytes, NBITS) <= 0) { break; } if (debug & PBM_OUTPUT) { debug_putpbm(canvas, width, NBITS); } else { putpbm(canvas, width, NBITS); } } free(canvas); } /* RGBをグレースケールに変換 */ #define rgb2gray(r,g,b) (( (double)(r) * 0.30 \ + (double)(g) * 0.59 \ + (double)(b) * 0.11) / (double)BMPMAXVAL) /* BMP用のバッファをクリアする */ void clearbmp(float *grayscale, int width, int height) { int x, y; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { grayscale[width * y + x] = 0.0; } } } /* 一度に印刷する分だけBMPファイルを読んで、グレースケールに変換 */ int fillbmp(FILE *fp, float *grayscale, int width, int height) { 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); /* 左右を反転して格納する */ grayscale[width * y + (width - x - 1)] += rgb2gray(r, g, b); } else { /* データがないときは「白」にしておく */ grayscale[width * y + (width - x - 1)] = 1.0; } } } return(n); } /* 誤差拡散法 (Floyd-Steinberg) で二値化してPBM形式に変換 */ void fs_bmp2pbm(float *bmp, 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 (bmp[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 = bmp[y * width + x] - val; if (x < width - 1) { bmp[y * width + x + 1] += error * 7.0 / 16.0; bmp[(y + 1) * width + x + 1] += error * 1.0 / 16.0; } if (x > 0) { bmp[(y + 1) * width + x - 1] += error * 3.0 / 16.0; } bmp[(y + 1) * width + x] += error * 5.0 / 16.0; bmp[y * width + x] = 0.0; } } /* 最終列の誤差を次のところにコピー */ for (x = 0; x < width; x++) { bmp[x] = bmp[height * width + x]; bmp[height * width + x] = 0.0; } } /* ランダムディザ法で二値化してPBM形式に変換 */ void rd_bmp2pbm(float *bmp, 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 (bmp[y * width + x] < val) { pbm[xbytes * y + (x / 8)] |= bit; } bit >>= 1; if (bit <= 0) { bit = 0x80; } bmp[y * width + x] = 0.0; } } } /* BMPファイルを二値化してESC/P形式に変換 */ void bmptoescp(FILE *fp, int q, int debug) { float *bmpcanvas; unsigned char *canvas; int width, height; int y; int xbytes; /* 寸法決め打ち (^^; */ width = BMPWIDTH; height = BMPHEIGHT; fseek(fp, BMPSKIP, SEEK_CUR); bmpcanvas = (float *)calloc(width * (NBITS + 1), sizeof(float)); clearbmp(bmpcanvas, width, NBITS + 1); xbytes = (width + 7) / 8; canvas = (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, bmpcanvas, width, NBITS) <= 0) { break; } if (q == RANDOM_DITHER) { rd_bmp2pbm(bmpcanvas, canvas, width, NBITS); } else { fs_bmp2pbm(bmpcanvas, canvas, width, NBITS); } if (debug & PBM_OUTPUT) { debug_putpbm(canvas, width, NBITS); } else { putpbm(canvas, width, NBITS); } } free(canvas); free(bmpcanvas); } 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_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: */