Panda3D
|
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