Panda3D
 All Classes Functions Variables Enumerations
pnmFileTypeBMPWriter.cxx
1 // Filename: pnmFileTypeBMPWriter.cxx
2 // Created by: drose (19Jun00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pnmFileTypeBMP.h"
16 
17 #ifdef HAVE_BMP
18 
19 #include "config_pnmimagetypes.h"
20 
21 #include "pnmImage.h"
22 #include "pnmWriter.h"
23 
24 #include "bmp.h"
25 #include "ppmcmap.h"
26 #include "pnmbitio.h"
27 #include "thread.h"
28 
29 // Much code in this file is borrowed from Netpbm, specifically ppmtobmp.c.
30 /*
31  * ppmtobmp.c - Converts from a PPM file to a Microsoft Windows or OS/2
32  * .BMP file.
33  *
34  * The current implementation is probably not complete, but it works for
35  * me. I welcome feedback.
36  *
37  * Copyright (C) 1992 by David W. Sanderson.
38  *
39  * Permission to use, copy, modify, and distribute this software and its
40  * documentation for any purpose and without fee is hereby granted,
41  * provided that the above copyright notice appear in all copies and
42  * that both that copyright notice and this permission notice appear
43  * in supporting documentation. This software is provided "as is"
44  * without express or implied warranty.
45  */
46 
47 #define MAXCOLORS 256
48 
49 /*
50  * Utilities
51  */
52 
53 static char er_write[] = "stdout: write error";
54 
55 /* prototypes */
56 static void PutByte (ostream *fp, char v);
57 static void PutShort (ostream *fp, short v);
58 static void PutLong (ostream *fp, long v);
59 static int BMPwritefileheader (ostream *fp, int classv, unsigned long bitcount,
60  unsigned long x, unsigned long y);
61 static int BMPwriteinfoheader (ostream *fp, int classv, unsigned long bitcount,
62  unsigned long x, unsigned long y);
63 static int BMPwritergb (ostream *fp, int classv, pixval R, pixval G, pixval B);
64 static int BMPwritergbtable (ostream *fp, int classv, int bpp, int colors,
65  pixval *R, pixval *G, pixval *B);
66 static int colorstobpp (int colors);
67 static void BMPEncode (ostream *fp, int classv, int x, int y, pixel **pixels,
68  int colors, colorhash_table cht, pixval *R, pixval *G, pixval *B);
69 static void
70 PutByte(
71  ostream *fp,
72  char v)
73 {
74  if (!fp->put(v))
75  {
76  pm_error(er_write);
77  }
78 }
79 
80 static void
81 PutShort(
82  ostream *fp,
83  short v)
84 {
85  if (pm_writelittleshort(fp, v) == -1)
86  {
87  pm_error(er_write);
88  }
89 }
90 
91 static void
92 PutLong(
93  ostream *fp,
94  long v)
95 {
96  if (pm_writelittlelong(fp, v) == -1)
97  {
98  pm_error(er_write);
99  }
100 }
101 
102 /*
103  * BMP writing
104  */
105 
106 /*
107  * returns the number of bytes written, or -1 on error.
108  */
109 static int
110 BMPwritefileheader(
111  ostream *fp,
112  int classv,
113  unsigned long bitcount,
114  unsigned long x,
115  unsigned long y)
116 {
117  PutByte(fp, 'B');
118  PutByte(fp, 'M');
119 
120  /* cbSize */
121  PutLong(fp, (long)BMPlenfile(classv, bitcount, x, y));
122 
123  /* xHotSpot */
124  PutShort(fp, 0);
125 
126  /* yHotSpot */
127  PutShort(fp, 0);
128 
129  /* offBits */
130  PutLong(fp, (long)BMPoffbits(classv, bitcount));
131 
132  return 14;
133 }
134 
135 /*
136  * returns the number of bytes written, or -1 on error.
137  */
138 static int
139 BMPwriteinfoheader(
140  ostream *fp,
141  int classv,
142  unsigned long bitcount,
143  unsigned long x,
144  unsigned long y)
145 {
146  long cbFix = 0;
147 
148  /* cbFix */
149  switch (classv)
150  {
151  case C_WIN:
152  cbFix = 40;
153  PutLong(fp, cbFix);
154 
155  /* cx */
156  PutLong(fp, (long)x);
157  /* cy */
158  PutLong(fp, (long)y);
159  /* cPlanes */
160  PutShort(fp, 1);
161  /* cBitCount */
162  PutShort(fp, (short)bitcount);
163 
164  /*
165  * We've written 16 bytes so far, need to write 24 more
166  * for the required total of 40.
167  */
168 
169  PutLong(fp, 0);
170  PutLong(fp, 0);
171  PutLong(fp, 0);
172  PutLong(fp, 0);
173  PutLong(fp, 0);
174  PutLong(fp, 0);
175 
176 
177  break;
178  case C_OS2:
179  cbFix = 12;
180  PutLong(fp, cbFix);
181 
182  /* cx */
183  PutShort(fp, (short)x);
184  /* cy */
185  PutShort(fp, (short)y);
186  /* cPlanes */
187  PutShort(fp, 1);
188  /* cBitCount */
189  PutShort(fp, (short)bitcount);
190 
191  break;
192  default:
193  pm_error(er_internal, "BMPwriteinfoheader");
194  }
195 
196  return cbFix;
197 }
198 
199 /*
200  * returns the number of bytes written, or -1 on error.
201  */
202 static int
203 BMPwritergb(
204  ostream *fp,
205  int classv,
206  pixval R,
207  pixval G,
208  pixval B)
209 {
210  switch (classv)
211  {
212  case C_WIN:
213  PutByte(fp, B);
214  PutByte(fp, G);
215  PutByte(fp, R);
216  PutByte(fp, 0);
217  return 4;
218  case C_OS2:
219  PutByte(fp, B);
220  PutByte(fp, G);
221  PutByte(fp, R);
222  return 3;
223  default:
224  pm_error(er_internal, "BMPwritergb");
225  }
226  return -1;
227 }
228 
229 /*
230  * returns the number of bytes written, or -1 on error.
231  */
232 static int
233 BMPwritergbtable(
234  ostream *fp,
235  int classv,
236  int bpp,
237  int colors,
238  pixval *R,
239  pixval *G,
240  pixval *B)
241 {
242  int nbyte = 0;
243  int i;
244  long ncolors;
245 
246  for (i = 0; i < colors; i++)
247  {
248  nbyte += BMPwritergb(fp,classv,R[i],G[i],B[i]);
249  }
250 
251  ncolors = (1 << bpp);
252 
253  for (; i < ncolors; i++)
254  {
255  nbyte += BMPwritergb(fp,classv,0,0,0);
256  }
257 
258  return nbyte;
259 }
260 
261 /*
262  * returns the number of bytes written, or -1 on error.
263  */
264 static int
265 BMPwriterow(
266  ostream *fp,
267  pixel *row,
268  unsigned long cx,
269  unsigned short bpp,
270  int indexed,
271  colorhash_table cht,
272  xelval maxval)
273 {
274  BITSTREAM b;
275  unsigned nbyte = 0;
276  int rc;
277  unsigned x;
278 
279  if (indexed) {
280  if ((b = pm_bitinit(fp, "w")) == (BITSTREAM) 0)
281  {
282  return -1;
283  }
284 
285  for (x = 0; x < cx; x++, row++)
286  {
287  if ((rc = pm_bitwrite(b, bpp, ppm_lookupcolor(cht, row))) == -1)
288  {
289  return -1;
290  }
291  nbyte += rc;
292  }
293 
294  if ((rc = pm_bitfini(b)) == -1)
295  {
296  return -1;
297  }
298  nbyte += rc;
299  } else {
300 
301  for (x = 0; x < cx; x++, row++)
302  {
303  PutByte(fp, PPM_GETB(*row) * 255 / maxval);
304  PutByte(fp, PPM_GETG(*row) * 255 / maxval);
305  PutByte(fp, PPM_GETR(*row) * 255 / maxval);
306  nbyte += 3;
307  }
308  }
309 
310  /*
311  * Make sure we write a multiple of 4 bytes.
312  */
313  while (nbyte % 4)
314  {
315  PutByte(fp, 0);
316  nbyte++;
317  }
318 
319  return nbyte;
320 }
321 
322 /*
323  * returns the number of bytes written, or -1 on error.
324  */
325 static int
326 BMPwritebits(
327  ostream *fp,
328  unsigned long cx,
329  unsigned long cy,
330  unsigned short cBitCount,
331  pixel **pixels,
332  int indexed,
333  colorhash_table cht,
334  xelval maxval)
335 {
336  int nbyte = 0;
337  long y;
338 
339  if(cBitCount > 24)
340  {
341  pm_error("cannot handle cBitCount: %d"
342  ,cBitCount);
343  }
344 
345  /*
346  * The picture is stored bottom line first, top line last
347  */
348 
349  for (y = (long)cy - 1; y >= 0; y--)
350  {
351  int rc;
352  rc = BMPwriterow(fp, pixels[y], cx, cBitCount, indexed, cht,
353  maxval);
354 
355  if(rc == -1)
356  {
357  pm_error("couldn't write row %d"
358  ,y);
359  }
360  if(rc%4)
361  {
362  pm_error("row had bad number of bytes: %d"
363  ,rc);
364  }
365  nbyte += rc;
366  }
367 
368  return nbyte;
369 }
370 
371 /*
372  * Return the number of bits per pixel required to represent the
373  * given number of colors.
374  */
375 
376 static int
377 colorstobpp(int colors)
378 {
379  int bpp;
380 
381  if (colors < 1)
382  {
383  pm_error("can't have less than one color");
384  }
385 
386  if ((bpp = pm_maxvaltobits(colors - 1)) > 8)
387  {
388  pm_error("can't happen");
389  }
390 
391  return bpp;
392 }
393 
394 /*
395  * Write a BMP file of the given classv.
396  *
397  * Note that we must have 'colors' in order to know exactly how many
398  * colors are in the R, G, B, arrays. Entries beyond those in the
399  * arrays are undefined.
400  */
401 static void
402 BMPEncode(
403  ostream *fp,
404  int classv,
405  int x,
406  int y,
407  pixel **pixels,
408  int colors, /* number of valid entries in R,G,B */
409  colorhash_table cht,
410  pixval *R,
411  pixval *G,
412  pixval *B)
413 {
414  int bpp; /* bits per pixel */
415  unsigned long nbyte = 0;
416 
417  bpp = bmp_bpp;
418  int needs_bpp = colorstobpp(colors);
419  if (bpp != 0 && bpp < needs_bpp) {
420  pnmimage_bmp_cat.info()
421  << "too many colors for " << bmp_bpp << "-bit image.\n";
422  bpp = 0;
423  }
424 
425  if (bpp == 0) {
426  bpp = needs_bpp;
427 
428  /*
429  * I have found empirically at least one BMP-displaying program
430  * that can't deal with (for instance) using 3 bits per pixel.
431  * I have seen no programs that can deal with using 3 bits per
432  * pixel. I have seen programs which can deal with 1, 4, and
433  * 8 bits per pixel.
434  *
435  * Based on this, I adjust actual the number of bits per pixel
436  * as follows. If anyone knows better, PLEASE tell me!
437  */
438  switch(bpp)
439  {
440  case 2:
441  case 3:
442  bpp = 4;
443  break;
444  case 5:
445  case 6:
446  case 7:
447  bpp = 8;
448  break;
449  }
450  }
451 
452  pnmimage_bmp_cat.info()
453  << "Using " << bpp << " bits per pixel.\n";
454 
455  nbyte += BMPwritefileheader(fp, classv, bpp, x, y);
456  nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y);
457  nbyte += BMPwritergbtable(fp, classv, bpp, colors, R, G, B);
458 
459  if(nbyte != ( BMPlenfileheader(classv)
460  + BMPleninfoheader(classv)
461  + BMPlenrgbtable(classv, bpp)))
462  {
463  pm_error(er_internal, "BMPEncode");
464  }
465 
466  nbyte += BMPwritebits(fp, x, y, bpp, pixels, true, cht, 255);
467  if(nbyte != BMPlenfile(classv, bpp, x, y))
468  {
469  pm_error(er_internal, "BMPEncode");
470  }
471 }
472 
473 /*
474  * Write a BMP file of the given class, with 24 bits per pixel nonindexed.
475  */
476 static void
477 BMPEncode24(
478  ostream *fp,
479  int classv,
480  int x,
481  int y,
482  pixel **pixels,
483  xelval maxval)
484 {
485  unsigned long nbyte = 0;
486  int bpp = 24;
487 
488  pnmimage_bmp_cat.info()
489  << "Using " << bpp << " bits per pixel.\n";
490 
491  nbyte += BMPwritefileheader(fp, classv, bpp, x, y);
492  nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y);
493 
494  if(nbyte != ( BMPlenfileheader(classv)
495  + BMPleninfoheader(classv)))
496  {
497  pm_error(er_internal, "BMPEncode24");
498  }
499 
500  nbyte += BMPwritebits(fp, x, y, bpp, pixels, false, colorhash_table(),
501  maxval);
502  if(nbyte != BMPlenfile(classv, bpp, x, y))
503  {
504  pm_error(er_internal, "BMPEncode24");
505  }
506 }
507 
508 
509 ////////////////////////////////////////////////////////////////////
510 // Function: PNMFileTypeBMP::Writer::Constructor
511 // Access: Public
512 // Description:
513 ////////////////////////////////////////////////////////////////////
514 PNMFileTypeBMP::Writer::
515 Writer(PNMFileType *type, ostream *file, bool owns_file) :
516  PNMWriter(type, file, owns_file)
517 {
518 }
519 
520 
521 ////////////////////////////////////////////////////////////////////
522 // Function: PNMFileTypeBMP::Writer::write_data
523 // Access: Public, Virtual
524 // Description: Writes out an entire image all at once, including the
525 // header, based on the image data stored in the given
526 // _x_size * _y_size array and alpha pointers. (If the
527 // image type has no alpha channel, alpha is ignored.)
528 // Returns the number of rows correctly written.
529 //
530 // It is the user's responsibility to fill in the header
531 // data via calls to set_x_size(), set_num_channels(),
532 // etc., or copy_header_from(), before calling
533 // write_data().
534 //
535 // It is important to delete the PNMWriter class after
536 // successfully writing the data. Failing to do this
537 // may result in some data not getting flushed!
538 //
539 // Derived classes need not override this if they
540 // instead provide supports_streaming() and write_row(),
541 // below.
542 ////////////////////////////////////////////////////////////////////
543 int PNMFileTypeBMP::Writer::
544 write_data(xel *array, xelval *) {
545  if (_y_size<=0 || _x_size<=0) {
546  return 0;
547  }
548 
549  int classv = C_WIN;
550 
551  int colors;
552  int i;
553  colorhist_vector chv;
554  pixval Red[MAXCOLORS];
555  pixval Green[MAXCOLORS];
556  pixval Blue[MAXCOLORS];
557 
558  pixel** pixels;
559  colorhash_table cht;
560 
561 #if 0
562  {
563  char *name;
564  switch (classv)
565  {
566  case C_WIN:
567  name = "a Windows";
568  break;
569  case C_OS2:
570  name = "an OS/2";
571  break;
572  default:
573  pm_error(er_internal, "report");
574  break;
575  }
576  pm_message("generating %s BMP file", name);
577  }
578 #endif
579 
580  // We need an honest 2-d array of pixels, instead of one big 1-d array.
581  pixels = (pixel **)alloca(sizeof(pixel *) * _y_size);
582  for (i = 0; i < _y_size; i++) {
583  pixels[i] = (pixel *)(array + i * _x_size);
584  }
585 
586  /* Figure out the colormap. */
587  chv = ppm_computecolorhist(pixels, _x_size, _y_size, MAXCOLORS, &colors);
588  if (bmp_bpp > 8) {
589  // Quietly generate a 24-bit image.
590  BMPEncode24(_file, classv, _x_size, _y_size, pixels, _maxval);
591 
592  } else if (chv == (colorhist_vector) 0) {
593  if (bmp_bpp != 0) {
594  // Even though we asked for fewer bits, we have to settle for 24-bit.
595  pnmimage_bmp_cat.info()
596  << "too many colors for " << bmp_bpp << "-bit image.\n";
597  }
598 
599  BMPEncode24(_file, classv, _x_size, _y_size, pixels, _maxval);
600 
601  } else {
602  pnmimage_bmp_cat.debug()
603  << colors << " colors found\n";
604 
605  /*
606  * Now turn the ppm colormap into the appropriate BMP colormap.
607  */
608  if (_maxval > 255) {
609  pnmimage_bmp_cat.debug()
610  << "maxval is not 255 - automatically rescaling colors\n";
611  }
612 
613  for (i = 0; i < colors; ++i) {
614  if (_maxval == 255) {
615  Red[i] = PPM_GETR(chv[i].color);
616  Green[i] = PPM_GETG(chv[i].color);
617  Blue[i] = PPM_GETB(chv[i].color);
618  } else {
619  Red[i] = (pixval) PPM_GETR(chv[i].color) * 255 / _maxval;
620  Green[i] = (pixval) PPM_GETG(chv[i].color) * 255 / _maxval;
621  Blue[i] = (pixval) PPM_GETB(chv[i].color) * 255 / _maxval;
622  }
623  }
624 
625  /* And make a hash table for fast lookup. */
626  cht = ppm_colorhisttocolorhash(chv, colors);
627  ppm_freecolorhist(chv);
628 
629  BMPEncode(_file, classv, _x_size, _y_size, pixels, colors, cht,
630  Red, Green, Blue);
631  }
633 
634  return _y_size;
635 }
636 
637 ////////////////////////////////////////////////////////////////////
638 // Function: PNMFileTypeBMP::Writer::supports_grayscale
639 // Access: Public, Virtual
640 // Description: Returns true if this particular PNMWriter understands
641 // grayscale images. If this is false, then the rgb
642 // values of the xel array will be pre-filled with the
643 // same value across all three channels, to allow the
644 // writer to simply write out RGB data for a grayscale
645 // image.
646 ////////////////////////////////////////////////////////////////////
647 bool PNMFileTypeBMP::Writer::
648 supports_grayscale() const {
649  return false;
650 }
651 
652 #endif // HAVE_BMP
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:35
static void consider_yield()
Possibly suspends the current thread for the rest of the current epoch, if it has run for enough this...
Definition: thread.I:263
This is an abstract base class that defines the interface for writing image files of various types...
Definition: pnmWriter.h:31