home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Resource Library: Graphics
/
graphics-16000.iso
/
general
/
convrtrs
/
pbmplus
/
ntpbmsrc.lha
/
netpbm
/
ppm
/
ppmtobmp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-10-04
|
16KB
|
632 lines
/*\
* $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);
}