Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/pnmimagetypes/pnmFileTypeBMPWriter.cxx

Go to the documentation of this file.
00001 // Filename: pnmFileTypeBMPWriter.cxx
00002 // Created by:  drose (19Jun00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "pnmFileTypeBMP.h"
00020 #include "config_pnmimagetypes.h"
00021 
00022 #include "pnmImage.h"
00023 #include "pnmWriter.h"
00024 
00025 #include "bmp.h"
00026 #include "ppmcmap.h"
00027 #include "pnmbitio.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;
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 {
00273         BITSTREAM       b;
00274         unsigned        nbyte = 0;
00275         int             rc;
00276         unsigned        x;
00277 
00278         if (indexed) {
00279           if ((b = pm_bitinit(fp, "w")) == (BITSTREAM) 0)
00280             {
00281               return -1;
00282             }
00283 
00284           for (x = 0; x < cx; x++, row++)
00285             {
00286               if ((rc = pm_bitwrite(b, bpp, ppm_lookupcolor(cht, row))) == -1)
00287                 {
00288                   return -1;
00289                 }
00290               nbyte += rc;
00291             }
00292 
00293           if ((rc = pm_bitfini(b)) == -1)
00294             {
00295               return -1;
00296             }
00297           nbyte += rc;
00298         } else {
00299 
00300           for (x = 0; x < cx; x++, row++)
00301             {
00302               PutByte(fp, PPM_GETB(*row));
00303               PutByte(fp, PPM_GETG(*row));
00304               PutByte(fp, PPM_GETR(*row));
00305               nbyte += 3;
00306             }
00307         }
00308 
00309         /*
00310          * Make sure we write a multiple of 4 bytes.
00311          */
00312         while (nbyte % 4)
00313         {
00314                 PutByte(fp, 0);
00315                 nbyte++;
00316         }
00317 
00318         return nbyte;
00319 }
00320 
00321 /*
00322  * returns the number of bytes written, or -1 on error.
00323  */
00324 static int
00325 BMPwritebits(
00326         ostream           *fp,
00327         unsigned long   cx,
00328         unsigned long   cy,
00329         unsigned short  cBitCount,
00330         pixel         **pixels,
00331         int             indexed,
00332         colorhash_table cht)
00333 {
00334         int             nbyte = 0;
00335         long            y;
00336 
00337         if(cBitCount > 24)
00338         {
00339                 pm_error("cannot handle cBitCount: %d"
00340                          ,cBitCount);
00341         }
00342 
00343         /*
00344          * The picture is stored bottom line first, top line last
00345          */
00346 
00347         for (y = (long)cy - 1; y >= 0; y--)
00348         {
00349                 int rc;
00350                 rc = BMPwriterow(fp, pixels[y], cx, cBitCount, indexed, cht);
00351 
00352                 if(rc == -1)
00353                 {
00354                         pm_error("couldn't write row %d"
00355                                  ,y);
00356                 }
00357                 if(rc%4)
00358                 {
00359                         pm_error("row had bad number of bytes: %d"
00360                                  ,rc);
00361                 }
00362                 nbyte += rc;
00363         }
00364 
00365         return nbyte;
00366 }
00367 
00368 /*
00369  * Return the number of bits per pixel required to represent the
00370  * given number of colors.
00371  */
00372 
00373 static int
00374 colorstobpp(int colors)
00375 {
00376         int             bpp;
00377 
00378         if (colors < 1)
00379         {
00380                 pm_error("can't have less than one color");
00381         }
00382 
00383         if ((bpp = pm_maxvaltobits(colors - 1)) > 8)
00384         {
00385                 pm_error("can't happen");
00386         }
00387 
00388         return bpp;
00389 }
00390 
00391 /*
00392  * Write a BMP file of the given classv.
00393  *
00394  * Note that we must have 'colors' in order to know exactly how many
00395  * colors are in the R, G, B, arrays.  Entries beyond those in the
00396  * arrays are undefined.
00397  */
00398 static void
00399 BMPEncode(
00400         ostream           *fp,
00401         int             classv,
00402         int             x,
00403         int             y,
00404         pixel         **pixels,
00405         int             colors, /* number of valid entries in R,G,B */
00406         colorhash_table cht,
00407         pixval         *R,
00408         pixval         *G,
00409         pixval         *B)
00410 {
00411         int             bpp;    /* bits per pixel */
00412         unsigned long   nbyte = 0;
00413 
00414         bpp = bmp_bpp;
00415         int needs_bpp = colorstobpp(colors);
00416         if (bpp != 0 && bpp < needs_bpp) {
00417           pnmimage_bmp_cat.info()
00418             << "too many colors for " << bmp_bpp << "-bit image.\n";
00419           bpp = 0;
00420         }
00421 
00422         if (bpp == 0) {
00423           bpp = needs_bpp;
00424 
00425           /*
00426            * I have found empirically at least one BMP-displaying program
00427            * that can't deal with (for instance) using 3 bits per pixel.
00428            * I have seen no programs that can deal with using 3 bits per
00429            * pixel.  I have seen programs which can deal with 1, 4, and
00430            * 8 bits per pixel.
00431            *
00432            * Based on this, I adjust actual the number of bits per pixel
00433            * as follows.  If anyone knows better, PLEASE tell me!
00434            */
00435           switch(bpp)
00436             {
00437             case 2:
00438             case 3:
00439               bpp = 4;
00440               break;
00441             case 5:
00442             case 6:
00443             case 7:
00444               bpp = 8;
00445               break;
00446             }
00447         }
00448 
00449         pnmimage_bmp_cat.info()
00450           << "Using " << bpp << " bits per pixel.\n";
00451 
00452         nbyte += BMPwritefileheader(fp, classv, bpp, x, y);
00453         nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y);
00454         nbyte += BMPwritergbtable(fp, classv, bpp, colors, R, G, B);
00455 
00456         if(nbyte !=     ( BMPlenfileheader(classv)
00457                         + BMPleninfoheader(classv)
00458                         + BMPlenrgbtable(classv, bpp)))
00459         {
00460                 pm_error(er_internal, "BMPEncode");
00461         }
00462 
00463         nbyte += BMPwritebits(fp, x, y, bpp, pixels, true, cht);
00464         if(nbyte != BMPlenfile(classv, bpp, x, y))
00465         {
00466                 pm_error(er_internal, "BMPEncode");
00467         }
00468 }
00469 
00470 /*
00471  * Write a BMP file of the given class, with 24 bits per pixel nonindexed.
00472  */
00473 static void
00474 BMPEncode24(
00475         ostream           *fp,
00476         int             classv,
00477         int             x,
00478         int             y,
00479         pixel         **pixels)
00480 {
00481         unsigned long   nbyte = 0;
00482         int             bpp = 24;
00483 
00484         pnmimage_bmp_cat.info()
00485           << "Using " << bpp << " bits per pixel.\n";
00486 
00487         nbyte += BMPwritefileheader(fp, classv, bpp, x, y);
00488         nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y);
00489 
00490         if(nbyte !=     ( BMPlenfileheader(classv)
00491                         + BMPleninfoheader(classv)))
00492         {
00493                 pm_error(er_internal, "BMPEncode24");
00494         }
00495 
00496         nbyte += BMPwritebits(fp, x, y, bpp, pixels, false, colorhash_table());
00497         if(nbyte != BMPlenfile(classv, bpp, x, y))
00498         {
00499                 pm_error(er_internal, "BMPEncode24");
00500         }
00501 }
00502 
00503 
00504 ////////////////////////////////////////////////////////////////////
00505 //     Function: PNMFileTypeBMP::Writer::Constructor
00506 //       Access: Public
00507 //  Description:
00508 ////////////////////////////////////////////////////////////////////
00509 PNMFileTypeBMP::Writer::
00510 Writer(PNMFileType *type, ostream *file, bool owns_file) :
00511   PNMWriter(type, file, owns_file)
00512 {
00513 }
00514 
00515 
00516 ////////////////////////////////////////////////////////////////////
00517 //     Function: PNMFileTypeBMP::Writer::write_data
00518 //       Access: Public, Virtual
00519 //  Description: Writes out an entire image all at once, including the
00520 //               header, based on the image data stored in the given
00521 //               _x_size * _y_size array and alpha pointers.  (If the
00522 //               image type has no alpha channel, alpha is ignored.)
00523 //               Returns the number of rows correctly written.
00524 //
00525 //               It is the user's responsibility to fill in the header
00526 //               data via calls to set_x_size(), set_num_channels(),
00527 //               etc., or copy_header_from(), before calling
00528 //               write_data().
00529 //
00530 //               It is important to delete the PNMWriter class after
00531 //               successfully writing the data.  Failing to do this
00532 //               may result in some data not getting flushed!
00533 //
00534 //               Derived classes need not override this if they
00535 //               instead provide supports_streaming() and write_row(),
00536 //               below.
00537 ////////////////////////////////////////////////////////////////////
00538 int PNMFileTypeBMP::Writer::
00539 write_data(xel *array, xelval *) {
00540   if (_y_size<=0 || _x_size<=0) {
00541     return 0;
00542   }
00543 
00544   int             classv = C_WIN;
00545 
00546   int             colors;
00547   int             i;
00548   colorhist_vector chv;
00549   pixval          Red[MAXCOLORS];
00550   pixval          Green[MAXCOLORS];
00551   pixval          Blue[MAXCOLORS];
00552 
00553   pixel** pixels;
00554   colorhash_table cht;
00555 
00556 #if 0
00557   {
00558     char *name;
00559     switch (classv)
00560       {
00561       case C_WIN:
00562         name = "a Windows";
00563         break;
00564       case C_OS2:
00565         name = "an OS/2";
00566         break;
00567       default:
00568         pm_error(er_internal, "report");
00569         break;
00570       }
00571     pm_message("generating %s BMP file", name);
00572   }
00573 #endif
00574 
00575   // We need an honest 2-d array of pixels, instead of one big 1-d array.
00576   pixels = (pixel **)alloca(sizeof(pixel *) * _y_size);
00577   for (i = 0; i < _y_size; i++) {
00578     pixels[i] = (pixel *)(array + i * _x_size);
00579   }
00580 
00581   /* Figure out the colormap. */
00582   chv = ppm_computecolorhist(pixels, _x_size, _y_size, MAXCOLORS, &colors);
00583   if (bmp_bpp > 8) {
00584     // Quietly generate a 24-bit image.
00585     BMPEncode24(_file, classv, _x_size, _y_size, pixels);
00586 
00587   } else if (chv == (colorhist_vector) 0) {
00588     if (bmp_bpp != 0) {
00589       // Even though we asked for fewer bits, we have to settle for 24-bit.
00590       pnmimage_bmp_cat.info()
00591         << "too many colors for " << bmp_bpp << "-bit image.\n";
00592     }
00593 
00594     BMPEncode24(_file, classv, _x_size, _y_size, pixels);
00595 
00596   } else {
00597     pnmimage_bmp_cat.debug()
00598       << colors << " colors found\n";
00599 
00600     /*
00601      * Now turn the ppm colormap into the appropriate BMP colormap.
00602      */
00603     if (_maxval > 255) {
00604       pnmimage_bmp_cat.debug()
00605         << "maxval is not 255 - automatically rescaling colors\n";
00606     }
00607 
00608     for (i = 0; i < colors; ++i) {
00609       if (_maxval == 255) {
00610         Red[i] = PPM_GETR(chv[i].color);
00611         Green[i] = PPM_GETG(chv[i].color);
00612         Blue[i] = PPM_GETB(chv[i].color);
00613       } else {
00614         Red[i] = (pixval) PPM_GETR(chv[i].color) * 255 / _maxval;
00615         Green[i] = (pixval) PPM_GETG(chv[i].color) * 255 / _maxval;
00616         Blue[i] = (pixval) PPM_GETB(chv[i].color) * 255 / _maxval;
00617       }
00618     }
00619 
00620     /* And make a hash table for fast lookup. */
00621     cht = ppm_colorhisttocolorhash(chv, colors);
00622     ppm_freecolorhist(chv);
00623 
00624     BMPEncode(_file, classv, _x_size, _y_size, pixels, colors, cht,
00625               Red, Green, Blue);
00626   }
00627 
00628   return _y_size;
00629 }

Generated on Fri May 2 00:43:15 2003 for Panda by doxygen1.3