home *** CD-ROM | disk | FTP | other *** search
- /*\
- * $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.
- *
- * Revision 1.4 1992/10/27 06:28:28 dws
- * Corrected stupid typo.
- *
- * Revision 1.3 1992/10/27 06:17:10 dws
- * Removed a magic constant value.
- *
- * Revision 1.2 1992/10/27 06:09:58 dws
- * Made stdin seekable.
- *
- * Revision 1.1 1992/10/27 05:31:41 dws
- * Initial revision
- \*/
-
- #include "bmp.h"
- #include "ppm.h"
- #include "bitio.h"
-
- #define MAXCOLORS 256
-
- static char *ifname;
-
- /*
- * Utilities
- */
-
- 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);
- }
-
- return v;
- }
-
- static short
- GetShort(fp)
- FILE *fp;
- {
- short v;
-
- if (pm_readlittleshort(fp, &v) == -1)
- {
- pm_error(er_read, ifname);
- }
-
- return v;
- }
-
- static long
- GetLong(fp)
- FILE *fp;
- {
- long v;
-
- if (pm_readlittlelong(fp, &v) == -1)
- {
- pm_error(er_read, ifname);
- }
-
- 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);
- }
- }
-
- *ppos = pos;
- }
-
- #if 0
- static void
- Seek(fp, off)
- FILE *fp;
- long off;
- {
- if (fseek(fp, off, 0) == -1)
- {
- pm_error(er_seek, ifname);
- }
- }
-
- /*
- * 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;
- }
- #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);
- }
-
- 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;
- }
-
- if (cPlanes != 1)
- {
- pm_error("%s: don't know how to handle cPlanes = %d"
- ,ifname
- ,cPlanes);
- }
-
- 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;
- }
-
- #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;
- }
-
- /*
- * 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++;
- }
- }
-
- *ppos += nbyte;
- return nbyte;
- }
-
- /*
- * returns the number of bytes read, or -1 on error.
- */
- static int
- BMPreadrow(fp, ppos, row, cx, cBitCount, R, G, B)
- FILE *fp;
- unsigned long *ppos; /* number of bytes read from fp */
- pixel *row;
- unsigned long cx;
- unsigned short cBitCount;
- pixval *R;
- pixval *G;
- pixval *B;
- {
- BITSTREAM b;
- unsigned nbyte = 0;
- int rc;
- unsigned x;
-
- if ((b = pm_bitinit(fp, "r")) == (BITSTREAM) 0)
- {
- return -1;
- }
-
- for (x = 0; x < cx; x++, row++)
- {
- unsigned long v;
-
- if ((rc = pm_bitread(b, cBitCount, &v)) == -1)
- {
- return -1;
- }
- nbyte += rc;
-
- PPM_ASSIGN(*row, R[v], G[v], B[v]);
- }
-
- if ((rc = pm_bitfini(b)) != 0)
- {
- return -1;
- }
-
- /*
- * Make sure we read a multiple of 4 bytes.
- */
- while (nbyte % 4)
- {
- GetByte(fp);
- nbyte++;
- }
-
- *ppos += nbyte;
- return nbyte;
- }
-
- static pixel **
- BMPreadbits(fp, ppos, offBits, cx, cy, cBitCount, class, R, G, B)
- FILE *fp;
- unsigned long *ppos; /* number of bytes read from fp */
- unsigned long offBits;
- unsigned long cx;
- unsigned long cy;
- unsigned short cBitCount;
- int class;
- pixval *R;
- pixval *G;
- pixval *B;
- {
- pixel **pixels; /* output */
- long y;
-
- readto(fp, ppos, offBits);
-
- pixels = ppm_allocarray(cx, cy);
-
- if(cBitCount > 24)
- {
- pm_error("%s: cannot handle cBitCount: %d"
- ,ifname
- ,cBitCount);
- }
-
- /*
- * 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);
- }
- }
-
- return pixels;
- }
-
- int
- main(argc, argv)
- int argc;
- char **argv;
- {
- FILE *ifp = stdin;
- char *usage = "[bmpfile]";
- int argn;
-
- int rc;
- unsigned long pos = 0;
-
- unsigned long offBits;
-
- unsigned long cx;
- unsigned long cy;
- unsigned short cBitCount;
- int class;
-
- pixval R[MAXCOLORS]; /* reds */
- pixval G[MAXCOLORS]; /* greens */
- pixval B[MAXCOLORS]; /* blues */
-
- pixel **pixels;
-
-
- ppm_init(&argc, argv);
-
- /*
- * 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'.
- */
-
- argn = 1;
- while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0')
- {
- pm_usage(usage);
- ++argn;
- }
-
- if (argn < argc)
- {
- ifname = argv[argn];
- ifp = pm_openr(ifname);
- ++argn;
- }
- else
- {
- ifname = "standard input";
- ifp = stdin;
- }
-
- if (argn != argc)
- {
- pm_usage(usage);
- }
-
- BMPreadfileheader(ifp, &pos, &offBits);
- BMPreadinfoheader(ifp, &pos, &cx, &cy, &cBitCount, &class);
-
- if(offBits != BMPoffbits(class, cBitCount))
- {
- pm_message("warning: offBits is %d, expected %d"
- , pos
- , BMPoffbits(class, cBitCount));
- }
-
- rc = BMPreadrgbtable(ifp, &pos, cBitCount, class, R, G, B);
-
- if(rc != BMPlenrgbtable(class, cBitCount))
- {
- pm_message("warning: %d-byte RGB table, expected %d bytes"
- , rc
- , BMPlenrgbtable(class, cBitCount));
- }
-
-
- pixels = BMPreadbits(ifp, &pos, offBits, cx, cy
- , cBitCount, class, R, G, B);
-
- if(pos != BMPlenfile(class, cBitCount, cx, cy))
- {
- pm_message("warning: read %d bytes, expected to read %d bytes"
- , pos
- , BMPlenfile(class, cBitCount, cx, cy));
- }
-
- pm_close(ifp);
- ppm_writeppm(stdout, pixels, cx, cy, (pixval) (MAXCOLORS-1), 0);
- pm_close(stdout);
-
- exit(0);
- }
-