--- OLD/ppm/bmptoppm.c Thu Jan 1 00:00:00 1970 +++ NEW/ppm/bmptoppm.c Thu Jan 1 00:00:00 1970 @@ -1,571 +1,445 @@ -/*\ - * $Id: bmptoppm.c,v 1.10 1992/11/24 19:38:17 dws Exp dws $ - * - * bmptoppm.c - Converts from a Microsoft Windows or OS/2 .BMP file to a - * PPM file. - * - * The current implementation is probably not complete, but it works for - * all the BMP files I have. I welcome feedback. - * - * Copyright (C) 1992 by David W. Sanderson. - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose and without fee is hereby granted, - * provided that the above copyright notice appear in all copies and - * that both that copyright notice and this permission notice appear - * in supporting documentation. This software is provided "as is" - * without express or implied warranty. - * - * $Log: bmptoppm.c,v $ - * Revision 1.10 1992/11/24 19:38:17 dws - * Added code to verify that reading occurred at the correct offsets. - * Added copyright. - * - * Revision 1.9 1992/11/17 02:15:24 dws - * Changed to include bmp.h. - * Eliminated need for fseek(), and therefore the need for a - * temporary file. - * - * Revision 1.8 1992/11/13 23:48:57 dws - * Made definition of Seekable() static, to match its prototype. - * - * Revision 1.7 1992/11/11 00:17:50 dws - * Generalized to use bitio routines. - * - * Revision 1.6 1992/11/10 23:51:44 dws - * Enhanced command-line handling. - * - * Revision 1.5 1992/11/08 00:38:46 dws - * Changed some names to help w/ addition of ppmtobmp. +/* + * Replacement bmptoppm. The bmptoppm in netpbm - at least the version + * I have - does not work for 24bpp BMP files (it expects to find 16M + * of colourmap, if nothing else) and possibly others. * - * Revision 1.4 1992/10/27 06:28:28 dws - * Corrected stupid typo. + * This works for 1bpp, 4bpp, 8bpp, and 24bpp and does not blab a lot + * of noise to stderr. However, parts of it are untested. + * Specifically, at this writing, these are untested: + * - 1bpp, 4bpp, and 8bpp reading support, + * - writing other than true-colour PPM files, + * - reading OS/2 BMP files. * - * Revision 1.3 1992/10/27 06:17:10 dws - * Removed a magic constant value. + * This file is in the public domain; anyone may use it in any way for + * any purpose, though I would appreciate credit where it is due. I'd + * also be interested in hearing about any bugs you find, especially + * if you also fix them. * - * Revision 1.2 1992/10/27 06:09:58 dws - * Made stdin seekable. + * der Mouse * - * Revision 1.1 1992/10/27 05:31:41 dws - * Initial revision -\*/ + * mouse@rodents.montreal.qc.ca + * 7D C8 61 52 5D E7 2D 39 4E F1 31 3E E8 B3 27 4B + */ -#include "bmp.h" -#include "ppm.h" -#include "bitio.h" +#include +#include +#include +#include +#include +#include -#define MAXCOLORS 256 +extern const char *__progname; -static char *ifname; +static FILE *f; +static const char *fname; +static unsigned long int foff; +static unsigned int filesize; +static unsigned int offbits; +static unsigned int xsize; +static unsigned int ysize; +static unsigned int planes; +static unsigned int bpp; +static unsigned char ctab[256][3]; +static void (*getctent)(unsigned char (*)[3]); +static void (*outpixel)(unsigned int); +static void (*outrow)(void); +static unsigned char pbmbuf; +static int pbmnbuf; +static char *obuf; +static int orlen; +static unsigned int ox; -/* - * Utilities - */ +static void usage(void) __attribute__((__noreturn__)); +static void usage(void) +{ + fprintf(stderr,"Usage: %s [filename]\n",__progname); + exit(1); +} -static int GetByte ARGS((FILE * fp)); -static short GetShort ARGS((FILE * fp)); -static long GetLong ARGS((FILE * fp)); -static void readto ARGS((FILE *fp, unsigned long *ppos, unsigned long dst)); -static void BMPreadfileheader ARGS((FILE *fp, unsigned long *ppos, - unsigned long *poffBits)); -static void BMPreadinfoheader ARGS((FILE *fp, unsigned long *ppos, - unsigned long *pcx, unsigned long *pcy, unsigned short *pcBitCount, - int *pclass)); -static int BMPreadrgbtable ARGS((FILE *fp, unsigned long *ppos, - unsigned short cBitCount, int class, pixval *R, pixval *G, pixval *B)); -static int BMPreadrow ARGS((FILE *fp, unsigned long *ppos, pixel *row, - unsigned long cx, unsigned short cBitCount, pixval *R, pixval *G, pixval *B)); -static pixel ** BMPreadbits ARGS((FILE *fp, unsigned long *ppos, - unsigned long offBits, unsigned long cx, unsigned long cy, - unsigned short cBitCount, int class, pixval *R, pixval *G, pixval *B)); - -static char er_read[] = "%s: read error"; -static char er_seek[] = "%s: seek error"; - -static int -GetByte(fp) - FILE *fp; -{ - int v; - - if ((v = getc(fp)) == EOF) - { - pm_error(er_read, ifname); - } +static void fail(const char *, ...) + __attribute__((__format__(__printf__,1,2),__noreturn__)); +static void fail(const char *fmt, ...) +{ + va_list ap; - return v; + fprintf(stderr,"%s: %s: ",__progname,fname); + va_start(ap,fmt); + vfprintf(stderr,fmt,ap); + va_end(ap); + fprintf(stderr,"\n"); + exit(1); } -static short -GetShort(fp) - FILE *fp; +static unsigned char get1(void) { - short v; - - if (pm_readlittleshort(fp, &v) == -1) - { - pm_error(er_read, ifname); - } + int c; - return v; + c = getc(f); + if (c == EOF) + { if (feof(f)) + { fprintf(stderr,"%s: %s: unexpected EOF (at offset %lu)\n",__progname,fname,foff); + } + else if (ferror(f)) + { fprintf(stderr,"%s: %s: read error: %s (at offset %lu)\n",__progname,fname,strerror(errno),foff); + } + else + { fprintf(stderr,"%s: %s: unclassifiable EOF (at offset %lu)\n",__progname,fname,foff); + } + exit(1); + } + foff ++; + return(c); } -static long -GetLong(fp) - FILE *fp; +static unsigned int get2(void) { - long v; + unsigned int v; - if (pm_readlittlelong(fp, &v) == -1) - { - pm_error(er_read, ifname); - } - - return v; + v = get1(); + v += get1() * 0x100; + return(v); } -/* - * readto - read as many bytes as necessary to position the - * file at the desired offset. - */ - -static void -readto(fp, ppos, dst) - FILE *fp; - unsigned long *ppos; /* pointer to number of bytes read from fp */ - unsigned long dst; -{ - unsigned long pos; - - if(!fp || !ppos) - return; - - pos = *ppos; - - if(pos > dst) - pm_error("%s: internal error in readto()", ifname); - - for(; pos < dst; pos++) - { - if (getc(fp) == EOF) - { - pm_error(er_read, ifname); - } - } +static unsigned int get4(void) +{ + unsigned int v; - *ppos = pos; + v = get1(); + v += get1() * 0x100; + v += get1() * 0x10000; + v += get1() * 0x1000000; + return(v); } -#if 0 -static void -Seek(fp, off) - FILE *fp; - long off; -{ - if (fseek(fp, off, 0) == -1) - { - pm_error(er_seek, ifname); - } +static void readmagic(void) +{ + if ( (get1() == 66) && /* ASCII B */ + (get1() == 77) ) return; /* ASCII M */ + fail("not a bmp: bad magic number"); } -/* - * Seekable(f) - makes sure the given FILE* is seekable (for - * reading). returns f if it is, and a new, seekable FILE* if f is - * stdin. - */ - -static FILE * -Seekable(f) - FILE *f; -{ - int c; - FILE *t; - - if (f != stdin) - { - return f; - } - - t = tmpfile(); - - while ((c = getc(f)) != EOF) - { - putc(c, t); - } - - rewind(t); - - return t; +static void readfileheader(void) +{ + filesize = get4(); + get2(); + get2(); + offbits = get4(); } -#endif - - -/* - * BMP reading routines - */ -static void -BMPreadfileheader(fp, ppos, poffBits) - FILE *fp; - unsigned long *ppos; /* number of bytes read from fp */ - unsigned long *poffBits; -{ - unsigned long cbSize; - unsigned short xHotSpot; - unsigned short yHotSpot; - unsigned long offBits; - - if (GetByte(fp) != 'B') - { - pm_error("%s is not a BMP file", ifname); - } - if (GetByte(fp) != 'M') - { - pm_error("%s is not a BMP file", ifname); - } +static void getctent_3(unsigned char (*ent)[3]) +{ + (*ent)[2] = get1(); + (*ent)[1] = get1(); + (*ent)[0] = get1(); +} - cbSize = GetLong(fp); - xHotSpot = GetShort(fp); - yHotSpot = GetShort(fp); - offBits = GetLong(fp); - - *poffBits = offBits; - - *ppos += 14; -} - -static void -BMPreadinfoheader(fp, ppos, pcx, pcy, pcBitCount, pclass) - FILE *fp; - unsigned long *ppos; /* number of bytes read from fp */ - unsigned long *pcx; - unsigned long *pcy; - unsigned short *pcBitCount; - int *pclass; -{ - unsigned long cbFix; - unsigned short cPlanes; - - unsigned long cx; - unsigned long cy; - unsigned short cBitCount; - int class; - - cbFix = GetLong(fp); - - switch (cbFix) - { - case 12: - class = C_OS2; - - cx = GetShort(fp); - cy = GetShort(fp); - cPlanes = GetShort(fp); - cBitCount = GetShort(fp); - - break; - case 40: - class = C_WIN; - - cx = GetLong(fp); - cy = GetLong(fp); - cPlanes = GetShort(fp); - cBitCount = GetShort(fp); - - /* - * We've read 16 bytes so far, need to read 24 more - * for the required total of 40. - */ - - GetLong(fp); - GetLong(fp); - GetLong(fp); - GetLong(fp); - GetLong(fp); - GetLong(fp); - - break; - default: - pm_error("%s: unknown cbFix: %d", ifname, cbFix); - break; - } +static void getctent_4(unsigned char (*ent)[3]) +{ + (*ent)[2] = get1(); + (*ent)[1] = get1(); + (*ent)[0] = get1(); + get1(); +} - if (cPlanes != 1) - { - pm_error("%s: don't know how to handle cPlanes = %d" - ,ifname - ,cPlanes); - } +static void readinfoheader(void) +{ + unsigned int hsize; - switch (class) - { - case C_WIN: - pm_message("Windows BMP, %dx%dx%d" - ,cx - ,cy - ,cBitCount); - break; - case C_OS2: - pm_message("OS/2 BMP, %dx%dx%d" - ,cx - ,cy - ,cBitCount); - break; + hsize = get4(); + switch (hsize) + { case 12: + xsize = get2(); + ysize = get2(); + planes = get2(); + bpp = get2(); + getctent = &getctent_3; + break; + case 40: + xsize = get4(); + ysize = get4(); + planes = get2(); + bpp = get2(); + getctent = &getctent_4; + if (get4()) + { fprintf(stderr,"%s: %s: image is compressed, don't know how to uncompress\n",__progname,fname); + exit(1); } - -#ifdef DEBUG - pm_message("cbFix: %d", cbFix); - pm_message("cx: %d", cx); - pm_message("cy: %d", cy); - pm_message("cPlanes: %d", cPlanes); - pm_message("cBitCount: %d", cBitCount); -#endif - - *pcx = cx; - *pcy = cy; - *pcBitCount = cBitCount; - *pclass = class; - - *ppos += cbFix; + get4(); /* image data size, can be ignored if no compression */ + get4(); /* X pixels per meter */ + get4(); /* Y pixels per meter */ + get4(); /* used colour count */ + get4(); /* important colour count */ + break; + } + switch (bpp) + { case 1: + break; + case 4: + break; + case 8: + break; + case 24: + break; + default: + fprintf(stderr,"%s: %s: claims %u bpp, don't know how to handle that\n",__progname,fname,bpp); + exit(1); + } } -/* - * returns the number of bytes read, or -1 on error. - */ -static int -BMPreadrgbtable(fp, ppos, cBitCount, class, R, G, B) - FILE *fp; - unsigned long *ppos; /* number of bytes read from fp */ - unsigned short cBitCount; - int class; - pixval *R; - pixval *G; - pixval *B; -{ - int i; - int nbyte = 0; - - long ncolors = (1 << cBitCount); - - for (i = 0; i < ncolors; i++) - { - B[i] = (pixval) GetByte(fp); - G[i] = (pixval) GetByte(fp); - R[i] = (pixval) GetByte(fp); - nbyte += 3; - - if (class == C_WIN) - { - (void) GetByte(fp); - nbyte++; - } - } +static void readctab(void) +{ + int n; + int i; - *ppos += nbyte; - return nbyte; + switch (bpp) + { case 1: case 4: case 8: + n = 1 << bpp; + break; + case 24: + return; + break; + default: + abort(); + break; + } + for (i=0;i 24) - { - pm_error("%s: cannot handle cBitCount: %d" - ,ifname - ,cBitCount); - } +static void output_byte(unsigned char c) +{ + obuf[ox++] = c; +} - /* - * The picture is stored bottom line first, top line last - */ - - for (y = cy - 1; y >= 0; y--) - { - int rc; - rc = BMPreadrow(fp, ppos, pixels[y], cx, cBitCount, R, G, B); - - if(rc == -1) - { - pm_error("%s: couldn't read row %d" - ,ifname - ,y); - } - if(rc%4) - { - pm_error("%s: row had bad number of bytes: %d" - ,ifname - ,rc); - } - } +static void output_done(void) +{ + unsigned int y; + char *obp; - return pixels; + obp = obuf + (ysize * orlen); + for (y=ysize;y>0;y--) fwrite(obp-=orlen,1,orlen,stdout); } -int -main(argc, argv) - int argc; - char **argv; +static int ctab_is_black(int inx) { - FILE *ifp = stdin; - char *usage = "[bmpfile]"; - int argn; + return((ctab[inx][0]==0)&&(ctab[inx][1]==0)&&(ctab[inx][2]==0)); +} - int rc; - unsigned long pos = 0; +static int ctab_is_white(int inx) +{ + return((ctab[inx][0]==255)&&(ctab[inx][1]==255)&&(ctab[inx][2]==255)); +} - unsigned long offBits; +static void writeheader_pbm(void) +{ + printf("P4\n%u %u\n",xsize,ysize); + pbmnbuf = 0; + init_output((xsize+7)>>3); +} - unsigned long cx; - unsigned long cy; - unsigned short cBitCount; - int class; +static void pbmflush(void) +{ + output_byte(pbmbuf<<(8-pbmnbuf)); + pbmnbuf = 0; +} - pixval R[MAXCOLORS]; /* reds */ - pixval G[MAXCOLORS]; /* greens */ - pixval B[MAXCOLORS]; /* blues */ +static void out_pixel_pbm(unsigned int pix) +{ + pbmbuf = (pbmbuf << 1) | pix; + if (++pbmnbuf > 7) pbmflush(); +} - pixel **pixels; +static void out_pixel_pbmi(unsigned int pix) +{ + out_pixel_pbm(!pix); +} +static void out_row_pbm(void) +{ + pbmflush(); +} - ppm_init(&argc, argv); +static int ctab_all_grey(void) +{ + int i; - /* - * Since this command takes no flags, produce an error message - * if the user tries to give any. - * This is friendlier than if the command were to say - * 'no such file: -help'. - */ + for (i=(1<=0;i--) + { if ((ctab[i][0] != ctab[i][1]) || (ctab[i][0] != ctab[i][2])) return(0); + } + return(1); +} - argn = 1; - while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') - { - pm_usage(usage); - ++argn; - } +static void writeheader_pgm(void) +{ + printf("P5\n%u %u\n255\n",xsize,ysize); + init_output(xsize); +} - if (argn < argc) - { - ifname = argv[argn]; - ifp = pm_openr(ifname); - ++argn; - } - else - { - ifname = "standard input"; - ifp = stdin; - } +static void out_pixel_pgm(unsigned int pix) +{ + output_byte(ctab[pix][0]); +} - if (argn != argc) - { - pm_usage(usage); - } +static void out_row_pgm(void) +{ +} - BMPreadfileheader(ifp, &pos, &offBits); - BMPreadinfoheader(ifp, &pos, &cx, &cy, &cBitCount, &class); +static void writeheader_ppm(void) +{ + printf("P6\n%u %u\n255\n",xsize,ysize); + init_output(xsize*3); +} - if(offBits != BMPoffbits(class, cBitCount)) - { - pm_message("warning: offBits is %d, expected %d" - , pos - , BMPoffbits(class, cBitCount)); - } +static void out_pixel_ppm_map(unsigned int pix) +{ + output_byte(ctab[pix][0]); + output_byte(ctab[pix][1]); + output_byte(ctab[pix][2]); +} - rc = BMPreadrgbtable(ifp, &pos, cBitCount, class, R, G, B); +static void out_pixel_ppm_true(unsigned int pix) +{ + output_byte(pix&0xff); + output_byte((pix>>8)&0xff); + output_byte((pix>>16)&0xff); +} - if(rc != BMPlenrgbtable(class, cBitCount)) - { - pm_message("warning: %d-byte RGB table, expected %d bytes" - , rc - , BMPlenrgbtable(class, cBitCount)); - } +static void out_row_ppm(void) +{ +} +static void writeheader(void) +{ + if ((bpp == 1) && ctab_is_black(0) && ctab_is_white(1)) + { outpixel = &out_pixel_pbmi; + outrow = &out_row_pbm; + writeheader_pbm(); + } + else if ((bpp == 1) && ctab_is_black(1) && ctab_is_white(0)) + { outpixel = &out_pixel_pbm; + outrow = &out_row_pbm; + writeheader_pbm(); + } + else if ((bpp < 24) && ctab_all_grey()) + { outpixel = &out_pixel_pgm; + outrow = &out_row_pgm; + writeheader_pgm(); + } + else if (bpp < 24) + { outpixel = &out_pixel_ppm_map; + outrow = &out_row_ppm; + writeheader_ppm(); + } + else + { outpixel = &out_pixel_ppm_true; + outrow = &out_row_ppm; + writeheader_ppm(); + } +} - pixels = BMPreadbits(ifp, &pos, offBits, cx, cy - , cBitCount, class, R, G, B); +static void processdata(void) +{ + unsigned int y; + unsigned int x; + unsigned int i; - if(pos != BMPlenfile(class, cBitCount, cx, cy)) - { - pm_message("warning: read %d bytes, expected to read %d bytes" - , pos - , BMPlenfile(class, cBitCount, cx, cy)); - } + for (y=0;y>= 1; + } + } + (*outrow)(); + for (i="\0\3\2\1"[((xsize+7)>>3)&3];i>0;i--) get1(); + break; + case 4: + for (x=0;x>= 4; + } + } + (*outrow)(); + for (i="\0\3\2\1"[((xsize+1)>>1)&3];i>0;i--) get1(); + break; + case 8: + for (x=0;x0;i--) get1(); + break; + case 24: + for (x=0;x0;i--) get1(); + break; + } + } + output_done(); +} - pm_close(ifp); - ppm_writeppm(stdout, pixels, cx, cy, (pixval) (MAXCOLORS-1), 0); - pm_close(stdout); +static void convert(const char *fn) +{ + if (fn) + { f = fopen(fn,"r"); + if (f == 0) + { fprintf(stderr,"%s: can't open %s: %s\n",__progname,fn,strerror(errno)); + usage(); + } + fname = fn; + } + else + { f = stdin; + fname = "standard input"; + } + foff = 0; + readmagic(); + readfileheader(); + readinfoheader(); + readctab(); + checkpos(); + writeheader(); + processdata(); +} - exit(0); +int main(int, char **); +int main(int ac, char **av) +{ + switch (ac) + { case 1: + convert(0); + break; + case 2: + convert(av[1]); + break; + default: + usage(); + break; + } + exit(0); }