Panda3D
 All Classes Functions Variables Enumerations
pnmFileTypeBMPWriter.cxx
00001 // Filename: pnmFileTypeBMPWriter.cxx
00002 // Created by:  drose (19Jun00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "pnmFileTypeBMP.h"
00016 
00017 #ifdef HAVE_BMP
00018 
00019 #include "config_pnmimagetypes.h"
00020 
00021 #include "pnmImage.h"
00022 #include "pnmWriter.h"
00023 
00024 #include "bmp.h"
00025 #include "ppmcmap.h"
00026 #include "pnmbitio.h"
00027 #include "thread.h"
00028 
00029 // Much code in this file is borrowed from Netpbm, specifically ppmtobmp.c.
00030 /*
00031  * ppmtobmp.c - Converts from a PPM file to a Microsoft Windows or OS/2
00032  * .BMP file.
00033  *
00034  * The current implementation is probably not complete, but it works for
00035  * me.  I welcome feedback.
00036  *
00037  * Copyright (C) 1992 by David W. Sanderson.
00038  *
00039  * Permission to use, copy, modify, and distribute this software and its
00040  * documentation for any purpose and without fee is hereby granted,
00041  * provided that the above copyright notice appear in all copies and
00042  * that both that copyright notice and this permission notice appear
00043  * in supporting documentation.  This software is provided "as is"
00044  * without express or implied warranty.
00045  */
00046 
00047 #define MAXCOLORS 256
00048 
00049 /*
00050  * Utilities
00051  */
00052 
00053 static char     er_write[] = "stdout: write error";
00054 
00055 /* prototypes */
00056 static void PutByte (ostream *fp, char v);
00057 static void PutShort (ostream *fp, short v);
00058 static void PutLong (ostream *fp, long v);
00059 static int BMPwritefileheader (ostream *fp, int classv, unsigned long bitcount,
00060     unsigned long x, unsigned long y);
00061 static int BMPwriteinfoheader (ostream *fp, int classv, unsigned long bitcount,
00062     unsigned long x, unsigned long y);
00063 static int BMPwritergb (ostream *fp, int classv, pixval R, pixval G, pixval B);
00064 static int BMPwritergbtable (ostream *fp, int classv, int bpp, int colors,
00065     pixval *R, pixval *G, pixval *B);
00066 static int colorstobpp (int colors);
00067 static void BMPEncode (ostream *fp, int classv, int x, int y, pixel **pixels,
00068     int colors, colorhash_table cht, pixval *R, pixval *G, pixval *B);
00069 static void
00070 PutByte(
00071         ostream           *fp,
00072         char            v)
00073 {
00074         if (!fp->put(v))
00075         {
00076                 pm_error(er_write);
00077         }
00078 }
00079 
00080 static void
00081 PutShort(
00082         ostream           *fp,
00083         short           v)
00084 {
00085         if (pm_writelittleshort(fp, v) == -1)
00086         {
00087                 pm_error(er_write);
00088         }
00089 }
00090 
00091 static void
00092 PutLong(
00093         ostream           *fp,
00094         long            v)
00095 {
00096         if (pm_writelittlelong(fp, v) == -1)
00097         {
00098                 pm_error(er_write);
00099         }
00100 }
00101 
00102 /*
00103  * BMP writing
00104  */
00105 
00106 /*
00107  * returns the number of bytes written, or -1 on error.
00108  */
00109 static int
00110 BMPwritefileheader(
00111         ostream           *fp,
00112         int             classv,
00113         unsigned long   bitcount,
00114         unsigned long   x,
00115         unsigned long   y)
00116 {
00117         PutByte(fp, 'B');
00118         PutByte(fp, 'M');
00119 
00120         /* cbSize */
00121         PutLong(fp, (long)BMPlenfile(classv, bitcount, x, y));
00122 
00123         /* xHotSpot */
00124         PutShort(fp, 0);
00125 
00126         /* yHotSpot */
00127         PutShort(fp, 0);
00128 
00129         /* offBits */
00130         PutLong(fp, (long)BMPoffbits(classv, bitcount));
00131 
00132         return 14;
00133 }
00134 
00135 /*
00136  * returns the number of bytes written, or -1 on error.
00137  */
00138 static int
00139 BMPwriteinfoheader(
00140         ostream           *fp,
00141         int             classv,
00142         unsigned long   bitcount,
00143         unsigned long   x,
00144         unsigned long   y)
00145 {
00146         long    cbFix = 0;
00147 
00148         /* cbFix */
00149         switch (classv)
00150         {
00151         case C_WIN:
00152                 cbFix = 40;
00153                 PutLong(fp, cbFix);
00154 
00155                 /* cx */
00156                 PutLong(fp, (long)x);
00157                 /* cy */
00158                 PutLong(fp, (long)y);
00159                 /* cPlanes */
00160                 PutShort(fp, 1);
00161                 /* cBitCount */
00162                 PutShort(fp, (short)bitcount);
00163 
00164                 /*
00165                  * We've written 16 bytes so far, need to write 24 more
00166                  * for the required total of 40.
00167                  */
00168 
00169                 PutLong(fp, 0);
00170                 PutLong(fp, 0);
00171                 PutLong(fp, 0);
00172                 PutLong(fp, 0);
00173                 PutLong(fp, 0);
00174                 PutLong(fp, 0);
00175 
00176 
00177                 break;
00178         case C_OS2:
00179                 cbFix = 12;
00180                 PutLong(fp, cbFix);
00181 
00182                 /* cx */
00183                 PutShort(fp, (short)x);
00184                 /* cy */
00185                 PutShort(fp, (short)y);
00186                 /* cPlanes */
00187                 PutShort(fp, 1);
00188                 /* cBitCount */
00189                 PutShort(fp, (short)bitcount);
00190 
00191                 break;
00192         default:
00193                 pm_error(er_internal, "BMPwriteinfoheader");
00194         }
00195 
00196         return cbFix;
00197 }
00198 
00199 /*
00200  * returns the number of bytes written, or -1 on error.
00201  */
00202 static int
00203 BMPwritergb(
00204         ostream           *fp,
00205         int             classv,
00206         pixval          R,
00207         pixval          G,
00208         pixval          B)
00209 {
00210         switch (classv)
00211         {
00212         case C_WIN:
00213                 PutByte(fp, B);
00214                 PutByte(fp, G);
00215                 PutByte(fp, R);
00216                 PutByte(fp, 0);
00217                 return 4;
00218         case C_OS2:
00219                 PutByte(fp, B);
00220                 PutByte(fp, G);
00221                 PutByte(fp, R);
00222                 return 3;
00223         default:
00224                 pm_error(er_internal, "BMPwritergb");
00225         }
00226         return -1;
00227 }
00228 
00229 /*
00230  * returns the number of bytes written, or -1 on error.
00231  */
00232 static int
00233 BMPwritergbtable(
00234         ostream           *fp,
00235         int             classv,
00236         int             bpp,
00237         int             colors,
00238         pixval         *R,
00239         pixval         *G,
00240         pixval         *B)
00241 {
00242         int             nbyte = 0;
00243         int             i;
00244         long            ncolors;
00245 
00246         for (i = 0; i < colors; i++)
00247         {
00248                 nbyte += BMPwritergb(fp,classv,R[i],G[i],B[i]);
00249         }
00250 
00251         ncolors = (1 << bpp);
00252 
00253         for (; i < ncolors; i++)
00254         {
00255                 nbyte += BMPwritergb(fp,classv,0,0,0);
00256         }
00257 
00258         return nbyte;
00259 }
00260 
00261 /*
00262  * returns the number of bytes written, or -1 on error.
00263  */
00264 static int
00265 BMPwriterow(
00266         ostream           *fp,
00267         pixel          *row,
00268         unsigned long   cx,
00269         unsigned short  bpp,
00270         int             indexed,
00271         colorhash_table cht,
00272         xelval maxval)
00273 {
00274         BITSTREAM       b;
00275         unsigned        nbyte = 0;
00276         int             rc;
00277         unsigned        x;
00278 
00279         if (indexed) {
00280           if ((b = pm_bitinit(fp, "w")) == (BITSTREAM) 0)
00281             {
00282               return -1;
00283             }
00284 
00285           for (x = 0; x < cx; x++, row++)
00286             {
00287               if ((rc = pm_bitwrite(b, bpp, ppm_lookupcolor(cht, row))) == -1)
00288                 {
00289                   return -1;
00290                 }
00291               nbyte += rc;
00292             }
00293 
00294           if ((rc = pm_bitfini(b)) == -1)
00295             {
00296               return -1;
00297             }
00298           nbyte += rc;
00299         } else {
00300 
00301           for (x = 0; x < cx; x++, row++)
00302             {
00303               PutByte(fp, PPM_GETB(*row) * 255 / maxval);
00304               PutByte(fp, PPM_GETG(*row) * 255 / maxval);
00305               PutByte(fp, PPM_GETR(*row) * 255 / maxval);
00306               nbyte += 3;
00307             }
00308         }
00309 
00310         /*
00311          * Make sure we write a multiple of 4 bytes.
00312          */
00313         while (nbyte % 4)
00314         {
00315                 PutByte(fp, 0);
00316                 nbyte++;
00317         }
00318 
00319         return nbyte;
00320 }
00321 
00322 /*
00323  * returns the number of bytes written, or -1 on error.
00324  */
00325 static int
00326 BMPwritebits(
00327         ostream           *fp,
00328         unsigned long   cx,
00329         unsigned long   cy,
00330         unsigned short  cBitCount,
00331         pixel         **pixels,
00332         int             indexed,
00333         colorhash_table cht,
00334         xelval maxval)
00335 {
00336         int             nbyte = 0;
00337         long            y;
00338 
00339         if(cBitCount > 24)
00340         {
00341                 pm_error("cannot handle cBitCount: %d"
00342                          ,cBitCount);
00343         }
00344 
00345         /*
00346          * The picture is stored bottom line first, top line last
00347          */
00348 
00349         for (y = (long)cy - 1; y >= 0; y--)
00350         {
00351                 int rc;
00352                 rc = BMPwriterow(fp, pixels[y], cx, cBitCount, indexed, cht,
00353                                  maxval);
00354 
00355                 if(rc == -1)
00356                 {
00357                         pm_error("couldn't write row %d"
00358                                  ,y);
00359                 }
00360                 if(rc%4)
00361                 {
00362                         pm_error("row had bad number of bytes: %d"
00363                                  ,rc);
00364                 }
00365                 nbyte += rc;
00366         }
00367 
00368         return nbyte;
00369 }
00370 
00371 /*
00372  * Return the number of bits per pixel required to represent the
00373  * given number of colors.
00374  */
00375 
00376 static int
00377 colorstobpp(int colors)
00378 {
00379         int             bpp;
00380 
00381         if (colors < 1)
00382         {
00383                 pm_error("can't have less than one color");
00384         }
00385 
00386         if ((bpp = pm_maxvaltobits(colors - 1)) > 8)
00387         {
00388                 pm_error("can't happen");
00389         }
00390 
00391         return bpp;
00392 }
00393 
00394 /*
00395  * Write a BMP file of the given classv.
00396  *
00397  * Note that we must have 'colors' in order to know exactly how many
00398  * colors are in the R, G, B, arrays.  Entries beyond those in the
00399  * arrays are undefined.
00400  */
00401 static void
00402 BMPEncode(
00403         ostream           *fp,
00404         int             classv,
00405         int             x,
00406         int             y,
00407         pixel         **pixels,
00408         int             colors, /* number of valid entries in R,G,B */
00409         colorhash_table cht,
00410         pixval         *R,
00411         pixval         *G,
00412         pixval         *B)
00413 {
00414         int             bpp;    /* bits per pixel */
00415         unsigned long   nbyte = 0;
00416 
00417         bpp = bmp_bpp;
00418         int needs_bpp = colorstobpp(colors);
00419         if (bpp != 0 && bpp < needs_bpp) {
00420           pnmimage_bmp_cat.info()
00421             << "too many colors for " << bmp_bpp << "-bit image.\n";
00422           bpp = 0;
00423         }
00424 
00425         if (bpp == 0) {
00426           bpp = needs_bpp;
00427 
00428           /*
00429            * I have found empirically at least one BMP-displaying program
00430            * that can't deal with (for instance) using 3 bits per pixel.
00431            * I have seen no programs that can deal with using 3 bits per
00432            * pixel.  I have seen programs which can deal with 1, 4, and
00433            * 8 bits per pixel.
00434            *
00435            * Based on this, I adjust actual the number of bits per pixel
00436            * as follows.  If anyone knows better, PLEASE tell me!
00437            */
00438           switch(bpp)
00439             {
00440             case 2:
00441             case 3:
00442               bpp = 4;
00443               break;
00444             case 5:
00445             case 6:
00446             case 7:
00447               bpp = 8;
00448               break;
00449             }
00450         }
00451 
00452         pnmimage_bmp_cat.info()
00453           << "Using " << bpp << " bits per pixel.\n";
00454 
00455         nbyte += BMPwritefileheader(fp, classv, bpp, x, y);
00456         nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y);
00457         nbyte += BMPwritergbtable(fp, classv, bpp, colors, R, G, B);
00458 
00459         if(nbyte !=     ( BMPlenfileheader(classv)
00460                         + BMPleninfoheader(classv)
00461                         + BMPlenrgbtable(classv, bpp)))
00462         {
00463                 pm_error(er_internal, "BMPEncode");
00464         }
00465 
00466         nbyte += BMPwritebits(fp, x, y, bpp, pixels, true, cht, 255);
00467         if(nbyte != BMPlenfile(classv, bpp, x, y))
00468         {
00469                 pm_error(er_internal, "BMPEncode");
00470         }
00471 }
00472 
00473 /*
00474  * Write a BMP file of the given class, with 24 bits per pixel nonindexed.
00475  */
00476 static void
00477 BMPEncode24(
00478         ostream           *fp,
00479         int             classv,
00480         int             x,
00481         int             y,
00482         pixel         **pixels,
00483         xelval maxval)
00484 {
00485         unsigned long   nbyte = 0;
00486         int             bpp = 24;
00487 
00488         pnmimage_bmp_cat.info()
00489           << "Using " << bpp << " bits per pixel.\n";
00490 
00491         nbyte += BMPwritefileheader(fp, classv, bpp, x, y);
00492         nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y);
00493 
00494         if(nbyte !=     ( BMPlenfileheader(classv)
00495                         + BMPleninfoheader(classv)))
00496         {
00497                 pm_error(er_internal, "BMPEncode24");
00498         }
00499 
00500         nbyte += BMPwritebits(fp, x, y, bpp, pixels, false, colorhash_table(),
00501                               maxval);
00502         if(nbyte != BMPlenfile(classv, bpp, x, y))
00503         {
00504                 pm_error(er_internal, "BMPEncode24");
00505         }
00506 }
00507 
00508 
00509 ////////////////////////////////////////////////////////////////////
00510 //     Function: PNMFileTypeBMP::Writer::Constructor
00511 //       Access: Public
00512 //  Description:
00513 ////////////////////////////////////////////////////////////////////
00514 PNMFileTypeBMP::Writer::
00515 Writer(PNMFileType *type, ostream *file, bool owns_file) :
00516   PNMWriter(type, file, owns_file)
00517 {
00518 }
00519 
00520 
00521 ////////////////////////////////////////////////////////////////////
00522 //     Function: PNMFileTypeBMP::Writer::write_data
00523 //       Access: Public, Virtual
00524 //  Description: Writes out an entire image all at once, including the
00525 //               header, based on the image data stored in the given
00526 //               _x_size * _y_size array and alpha pointers.  (If the
00527 //               image type has no alpha channel, alpha is ignored.)
00528 //               Returns the number of rows correctly written.
00529 //
00530 //               It is the user's responsibility to fill in the header
00531 //               data via calls to set_x_size(), set_num_channels(),
00532 //               etc., or copy_header_from(), before calling
00533 //               write_data().
00534 //
00535 //               It is important to delete the PNMWriter class after
00536 //               successfully writing the data.  Failing to do this
00537 //               may result in some data not getting flushed!
00538 //
00539 //               Derived classes need not override this if they
00540 //               instead provide supports_streaming() and write_row(),
00541 //               below.
00542 ////////////////////////////////////////////////////////////////////
00543 int PNMFileTypeBMP::Writer::
00544 write_data(xel *array, xelval *) {
00545   if (_y_size<=0 || _x_size<=0) {
00546     return 0;
00547   }
00548 
00549   int             classv = C_WIN;
00550 
00551   int             colors;
00552   int             i;
00553   colorhist_vector chv;
00554   pixval          Red[MAXCOLORS];
00555   pixval          Green[MAXCOLORS];
00556   pixval          Blue[MAXCOLORS];
00557 
00558   pixel** pixels;
00559   colorhash_table cht;
00560 
00561 #if 0
00562   {
00563     char *name;
00564     switch (classv)
00565       {
00566       case C_WIN:
00567         name = "a Windows";
00568         break;
00569       case C_OS2:
00570         name = "an OS/2";
00571         break;
00572       default:
00573         pm_error(er_internal, "report");
00574         break;
00575       }
00576     pm_message("generating %s BMP file", name);
00577   }
00578 #endif
00579 
00580   // We need an honest 2-d array of pixels, instead of one big 1-d array.
00581   pixels = (pixel **)alloca(sizeof(pixel *) * _y_size);
00582   for (i = 0; i < _y_size; i++) {
00583     pixels[i] = (pixel *)(array + i * _x_size);
00584   }
00585 
00586   /* Figure out the colormap. */
00587   chv = ppm_computecolorhist(pixels, _x_size, _y_size, MAXCOLORS, &colors);
00588   if (bmp_bpp > 8) {
00589     // Quietly generate a 24-bit image.
00590     BMPEncode24(_file, classv, _x_size, _y_size, pixels, _maxval);
00591 
00592   } else if (chv == (colorhist_vector) 0) {
00593     if (bmp_bpp != 0) {
00594       // Even though we asked for fewer bits, we have to settle for 24-bit.
00595       pnmimage_bmp_cat.info()
00596         << "too many colors for " << bmp_bpp << "-bit image.\n";
00597     }
00598 
00599     BMPEncode24(_file, classv, _x_size, _y_size, pixels, _maxval);
00600 
00601   } else {
00602     pnmimage_bmp_cat.debug()
00603       << colors << " colors found\n";
00604 
00605     /*
00606      * Now turn the ppm colormap into the appropriate BMP colormap.
00607      */
00608     if (_maxval > 255) {
00609       pnmimage_bmp_cat.debug()
00610         << "maxval is not 255 - automatically rescaling colors\n";
00611     }
00612 
00613     for (i = 0; i < colors; ++i) {
00614       if (_maxval == 255) {
00615         Red[i] = PPM_GETR(chv[i].color);
00616         Green[i] = PPM_GETG(chv[i].color);
00617         Blue[i] = PPM_GETB(chv[i].color);
00618       } else {
00619         Red[i] = (pixval) PPM_GETR(chv[i].color) * 255 / _maxval;
00620         Green[i] = (pixval) PPM_GETG(chv[i].color) * 255 / _maxval;
00621         Blue[i] = (pixval) PPM_GETB(chv[i].color) * 255 / _maxval;
00622       }
00623     }
00624 
00625     /* And make a hash table for fast lookup. */
00626     cht = ppm_colorhisttocolorhash(chv, colors);
00627     ppm_freecolorhist(chv);
00628 
00629     BMPEncode(_file, classv, _x_size, _y_size, pixels, colors, cht,
00630               Red, Green, Blue);
00631   }
00632   Thread::consider_yield();
00633 
00634   return _y_size;
00635 }
00636 
00637 ////////////////////////////////////////////////////////////////////
00638 //     Function: PNMFileTypeBMP::Writer::supports_grayscale
00639 //       Access: Public, Virtual
00640 //  Description: Returns true if this particular PNMWriter understands
00641 //               grayscale images.  If this is false, then the rgb
00642 //               values of the xel array will be pre-filled with the
00643 //               same value across all three channels, to allow the
00644 //               writer to simply write out RGB data for a grayscale
00645 //               image.
00646 ////////////////////////////////////////////////////////////////////
00647 bool PNMFileTypeBMP::Writer::
00648 supports_grayscale() const {
00649   return false;
00650 }
00651 
00652 #endif  // HAVE_BMP
 All Classes Functions Variables Enumerations