home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / netpbma.zip / ppm / ppmtobmp.c < prev    next >
C/C++ Source or Header  |  1993-10-04  |  16KB  |  632 lines

  1. /*\
  2.  * $Id: ppmtobmp.c,v 1.9 1992/11/24 19:39:33 dws Exp dws $
  3.  *
  4.  * ppmtobmp.c - Converts from a PPM file to a Microsoft Windows or OS/2
  5.  * .BMP file.
  6.  *
  7.  * The current implementation is probably not complete, but it works for
  8.  * me.  I welcome feedback.
  9.  *
  10.  * Copyright (C) 1992 by David W. Sanderson.
  11.  *
  12.  * Permission to use, copy, modify, and distribute this software and its
  13.  * documentation for any purpose and without fee is hereby granted,
  14.  * provided that the above copyright notice appear in all copies and
  15.  * that both that copyright notice and this permission notice appear
  16.  * in supporting documentation.  This software is provided "as is"
  17.  * without express or implied warranty.
  18.  *
  19.  * $Log: ppmtobmp.c,v $
  20.  * Revision 1.9  1992/11/24  19:39:33  dws
  21.  * Added copyright.
  22.  *
  23.  * Revision 1.8  1992/11/17  02:16:52  dws
  24.  * Moved length functions to bmp.h.
  25.  *
  26.  * Revision 1.7  1992/11/11  23:18:16  dws
  27.  * Modified to adjust the bits per pixel to 1, 4, or 8.
  28.  *
  29.  * Revision 1.6  1992/11/11  22:43:39  dws
  30.  * Commented out a superfluous message.
  31.  *
  32.  * Revision 1.5  1992/11/11  05:58:06  dws
  33.  * First version that works.
  34.  *
  35.  * Revision 1.4  1992/11/11  03:40:32  dws
  36.  * Moved calculation of bits per pixel to BMPEncode.
  37.  *
  38.  * Revision 1.3  1992/11/11  03:02:34  dws
  39.  * Added BMPEncode function.
  40.  *
  41.  * Revision 1.2  1992/11/08  01:44:35  dws
  42.  * Added option processing and reading of PPM file.
  43.  *
  44.  * Revision 1.1  1992/11/08  00:46:07  dws
  45.  * Initial revision
  46. \*/
  47.  
  48. #include        "bmp.h"
  49. #include        "ppm.h"
  50. #include        "ppmcmap.h"
  51. #include        "bitio.h"
  52.  
  53. #define MAXCOLORS 256
  54.  
  55. /*
  56.  * Utilities
  57.  */
  58.  
  59. static char     er_write[] = "stdout: write error";
  60.  
  61. /* prototypes */
  62. static void PutByte ARGS((FILE *fp, char v));
  63. static void PutShort ARGS((FILE *fp, short v));
  64. static void PutLong ARGS((FILE *fp, long v));
  65. static int BMPwritefileheader ARGS((FILE *fp, int class, unsigned long bitcount,
  66.     unsigned long x, unsigned long y));
  67. static int BMPwriteinfoheader ARGS((FILE *fp, int class, unsigned long bitcount,
  68.     unsigned long x, unsigned long y));
  69. static int BMPwritergb ARGS((FILE *fp, int class, pixval R, pixval G, pixval B));
  70. static int BMPwritergbtable ARGS((FILE *fp, int class, int bpp, int colors,
  71.     pixval *R, pixval *G, pixval *B));
  72. static int BMPwriterow ARGS((FILE *fp, pixel *row, unsigned long cx,
  73.     unsigned short bpp, colorhash_table cht));
  74. static int BMPwritebits ARGS((FILE *fp, unsigned long cx, unsigned long cy,
  75.     unsigned short cBitCount, pixel **pixels, colorhash_table cht));
  76. static int colorstobpp ARGS((int colors));
  77. static void BMPEncode ARGS((FILE *fp, int class, int x, int y, pixel **pixels,
  78.     int colors, colorhash_table cht, pixval *R, pixval *G, pixval *B));
  79. static void
  80. PutByte(fp, v)
  81.         FILE           *fp;
  82.         char            v;
  83. {
  84.         if (putc(v, fp) == EOF)
  85.         {
  86.                 pm_error(er_write);
  87.         }
  88. }
  89.  
  90. static void
  91. PutShort(fp, v)
  92.         FILE           *fp;
  93.         short           v;
  94. {
  95.         if (pm_writelittleshort(fp, v) == -1)
  96.         {
  97.                 pm_error(er_write);
  98.         }
  99. }
  100.  
  101. static void
  102. PutLong(fp, v)
  103.         FILE           *fp;
  104.         long            v;
  105. {
  106.         if (pm_writelittlelong(fp, v) == -1)
  107.         {
  108.                 pm_error(er_write);
  109.         }
  110. }
  111.  
  112. /*
  113.  * BMP writing
  114.  */
  115.  
  116. /*
  117.  * returns the number of bytes written, or -1 on error.
  118.  */
  119. static int
  120. BMPwritefileheader(fp, class, bitcount, x, y)
  121.         FILE           *fp;
  122.         int             class;
  123.         unsigned long   bitcount;
  124.         unsigned long   x;
  125.         unsigned long   y;
  126. {
  127.         PutByte(fp, 'B');
  128.         PutByte(fp, 'M');
  129.  
  130.         /* cbSize */
  131.         PutLong(fp, BMPlenfile(class, bitcount, x, y));
  132.  
  133.         /* xHotSpot */
  134.         PutShort(fp, 0);
  135.  
  136.         /* yHotSpot */
  137.         PutShort(fp, 0);
  138.  
  139.         /* offBits */
  140.         PutLong(fp, BMPoffbits(class, bitcount));
  141.  
  142.         return 14;
  143. }
  144.  
  145. /*
  146.  * returns the number of bytes written, or -1 on error.
  147.  */
  148. static int
  149. BMPwriteinfoheader(fp, class, bitcount, x, y)
  150.         FILE           *fp;
  151.         int             class;
  152.         unsigned long   bitcount;
  153.         unsigned long   x;
  154.         unsigned long   y;
  155. {
  156.         long    cbFix;
  157.  
  158.         /* cbFix */
  159.         switch (class)
  160.         {
  161.         case C_WIN:
  162.                 cbFix = 40;
  163.                 PutLong(fp, cbFix);
  164.  
  165.                 /* cx */
  166.                 PutLong(fp, x);
  167.                 /* cy */
  168.                 PutLong(fp, y);
  169.                 /* cPlanes */
  170.                 PutShort(fp, 1);
  171.                 /* cBitCount */
  172.                 PutShort(fp, bitcount);
  173.  
  174.                 /*
  175.                  * We've written 16 bytes so far, need to write 24 more
  176.                  * for the required total of 40.
  177.                  */
  178.  
  179.                 PutLong(fp, 0);
  180.                 PutLong(fp, 0);
  181.                 PutLong(fp, 0);
  182.                 PutLong(fp, 0);
  183.                 PutLong(fp, 0);
  184.                 PutLong(fp, 0);
  185.  
  186.  
  187.                 break;
  188.         case C_OS2:
  189.                 cbFix = 12;
  190.                 PutLong(fp, cbFix);
  191.  
  192.                 /* cx */
  193.                 PutShort(fp, x);
  194.                 /* cy */
  195.                 PutShort(fp, y);
  196.                 /* cPlanes */
  197.                 PutShort(fp, 1);
  198.                 /* cBitCount */
  199.                 PutShort(fp, bitcount);
  200.  
  201.                 break;
  202.         default:
  203.                 pm_error(er_internal, "BMPwriteinfoheader");
  204.         }
  205.  
  206.         return cbFix;
  207. }
  208.  
  209. /*
  210.  * returns the number of bytes written, or -1 on error.
  211.  */
  212. static int
  213. BMPwritergb(fp,class,R,G,B)
  214.         FILE           *fp;
  215.         int             class;
  216.         pixval          R;
  217.         pixval          G;
  218.         pixval          B;
  219. {
  220.         switch (class)
  221.         {
  222.         case C_WIN:
  223.                 PutByte(fp, B);
  224.                 PutByte(fp, G);
  225.                 PutByte(fp, R);
  226.                 PutByte(fp, 0);
  227.                 return 4;
  228.         case C_OS2:
  229.                 PutByte(fp, B);
  230.                 PutByte(fp, G);
  231.                 PutByte(fp, R);
  232.                 return 3;
  233.         default:
  234.                 pm_error(er_internal, "BMPwritergb");
  235.         }
  236.         return -1;
  237. }
  238.  
  239. /*
  240.  * returns the number of bytes written, or -1 on error.
  241.  */
  242. static int
  243. BMPwritergbtable(fp,class,bpp,colors,R,G,B)
  244.         FILE           *fp;
  245.         int             class;
  246.         int             bpp;
  247.         int             colors;
  248.         pixval         *R;
  249.         pixval         *G;
  250.         pixval         *B;
  251. {
  252.         int             nbyte = 0;
  253.         int             i;
  254.         long            ncolors;
  255.  
  256.         for (i = 0; i < colors; i++)
  257.         {
  258.                 nbyte += BMPwritergb(fp,class,R[i],G[i],B[i]);
  259.         }
  260.  
  261.         ncolors = (1 << bpp);
  262.  
  263.         for (; i < ncolors; i++)
  264.         {
  265.                 nbyte += BMPwritergb(fp,class,0,0,0);
  266.         }
  267.  
  268.         return nbyte;
  269. }
  270.  
  271. /*
  272.  * returns the number of bytes written, or -1 on error.
  273.  */
  274. static int
  275. BMPwriterow(fp, row, cx, bpp, cht)
  276.         FILE           *fp;
  277.         pixel          *row;
  278.         unsigned long   cx;
  279.         unsigned short  bpp;
  280.         colorhash_table cht;
  281. {
  282.         BITSTREAM       b;
  283.         unsigned        nbyte = 0;
  284.         int             rc;
  285.         unsigned        x;
  286.  
  287.         if ((b = pm_bitinit(fp, "w")) == (BITSTREAM) 0)
  288.         {
  289.                 return -1;
  290.         }
  291.  
  292.         for (x = 0; x < cx; x++, row++)
  293.         {
  294.                 if ((rc = pm_bitwrite(b, bpp, ppm_lookupcolor(cht, row))) == -1)
  295.                 {
  296.                         return -1;
  297.                 }
  298.                 nbyte += rc;
  299.         }
  300.  
  301.         if ((rc = pm_bitfini(b)) == -1)
  302.         {
  303.                 return -1;
  304.         }
  305.         nbyte += rc;
  306.  
  307.         /*
  308.          * Make sure we write a multiple of 4 bytes.
  309.          */
  310.         while (nbyte % 4)
  311.         {
  312.                 PutByte(fp, 0);
  313.                 nbyte++;
  314.         }
  315.  
  316.         return nbyte;
  317. }
  318.  
  319. /*
  320.  * returns the number of bytes written, or -1 on error.
  321.  */
  322. static int
  323. BMPwritebits(fp, cx, cy, cBitCount, pixels, cht)
  324.         FILE           *fp;
  325.         unsigned long   cx;
  326.         unsigned long   cy;
  327.         unsigned short  cBitCount;
  328.         pixel         **pixels;
  329.         colorhash_table cht;
  330. {
  331.         int             nbyte = 0;
  332.         long            y;
  333.  
  334.         if(cBitCount > 24)
  335.         {
  336.                 pm_error("cannot handle cBitCount: %d"
  337.                          ,cBitCount);
  338.         }
  339.  
  340.         /*
  341.          * The picture is stored bottom line first, top line last
  342.          */
  343.  
  344.         for (y = cy - 1; y >= 0; y--)
  345.         {
  346.                 int rc;
  347.                 rc = BMPwriterow(fp, pixels[y], cx, cBitCount, cht);
  348.  
  349.                 if(rc == -1)
  350.                 {
  351.                         pm_error("couldn't write row %d"
  352.                                  ,y);
  353.                 }
  354.                 if(rc%4)
  355.                 {
  356.                         pm_error("row had bad number of bytes: %d"
  357.                                  ,rc);
  358.                 }
  359.                 nbyte += rc;
  360.         }
  361.  
  362.         return nbyte;
  363. }
  364.  
  365. /*
  366.  * Return the number of bits per pixel required to represent the
  367.  * given number of colors.
  368.  */
  369.  
  370. static int
  371. colorstobpp(colors)
  372.         int             colors;
  373. {
  374.         int             bpp;
  375.  
  376.         if (colors < 1)
  377.         {
  378.                 pm_error("can't have less than one color");
  379.         }
  380.  
  381.         if ((bpp = pm_maxvaltobits(colors - 1)) > 8)
  382.         {
  383.                 pm_error("can't happen");
  384.         }
  385.  
  386.         return bpp;
  387. }
  388.  
  389. /*
  390.  * Write a BMP file of the given class.
  391.  *
  392.  * Note that we must have 'colors' in order to know exactly how many
  393.  * colors are in the R, G, B, arrays.  Entries beyond those in the
  394.  * arrays are undefined.
  395.  */
  396. static void
  397. BMPEncode(fp, class, x, y, pixels, colors, cht, R, G, B)
  398.         FILE           *fp;
  399.         int             class;
  400.         int             x;
  401.         int             y;
  402.         pixel         **pixels;
  403.         int             colors; /* number of valid entries in R,G,B */
  404.         colorhash_table cht;
  405.         pixval         *R;
  406.         pixval         *G;
  407.         pixval         *B;
  408. {
  409.         int             bpp;    /* bits per pixel */
  410.         unsigned long   nbyte = 0;
  411.  
  412.         bpp = colorstobpp(colors);
  413.  
  414.         /*
  415.          * I have found empirically at least one BMP-displaying program
  416.          * that can't deal with (for instance) using 3 bits per pixel.
  417.          * I have seen no programs that can deal with using 3 bits per
  418.          * pixel.  I have seen programs which can deal with 1, 4, and
  419.          * 8 bits per pixel.
  420.          *
  421.          * Based on this, I adjust actual the number of bits per pixel
  422.          * as follows.  If anyone knows better, PLEASE tell me!
  423.          */
  424.         switch(bpp)
  425.         {
  426.         case 2:
  427.         case 3:
  428.                 bpp = 4;
  429.                 break;
  430.         case 5:
  431.         case 6:
  432.         case 7:
  433.                 bpp = 8;
  434.                 break;
  435.         }
  436.  
  437.         pm_message("Using %d bits per pixel", bpp);
  438.  
  439.         nbyte += BMPwritefileheader(fp, class, bpp, x, y);
  440.         nbyte += BMPwriteinfoheader(fp, class, bpp, x, y);
  441.         nbyte += BMPwritergbtable(fp, class, bpp, colors, R, G, B);
  442.  
  443.         if(nbyte !=     ( BMPlenfileheader(class)
  444.                         + BMPleninfoheader(class)
  445.                         + BMPlenrgbtable(class, bpp)))
  446.         {
  447.                 pm_error(er_internal, "BMPEncode");
  448.         }
  449.  
  450.         nbyte += BMPwritebits(fp, x, y, bpp, pixels, cht);
  451.         if(nbyte != BMPlenfile(class, bpp, x, y))
  452.         {
  453.                 pm_error(er_internal, "BMPEncode");
  454.         }
  455. }
  456.  
  457. #if 0
  458. report(class, bitcount, x, y)
  459.         int             class;
  460.         unsigned long   bitcount;
  461.         unsigned long   x;
  462.         unsigned long   y;
  463. {
  464.         char *name;
  465.         switch (class)
  466.         {
  467.         case C_WIN:
  468.                 name = "Win";
  469.                 break;
  470.         case C_OS2:
  471.                 name = "OS/2";
  472.                 break;
  473.         default:
  474.                 pm_error(er_internal, "report");
  475.                 return 0;
  476.         }
  477.  
  478.         pm_message("For class %s, bitcount %d, x %d, y %d:"
  479.                 , name
  480.                 , bitcount
  481.                 , x
  482.                 , y);
  483.         pm_message("\tlenrgbtable: %d"
  484.                 , BMPlenrgbtable(class, bitcount));
  485.         pm_message("\tlenline:       %d"
  486.                 , BMPlenline(class, bitcount, x));
  487.         pm_message("\tlenbits:       %d"
  488.                 , BMPlenbits(class, bitcount, x, y));
  489.         pm_message("\toffbits:       %d"
  490.                 , BMPoffbits(class, bitcount));
  491.         pm_message("\tlenfile:       %d"
  492.                 , BMPlenfile(class, bitcount, x, y));
  493. }
  494.  
  495. int
  496. main(ac, av)
  497.     int             ac;
  498.     char          **av;
  499. {
  500.     ppm_init(&ac, av);
  501.  
  502.     if(ac != 5)
  503.     {
  504.     pm_message("usage: ppmtobmp class bitcount x y");
  505.     exit(1);
  506.     }
  507.  
  508.     report(atoi(av[1]), atoi(av[2]), atoi(av[3]), atoi(av[4]));
  509.  
  510.     exit(0);
  511. }
  512. #endif
  513.  
  514. int
  515. main(argc, argv)
  516.         int             argc;
  517.         char          **argv;
  518. {
  519.         FILE           *ifp = stdin;
  520.         char           *usage = "[-windows] [-os2] [ppmfile]";
  521.         int             class = C_OS2;
  522.  
  523.         int             argn;
  524.         int             rows;
  525.         int             cols;
  526.         int             colors;
  527.         int             i;
  528.         pixval          maxval;
  529.         colorhist_vector chv;
  530.         pixval          Red[MAXCOLORS];
  531.         pixval          Green[MAXCOLORS];
  532.         pixval          Blue[MAXCOLORS];
  533.  
  534.         pixel** pixels;
  535.         colorhash_table cht;
  536.  
  537.  
  538.         ppm_init(&argc, argv);
  539.  
  540.         argn = 1;
  541.  
  542.         while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0')
  543.         {
  544.                 if (pm_keymatch(argv[argn], "-windows", 2))
  545.                         class = C_WIN;
  546.                 else if (pm_keymatch(argv[argn], "-os2", 2))
  547.                         class = C_OS2;
  548.                 else
  549.                         pm_usage(usage);
  550.                 ++argn;
  551.         }
  552.  
  553.         if (argn < argc)
  554.         {
  555.                 ifp = pm_openr(argv[argn]);
  556.                 ++argn;
  557.         }
  558.  
  559.         if (argn != argc)
  560.         {
  561.                 pm_usage(usage);
  562.         }
  563.  
  564.         pixels = ppm_readppm(ifp, &cols, &rows, &maxval);
  565.  
  566.         pm_close(ifp);
  567.  
  568. #if 0
  569.         {
  570.                 char *name;
  571.                 switch (class)
  572.                 {
  573.                 case C_WIN:
  574.                         name = "a Windows";
  575.                         break;
  576.                 case C_OS2:
  577.                         name = "an OS/2";
  578.                         break;
  579.                 default:
  580.                         pm_error(er_internal, "report");
  581.                         break;
  582.                 }
  583.                 pm_message("generating %s BMP file", name);
  584.         }
  585. #endif
  586.  
  587.         /* Figure out the colormap. */
  588.         pm_message("computing colormap...");
  589.         chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors);
  590.         if (chv == (colorhist_vector) 0)
  591.                 pm_error("too many colors - try doing a 'ppmquant %d'"
  592.                         , MAXCOLORS);
  593.         pm_message("%d colors found", colors);
  594.  
  595.         /*
  596.          * Now turn the ppm colormap into the appropriate GIF
  597.          * colormap.
  598.          */
  599.         if (maxval > 255)
  600.         {
  601.                 pm_message("maxval is not 255 - automatically rescaling colors");
  602.         }
  603.         for (i = 0; i < colors; ++i)
  604.         {
  605.                 if (maxval == 255)
  606.                 {
  607.                         Red[i] = PPM_GETR(chv[i].color);
  608.                         Green[i] = PPM_GETG(chv[i].color);
  609.                         Blue[i] = PPM_GETB(chv[i].color);
  610.                 }
  611.                 else
  612.                 {
  613.                         Red[i] = (pixval) PPM_GETR(chv[i].color) * 255 / maxval;
  614.                         Green[i] = (pixval) PPM_GETG(chv[i].color) * 255 / maxval;
  615.                         Blue[i] = (pixval) PPM_GETB(chv[i].color) * 255 / maxval;
  616.                 }
  617.         }
  618.  
  619.         /* And make a hash table for fast lookup. */
  620.         cht = ppm_colorhisttocolorhash(chv, colors);
  621.         ppm_freecolorhist(chv);
  622.  
  623.         /* All set, let's do it. */
  624.         BMPEncode(stdout, class
  625.                 , cols, rows, pixels, colors, cht
  626.                 ,Red, Green, Blue);
  627.  
  628.         pm_close(stdout);
  629.  
  630.         exit(0);
  631. }
  632.