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

  1. /*\
  2.  * $Id: bmptoppm.c,v 1.10 1992/11/24 19:38:17 dws Exp dws $
  3.  * 
  4.  * bmptoppm.c - Converts from a Microsoft Windows or OS/2 .BMP file to a
  5.  * PPM file.
  6.  * 
  7.  * The current implementation is probably not complete, but it works for
  8.  * all the BMP files I have.  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: bmptoppm.c,v $
  20.  * Revision 1.10  1992/11/24  19:38:17  dws
  21.  * Added code to verify that reading occurred at the correct offsets.
  22.  * Added copyright.
  23.  *
  24.  * Revision 1.9  1992/11/17  02:15:24  dws
  25.  * Changed to include bmp.h.
  26.  * Eliminated need for fseek(), and therefore the need for a
  27.  * temporary file.
  28.  *
  29.  * Revision 1.8  1992/11/13  23:48:57  dws
  30.  * Made definition of Seekable() static, to match its prototype.
  31.  *
  32.  * Revision 1.7  1992/11/11  00:17:50  dws
  33.  * Generalized to use bitio routines.
  34.  *
  35.  * Revision 1.6  1992/11/10  23:51:44  dws
  36.  * Enhanced command-line handling.
  37.  *
  38.  * Revision 1.5  1992/11/08  00:38:46  dws
  39.  * Changed some names to help w/ addition of ppmtobmp.
  40.  *
  41.  * Revision 1.4  1992/10/27  06:28:28  dws
  42.  * Corrected stupid typo.
  43.  *
  44.  * Revision 1.3  1992/10/27  06:17:10  dws
  45.  * Removed a magic constant value.
  46.  *
  47.  * Revision 1.2  1992/10/27  06:09:58  dws
  48.  * Made stdin seekable.
  49.  *
  50.  * Revision 1.1  1992/10/27  05:31:41  dws
  51.  * Initial revision
  52. \*/
  53.  
  54. #include    "bmp.h"
  55. #include    "ppm.h"
  56. #include    "bitio.h"
  57.  
  58. #define    MAXCOLORS       256
  59.  
  60. static char    *ifname;
  61.  
  62. /*
  63.  * Utilities
  64.  */
  65.  
  66. static int GetByte ARGS((FILE * fp));
  67. static short GetShort ARGS((FILE * fp));
  68. static long GetLong ARGS((FILE * fp));
  69. static void readto ARGS((FILE *fp, unsigned long *ppos, unsigned long dst));
  70. static void BMPreadfileheader ARGS((FILE *fp, unsigned long *ppos,
  71.     unsigned long *poffBits));
  72. static void BMPreadinfoheader ARGS((FILE *fp, unsigned long *ppos,
  73.     unsigned long *pcx, unsigned long *pcy, unsigned short *pcBitCount,
  74.     int *pclass));
  75. static int BMPreadrgbtable ARGS((FILE *fp, unsigned long *ppos,
  76.     unsigned short cBitCount, int class, pixval *R, pixval *G, pixval *B));
  77. static int BMPreadrow ARGS((FILE *fp, unsigned long *ppos, pixel *row,
  78.     unsigned long cx, unsigned short cBitCount, pixval *R, pixval *G, pixval *B));
  79. static pixel ** BMPreadbits ARGS((FILE *fp, unsigned long *ppos,
  80.     unsigned long offBits, unsigned long cx, unsigned long cy,
  81.     unsigned short cBitCount, int class, pixval *R, pixval *G, pixval *B));
  82.  
  83. static char     er_read[] = "%s: read error";
  84. static char     er_seek[] = "%s: seek error";
  85.  
  86. static int
  87. GetByte(fp)
  88.     FILE           *fp;
  89. {
  90.     int             v;
  91.  
  92.     if ((v = getc(fp)) == EOF)
  93.     {
  94.         pm_error(er_read, ifname);
  95.     }
  96.  
  97.     return v;
  98. }
  99.  
  100. static short
  101. GetShort(fp)
  102.     FILE           *fp;
  103. {
  104.     short           v;
  105.  
  106.     if (pm_readlittleshort(fp, &v) == -1)
  107.     {
  108.         pm_error(er_read, ifname);
  109.     }
  110.  
  111.     return v;
  112. }
  113.  
  114. static long
  115. GetLong(fp)
  116.     FILE           *fp;
  117. {
  118.     long            v;
  119.  
  120.     if (pm_readlittlelong(fp, &v) == -1)
  121.     {
  122.         pm_error(er_read, ifname);
  123.     }
  124.  
  125.     return v;
  126. }
  127.  
  128. /*
  129.  * readto - read as many bytes as necessary to position the
  130.  * file at the desired offset.
  131.  */
  132.  
  133. static void
  134. readto(fp, ppos, dst)
  135.     FILE           *fp;
  136.     unsigned long  *ppos;    /* pointer to number of bytes read from fp */
  137.     unsigned long   dst;
  138. {
  139.     unsigned long   pos;
  140.  
  141.     if(!fp || !ppos)
  142.         return;
  143.  
  144.     pos = *ppos;
  145.  
  146.     if(pos > dst)
  147.         pm_error("%s: internal error in readto()", ifname);
  148.  
  149.     for(; pos < dst; pos++)
  150.     {
  151.         if (getc(fp) == EOF)
  152.         {
  153.             pm_error(er_read, ifname);
  154.         }
  155.     }
  156.  
  157.     *ppos = pos;
  158. }
  159.  
  160. #if 0
  161. static void
  162. Seek(fp, off)
  163.     FILE           *fp;
  164.     long            off;
  165. {
  166.     if (fseek(fp, off, 0) == -1)
  167.     {
  168.         pm_error(er_seek, ifname);
  169.     }
  170. }
  171.  
  172. /*
  173.  * Seekable(f) - makes sure the given FILE* is seekable (for
  174.  * reading). returns f if it is, and a new, seekable FILE* if f is
  175.  * stdin.
  176.  */
  177.  
  178. static FILE    *
  179. Seekable(f)
  180.     FILE           *f;
  181. {
  182.     int             c;
  183.     FILE           *t;
  184.  
  185.     if (f != stdin)
  186.     {
  187.         return f;
  188.     }
  189.  
  190.     t = tmpfile();
  191.  
  192.     while ((c = getc(f)) != EOF)
  193.     {
  194.         putc(c, t);
  195.     }
  196.  
  197.     rewind(t);
  198.  
  199.     return t;
  200. }
  201. #endif
  202.  
  203.  
  204. /*
  205.  * BMP reading routines
  206.  */
  207.  
  208. static void
  209. BMPreadfileheader(fp, ppos, poffBits)
  210.     FILE           *fp;
  211.     unsigned long  *ppos;    /* number of bytes read from fp */
  212.     unsigned long  *poffBits;
  213. {
  214.     unsigned long   cbSize;
  215.     unsigned short  xHotSpot;
  216.     unsigned short  yHotSpot;
  217.     unsigned long   offBits;
  218.  
  219.     if (GetByte(fp) != 'B')
  220.     {
  221.         pm_error("%s is not a BMP file", ifname);
  222.     }
  223.     if (GetByte(fp) != 'M')
  224.     {
  225.         pm_error("%s is not a BMP file", ifname);
  226.     }
  227.  
  228.     cbSize = GetLong(fp);
  229.     xHotSpot = GetShort(fp);
  230.     yHotSpot = GetShort(fp);
  231.     offBits = GetLong(fp);
  232.  
  233.     *poffBits = offBits;
  234.  
  235.     *ppos += 14;
  236. }
  237.  
  238. static void
  239. BMPreadinfoheader(fp, ppos, pcx, pcy, pcBitCount, pclass)
  240.     FILE           *fp;
  241.     unsigned long  *ppos;    /* number of bytes read from fp */
  242.     unsigned long  *pcx;
  243.     unsigned long  *pcy;
  244.     unsigned short *pcBitCount;
  245.     int            *pclass;
  246. {
  247.     unsigned long   cbFix;
  248.     unsigned short  cPlanes;
  249.  
  250.     unsigned long   cx;
  251.     unsigned long   cy;
  252.     unsigned short  cBitCount;
  253.     int             class;
  254.  
  255.     cbFix = GetLong(fp);
  256.  
  257.     switch (cbFix)
  258.     {
  259.     case 12:
  260.         class = C_OS2;
  261.  
  262.         cx = GetShort(fp);
  263.         cy = GetShort(fp);
  264.         cPlanes = GetShort(fp);
  265.         cBitCount = GetShort(fp);
  266.  
  267.         break;
  268.     case 40:
  269.         class = C_WIN;
  270.  
  271.         cx = GetLong(fp);
  272.         cy = GetLong(fp);
  273.         cPlanes = GetShort(fp);
  274.         cBitCount = GetShort(fp);
  275.  
  276.         /*
  277.          * We've read 16 bytes so far, need to read 24 more
  278.          * for the required total of 40.
  279.          */
  280.  
  281.         GetLong(fp);
  282.         GetLong(fp);
  283.         GetLong(fp);
  284.         GetLong(fp);
  285.         GetLong(fp);
  286.         GetLong(fp);
  287.  
  288.         break;
  289.     default:
  290.         pm_error("%s: unknown cbFix: %d", ifname, cbFix);
  291.         break;
  292.     }
  293.  
  294.     if (cPlanes != 1)
  295.     {
  296.         pm_error("%s: don't know how to handle cPlanes = %d"
  297.              ,ifname
  298.              ,cPlanes);
  299.     }
  300.  
  301.     switch (class)
  302.     {
  303.     case C_WIN:
  304.         pm_message("Windows BMP, %dx%dx%d"
  305.                ,cx
  306.                ,cy
  307.                ,cBitCount);
  308.         break;
  309.     case C_OS2:
  310.         pm_message("OS/2 BMP, %dx%dx%d"
  311.                ,cx
  312.                ,cy
  313.                ,cBitCount);
  314.         break;
  315.     }
  316.  
  317. #ifdef DEBUG
  318.     pm_message("cbFix: %d", cbFix);
  319.     pm_message("cx: %d", cx);
  320.     pm_message("cy: %d", cy);
  321.     pm_message("cPlanes: %d", cPlanes);
  322.     pm_message("cBitCount: %d", cBitCount);
  323. #endif
  324.  
  325.     *pcx = cx;
  326.     *pcy = cy;
  327.     *pcBitCount = cBitCount;
  328.     *pclass = class;
  329.  
  330.     *ppos += cbFix;
  331. }
  332.  
  333. /*
  334.  * returns the number of bytes read, or -1 on error.
  335.  */
  336. static int
  337. BMPreadrgbtable(fp, ppos, cBitCount, class, R, G, B)
  338.     FILE           *fp;
  339.     unsigned long  *ppos;    /* number of bytes read from fp */
  340.     unsigned short  cBitCount;
  341.     int             class;
  342.     pixval         *R;
  343.     pixval         *G;
  344.     pixval         *B;
  345. {
  346.     int             i;
  347.     int        nbyte = 0;
  348.  
  349.     long            ncolors = (1 << cBitCount);
  350.  
  351.     for (i = 0; i < ncolors; i++)
  352.     {
  353.         B[i] = (pixval) GetByte(fp);
  354.         G[i] = (pixval) GetByte(fp);
  355.         R[i] = (pixval) GetByte(fp);
  356.         nbyte += 3;
  357.  
  358.         if (class == C_WIN)
  359.         {
  360.             (void) GetByte(fp);
  361.             nbyte++;
  362.         }
  363.     }
  364.  
  365.     *ppos += nbyte;
  366.     return nbyte;
  367. }
  368.  
  369. /*
  370.  * returns the number of bytes read, or -1 on error.
  371.  */
  372. static int
  373. BMPreadrow(fp, ppos, row, cx, cBitCount, R, G, B)
  374.     FILE           *fp;
  375.     unsigned long  *ppos;    /* number of bytes read from fp */
  376.     pixel          *row;
  377.     unsigned long   cx;
  378.     unsigned short  cBitCount;
  379.     pixval         *R;
  380.     pixval         *G;
  381.     pixval         *B;
  382. {
  383.     BITSTREAM       b;
  384.     unsigned        nbyte = 0;
  385.     int             rc;
  386.     unsigned        x;
  387.  
  388.     if ((b = pm_bitinit(fp, "r")) == (BITSTREAM) 0)
  389.     {
  390.         return -1;
  391.     }
  392.  
  393.     for (x = 0; x < cx; x++, row++)
  394.     {
  395.         unsigned long   v;
  396.  
  397.         if ((rc = pm_bitread(b, cBitCount, &v)) == -1)
  398.         {
  399.             return -1;
  400.         }
  401.         nbyte += rc;
  402.  
  403.         PPM_ASSIGN(*row, R[v], G[v], B[v]);
  404.     }
  405.  
  406.     if ((rc = pm_bitfini(b)) != 0)
  407.     {
  408.         return -1;
  409.     }
  410.  
  411.     /*
  412.      * Make sure we read a multiple of 4 bytes.
  413.      */
  414.     while (nbyte % 4)
  415.     {
  416.         GetByte(fp);
  417.         nbyte++;
  418.     }
  419.  
  420.     *ppos += nbyte;
  421.     return nbyte;
  422. }
  423.  
  424. static pixel **
  425. BMPreadbits(fp, ppos, offBits, cx, cy, cBitCount, class, R, G, B)
  426.     FILE           *fp;
  427.     unsigned long  *ppos;    /* number of bytes read from fp */
  428.     unsigned long   offBits;
  429.     unsigned long   cx;
  430.     unsigned long   cy;
  431.     unsigned short  cBitCount;
  432.     int             class;
  433.     pixval         *R;
  434.     pixval         *G;
  435.     pixval         *B;
  436. {
  437.     pixel         **pixels;    /* output */
  438.     long            y;
  439.  
  440.     readto(fp, ppos, offBits);
  441.  
  442.     pixels = ppm_allocarray(cx, cy);
  443.  
  444.     if(cBitCount > 24)
  445.     {
  446.         pm_error("%s: cannot handle cBitCount: %d"
  447.              ,ifname
  448.              ,cBitCount);
  449.     }
  450.  
  451.     /*
  452.      * The picture is stored bottom line first, top line last
  453.      */
  454.  
  455.     for (y = cy - 1; y >= 0; y--)
  456.     {
  457.         int rc;
  458.         rc = BMPreadrow(fp, ppos, pixels[y], cx, cBitCount, R, G, B);
  459.  
  460.         if(rc == -1)
  461.         {
  462.             pm_error("%s: couldn't read row %d"
  463.                  ,ifname
  464.                  ,y);
  465.         }
  466.         if(rc%4)
  467.         {
  468.             pm_error("%s: row had bad number of bytes: %d"
  469.                  ,ifname
  470.                  ,rc);
  471.         }
  472.     }
  473.  
  474.     return pixels;
  475. }
  476.  
  477. int
  478. main(argc, argv)
  479.     int             argc;
  480.     char          **argv;
  481. {
  482.     FILE           *ifp = stdin;
  483.     char           *usage = "[bmpfile]";
  484.     int             argn;
  485.  
  486.     int             rc;
  487.     unsigned long    pos = 0;
  488.  
  489.     unsigned long   offBits;
  490.  
  491.     unsigned long   cx;
  492.     unsigned long   cy;
  493.     unsigned short  cBitCount;
  494.     int             class;
  495.  
  496.     pixval          R[MAXCOLORS];    /* reds */
  497.     pixval          G[MAXCOLORS];    /* greens */
  498.     pixval          B[MAXCOLORS];    /* blues */
  499.  
  500.     pixel         **pixels;
  501.  
  502.  
  503.     ppm_init(&argc, argv);
  504.  
  505.     /*
  506.      * Since this command takes no flags, produce an error message
  507.      * if the user tries to give any.
  508.      * This is friendlier than if the command were to say
  509.      * 'no such file: -help'.
  510.      */
  511.  
  512.     argn = 1;
  513.     while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0')
  514.     {
  515.         pm_usage(usage);
  516.         ++argn;
  517.     }
  518.  
  519.     if (argn < argc)
  520.     {
  521.         ifname = argv[argn];
  522.         ifp = pm_openr(ifname);
  523.         ++argn;
  524.     }
  525.     else
  526.     {
  527.         ifname = "standard input";
  528.         ifp = stdin;
  529.     }
  530.  
  531.     if (argn != argc)
  532.     {
  533.         pm_usage(usage);
  534.     }
  535.  
  536.     BMPreadfileheader(ifp, &pos, &offBits);
  537.     BMPreadinfoheader(ifp, &pos, &cx, &cy, &cBitCount, &class);
  538.  
  539.     if(offBits != BMPoffbits(class, cBitCount))
  540.     {
  541.         pm_message("warning: offBits is %d, expected %d"
  542.             , pos
  543.             , BMPoffbits(class, cBitCount));
  544.     }
  545.  
  546.     rc = BMPreadrgbtable(ifp, &pos, cBitCount, class, R, G, B);
  547.  
  548.     if(rc != BMPlenrgbtable(class, cBitCount))
  549.     {
  550.         pm_message("warning: %d-byte RGB table, expected %d bytes"
  551.             , rc
  552.             , BMPlenrgbtable(class, cBitCount));
  553.     }
  554.  
  555.  
  556.     pixels = BMPreadbits(ifp, &pos, offBits, cx, cy
  557.             , cBitCount, class, R, G, B);
  558.  
  559.     if(pos != BMPlenfile(class, cBitCount, cx, cy))
  560.     {
  561.         pm_message("warning: read %d bytes, expected to read %d bytes"
  562.             , pos
  563.             , BMPlenfile(class, cBitCount, cx, cy));
  564.     }
  565.  
  566.     pm_close(ifp);
  567.     ppm_writeppm(stdout, pixels, cx, cy, (pixval) (MAXCOLORS-1), 0);
  568.     pm_close(stdout);
  569.  
  570.     exit(0);
  571. }
  572.