This is ppmtobmp.c in view mode; [Download] [Up]
/*\ * $Id: ppmtobmp.c,v 1.9 1992/11/24 19:39:33 dws Exp dws $ * * ppmtobmp.c - Converts from a PPM file to a Microsoft Windows or OS/2 * .BMP file. * * The current implementation is probably not complete, but it works for * me. 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: ppmtobmp.c,v $ * Revision 1.9 1992/11/24 19:39:33 dws * Added copyright. * * Revision 1.8 1992/11/17 02:16:52 dws * Moved length functions to bmp.h. * * Revision 1.7 1992/11/11 23:18:16 dws * Modified to adjust the bits per pixel to 1, 4, or 8. * * Revision 1.6 1992/11/11 22:43:39 dws * Commented out a superfluous message. * * Revision 1.5 1992/11/11 05:58:06 dws * First version that works. * * Revision 1.4 1992/11/11 03:40:32 dws * Moved calculation of bits per pixel to BMPEncode. * * Revision 1.3 1992/11/11 03:02:34 dws * Added BMPEncode function. * * Revision 1.2 1992/11/08 01:44:35 dws * Added option processing and reading of PPM file. * * Revision 1.1 1992/11/08 00:46:07 dws * Initial revision \*/ #include "bmp.h" #include "ppm.h" #include "ppmcmap.h" #include "bitio.h" #define MAXCOLORS 256 /* * Utilities */ static char er_write[] = "stdout: write error"; /* prototypes */ static void PutByte ARGS((FILE *fp, char v)); static void PutShort ARGS((FILE *fp, short v)); static void PutLong ARGS((FILE *fp, long v)); static int BMPwritefileheader ARGS((FILE *fp, int class, unsigned long bitcount, unsigned long x, unsigned long y)); static int BMPwriteinfoheader ARGS((FILE *fp, int class, unsigned long bitcount, unsigned long x, unsigned long y)); static int BMPwritergb ARGS((FILE *fp, int class, pixval R, pixval G, pixval B)); static int BMPwritergbtable ARGS((FILE *fp, int class, int bpp, int colors, pixval *R, pixval *G, pixval *B)); static int BMPwriterow ARGS((FILE *fp, pixel *row, unsigned long cx, unsigned short bpp, colorhash_table cht)); static int BMPwritebits ARGS((FILE *fp, unsigned long cx, unsigned long cy, unsigned short cBitCount, pixel **pixels, colorhash_table cht)); static int colorstobpp ARGS((int colors)); static void BMPEncode ARGS((FILE *fp, int class, int x, int y, pixel **pixels, int colors, colorhash_table cht, pixval *R, pixval *G, pixval *B)); static void PutByte(fp, v) FILE *fp; char v; { if (putc(v, fp) == EOF) { pm_error(er_write); } } static void PutShort(fp, v) FILE *fp; short v; { if (pm_writelittleshort(fp, v) == -1) { pm_error(er_write); } } static void PutLong(fp, v) FILE *fp; long v; { if (pm_writelittlelong(fp, v) == -1) { pm_error(er_write); } } /* * BMP writing */ /* * returns the number of bytes written, or -1 on error. */ static int BMPwritefileheader(fp, class, bitcount, x, y) FILE *fp; int class; unsigned long bitcount; unsigned long x; unsigned long y; { PutByte(fp, 'B'); PutByte(fp, 'M'); /* cbSize */ PutLong(fp, BMPlenfile(class, bitcount, x, y)); /* xHotSpot */ PutShort(fp, 0); /* yHotSpot */ PutShort(fp, 0); /* offBits */ PutLong(fp, BMPoffbits(class, bitcount)); return 14; } /* * returns the number of bytes written, or -1 on error. */ static int BMPwriteinfoheader(fp, class, bitcount, x, y) FILE *fp; int class; unsigned long bitcount; unsigned long x; unsigned long y; { long cbFix; /* cbFix */ switch (class) { case C_WIN: cbFix = 40; PutLong(fp, cbFix); /* cx */ PutLong(fp, x); /* cy */ PutLong(fp, y); /* cPlanes */ PutShort(fp, 1); /* cBitCount */ PutShort(fp, bitcount); /* * We've written 16 bytes so far, need to write 24 more * for the required total of 40. */ PutLong(fp, 0); PutLong(fp, 0); PutLong(fp, 0); PutLong(fp, 0); PutLong(fp, 0); PutLong(fp, 0); break; case C_OS2: cbFix = 12; PutLong(fp, cbFix); /* cx */ PutShort(fp, x); /* cy */ PutShort(fp, y); /* cPlanes */ PutShort(fp, 1); /* cBitCount */ PutShort(fp, bitcount); break; default: pm_error(er_internal, "BMPwriteinfoheader"); } return cbFix; } /* * returns the number of bytes written, or -1 on error. */ static int BMPwritergb(fp,class,R,G,B) FILE *fp; int class; pixval R; pixval G; pixval B; { switch (class) { case C_WIN: PutByte(fp, B); PutByte(fp, G); PutByte(fp, R); PutByte(fp, 0); return 4; case C_OS2: PutByte(fp, B); PutByte(fp, G); PutByte(fp, R); return 3; default: pm_error(er_internal, "BMPwritergb"); } return -1; } /* * returns the number of bytes written, or -1 on error. */ static int BMPwritergbtable(fp,class,bpp,colors,R,G,B) FILE *fp; int class; int bpp; int colors; pixval *R; pixval *G; pixval *B; { int nbyte = 0; int i; long ncolors; for (i = 0; i < colors; i++) { nbyte += BMPwritergb(fp,class,R[i],G[i],B[i]); } ncolors = (1 << bpp); for (; i < ncolors; i++) { nbyte += BMPwritergb(fp,class,0,0,0); } return nbyte; } /* * returns the number of bytes written, or -1 on error. */ static int BMPwriterow(fp, row, cx, bpp, cht) FILE *fp; pixel *row; unsigned long cx; unsigned short bpp; colorhash_table cht; { BITSTREAM b; unsigned nbyte = 0; int rc; unsigned x; if ((b = pm_bitinit(fp, "w")) == (BITSTREAM) 0) { return -1; } for (x = 0; x < cx; x++, row++) { if ((rc = pm_bitwrite(b, bpp, ppm_lookupcolor(cht, row))) == -1) { return -1; } nbyte += rc; } if ((rc = pm_bitfini(b)) == -1) { return -1; } nbyte += rc; /* * Make sure we write a multiple of 4 bytes. */ while (nbyte % 4) { PutByte(fp, 0); nbyte++; } return nbyte; } /* * returns the number of bytes written, or -1 on error. */ static int BMPwritebits(fp, cx, cy, cBitCount, pixels, cht) FILE *fp; unsigned long cx; unsigned long cy; unsigned short cBitCount; pixel **pixels; colorhash_table cht; { int nbyte = 0; long y; if(cBitCount > 24) { pm_error("cannot handle cBitCount: %d" ,cBitCount); } /* * The picture is stored bottom line first, top line last */ for (y = cy - 1; y >= 0; y--) { int rc; rc = BMPwriterow(fp, pixels[y], cx, cBitCount, cht); if(rc == -1) { pm_error("couldn't write row %d" ,y); } if(rc%4) { pm_error("row had bad number of bytes: %d" ,rc); } nbyte += rc; } return nbyte; } /* * Return the number of bits per pixel required to represent the * given number of colors. */ static int colorstobpp(colors) int colors; { int bpp; if (colors < 1) { pm_error("can't have less than one color"); } if ((bpp = pm_maxvaltobits(colors - 1)) > 8) { pm_error("can't happen"); } return bpp; } /* * Write a BMP file of the given class. * * Note that we must have 'colors' in order to know exactly how many * colors are in the R, G, B, arrays. Entries beyond those in the * arrays are undefined. */ static void BMPEncode(fp, class, x, y, pixels, colors, cht, R, G, B) FILE *fp; int class; int x; int y; pixel **pixels; int colors; /* number of valid entries in R,G,B */ colorhash_table cht; pixval *R; pixval *G; pixval *B; { int bpp; /* bits per pixel */ unsigned long nbyte = 0; bpp = colorstobpp(colors); /* * I have found empirically at least one BMP-displaying program * that can't deal with (for instance) using 3 bits per pixel. * I have seen no programs that can deal with using 3 bits per * pixel. I have seen programs which can deal with 1, 4, and * 8 bits per pixel. * * Based on this, I adjust actual the number of bits per pixel * as follows. If anyone knows better, PLEASE tell me! */ switch(bpp) { case 2: case 3: bpp = 4; break; case 5: case 6: case 7: bpp = 8; break; } pm_message("Using %d bits per pixel", bpp); nbyte += BMPwritefileheader(fp, class, bpp, x, y); nbyte += BMPwriteinfoheader(fp, class, bpp, x, y); nbyte += BMPwritergbtable(fp, class, bpp, colors, R, G, B); if(nbyte != ( BMPlenfileheader(class) + BMPleninfoheader(class) + BMPlenrgbtable(class, bpp))) { pm_error(er_internal, "BMPEncode"); } nbyte += BMPwritebits(fp, x, y, bpp, pixels, cht); if(nbyte != BMPlenfile(class, bpp, x, y)) { pm_error(er_internal, "BMPEncode"); } } #if 0 report(class, bitcount, x, y) int class; unsigned long bitcount; unsigned long x; unsigned long y; { char *name; switch (class) { case C_WIN: name = "Win"; break; case C_OS2: name = "OS/2"; break; default: pm_error(er_internal, "report"); return 0; } pm_message("For class %s, bitcount %d, x %d, y %d:" , name , bitcount , x , y); pm_message("\tlenrgbtable: %d" , BMPlenrgbtable(class, bitcount)); pm_message("\tlenline: %d" , BMPlenline(class, bitcount, x)); pm_message("\tlenbits: %d" , BMPlenbits(class, bitcount, x, y)); pm_message("\toffbits: %d" , BMPoffbits(class, bitcount)); pm_message("\tlenfile: %d" , BMPlenfile(class, bitcount, x, y)); } int main(ac, av) int ac; char **av; { ppm_init(&ac, av); if(ac != 5) { pm_message("usage: ppmtobmp class bitcount x y"); exit(1); } report(atoi(av[1]), atoi(av[2]), atoi(av[3]), atoi(av[4])); exit(0); } #endif int main(argc, argv) int argc; char **argv; { FILE *ifp = stdin; char *usage = "[-windows] [-os2] [ppmfile]"; int class = C_OS2; int argn; int rows; int cols; int colors; int i; pixval maxval; colorhist_vector chv; pixval Red[MAXCOLORS]; pixval Green[MAXCOLORS]; pixval Blue[MAXCOLORS]; pixel** pixels; colorhash_table cht; ppm_init(&argc, argv); argn = 1; while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') { if (pm_keymatch(argv[argn], "-windows", 2)) class = C_WIN; else if (pm_keymatch(argv[argn], "-os2", 2)) class = C_OS2; else pm_usage(usage); ++argn; } if (argn < argc) { ifp = pm_openr(argv[argn]); ++argn; } if (argn != argc) { pm_usage(usage); } pixels = ppm_readppm(ifp, &cols, &rows, &maxval); pm_close(ifp); #if 0 { char *name; switch (class) { case C_WIN: name = "a Windows"; break; case C_OS2: name = "an OS/2"; break; default: pm_error(er_internal, "report"); break; } pm_message("generating %s BMP file", name); } #endif /* Figure out the colormap. */ pm_message("computing colormap..."); chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors); if (chv == (colorhist_vector) 0) pm_error("too many colors - try doing a 'ppmquant %d'" , MAXCOLORS); pm_message("%d colors found", colors); /* * Now turn the ppm colormap into the appropriate GIF * colormap. */ if (maxval > 255) { pm_message("maxval is not 255 - automatically rescaling colors"); } for (i = 0; i < colors; ++i) { if (maxval == 255) { Red[i] = PPM_GETR(chv[i].color); Green[i] = PPM_GETG(chv[i].color); Blue[i] = PPM_GETB(chv[i].color); } else { Red[i] = (pixval) PPM_GETR(chv[i].color) * 255 / maxval; Green[i] = (pixval) PPM_GETG(chv[i].color) * 255 / maxval; Blue[i] = (pixval) PPM_GETB(chv[i].color) * 255 / maxval; } } /* And make a hash table for fast lookup. */ cht = ppm_colorhisttocolorhash(chv, colors); ppm_freecolorhist(chv); /* All set, let's do it. */ BMPEncode(stdout, class , cols, rows, pixels, colors, cht ,Red, Green, Blue); pm_close(stdout); exit(0); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.